Hi folks!
We got an strange behavior on ListGrid. The ListGrid make two http request with same response any time I make a refresh or initial load. But, some times the first response comes blank and the grid doesn't show de records.
Version: smartgwt-power-5.0-p20150828.jar
This is the grid

This is the request. There are two request at the same time.

We need a way to make only one request, but I don't know if this is a SmartGWT bug or it's a poor implementation. By the way, I made various tests with diferente ListGrid configurations but doesn't work properly.
Take a look at the code
Grid View
	http://www.4shared.com/file/UtKfO6Z9...storyView.html
Grid Presenter
	http://www.4shared.com/file/ooeTLYn9...Presenter.html
Implementation of ListGrid
	http://www.4shared.com/file/vcMkdlLe...eListGrid.html
Best regards
					We got an strange behavior on ListGrid. The ListGrid make two http request with same response any time I make a refresh or initial load. But, some times the first response comes blank and the grid doesn't show de records.
Version: smartgwt-power-5.0-p20150828.jar
This is the grid
This is the request. There are two request at the same time.
We need a way to make only one request, but I don't know if this is a SmartGWT bug or it's a poor implementation. By the way, I made various tests with diferente ListGrid configurations but doesn't work properly.
Take a look at the code
Grid View
Code:
	
	    private BaseListGrid createGrid() {
        BaseListGrid grid = new BaseListGrid(false, 1) {
            @Override
            public Boolean willFetchData(Criteria newCriteria) {
                return true;
            }
        };
        grid.setHeaderHeight(DesignConstants.GRID_HEADER_HEIGHT);
        grid.setCellHeight(32);
        grid.setDataFetchMode(FetchMode.PAGED);
        grid.setFilterButtonPrompt(CONSTANTS.filter());
        grid.setFetchDelay(500);
        grid.setHoverCustomizer(new VehiclePlateHoverCustomizer(HistoryEntryFieldsConstants.FIELD_VEHICLE_NAME,
                HistoryEntryFieldsConstants.FIELD_VEHICLE_LICENSE_PLATE));
        grid.setCanSort(false);
        grid.setSortField(HistoryEntryFieldsConstants.FIELD_PERSISTED_DATE);
        grid.setSortDirection(SortDirection.DESCENDING);
        grid.setReportHeaderProvider(new ReportHeaderProvider() {
            @Override
            public Canvas getReportHeader() {
                return ReportHeaderBuilder.build(SessionData.getInstance(), getPrintingTitle(), startDate, finalDate);
            }
        });
        grid.setCanPrint(true);
        grid.setCanExport(true);
        grid.setExportFilename(CONSTANTS.vehicleHistoryTitle());
        grid.setExportClientSide(false);
        grid.setAutoFitFieldWidths(true);
        grid.setAutoFitFieldsFillViewport(true);
        grid.setAutoFitWidthApproach(AutoFitWidthApproach.TITLE);
        grid.setAutoFetchData(false);
        grid.setFilterOnKeypress(true);
        setGridDataSource(grid);
        setGridActions(grid);
        setGridFields(grid);
        configureFilter(grid);
        return grid;
    }
    private void setGridDataSource(final BaseListGrid grid) {
        DataSource vehicleHistoryDS = PortalOrionDataSource.getDataSource(HistoryEntryFieldsConstants.DATASOURCE_ID);
        grid.setDataSource(vehicleHistoryDS);
        vehicleHistoryDS.addHandleErrorHandler(new HandleErrorHandler() {
            @Override
            public void onHandleError(ErrorEvent event) {
                String error = event.getResponse().getAttribute("Error");
                Message message = null;
                switch (ErrorType.getByCode(error)) {
                case INVALID_CLIENT_PERMISSION:
                    message = new Message(MessageType.WARNING, MESSAGES.warningLandmarkDiferentClient());
                    break;
                default:
                    break;
                }
                if (message != null) {
                    CommonEventBus.getInstance().fireEvent(new MessageIssueEvent(message));
                }
                grid.discardAllEdits();
                event.cancel();
            }
        });
    }
    @Override
    public void refreshFilter(Date startDate, Date finalDate, Long vehicleId, String vehicleName) {
        this.startDate = startDate;
        this.finalDate = finalDate;
        this.vehicleId = vehicleId;
        if (vehicleName != null && vehicleName.equals(CONSTANTS.select())) {
            this.vehicleName = CONSTANTS.allVehicles();
        } else {
            this.vehicleName = vehicleName;
        }
        criteria = createGridCriteria(vehicleId, startDate, finalDate);
        ShowVehicleHistoryTitleEvent event = new ShowVehicleHistoryTitleEvent(vehicleName, startDate, finalDate);
        CommonEventBus.getInstance().fireEvent(event);
        Portlet portletHistory = Dashboard.getInstance().getPortletByName(Dashboard.HISTORY_PORTLET_NAME);
        if (!portletHistory.isVisible()) {
            portletHistory.show();
        }
        portletHistory.bringToFront();
        refreshGrid();
    }
    @Override
    public void refreshGrid() {
        vehicleHistoryGrid.setCriteria(criteria);
        vehicleHistoryGrid.fetchData(criteria);
        vehicleHistoryGrid.invalidateCache();
    }
    private AdvancedCriteria createGridCriteria(Long vehicleId, Date start, Date end) {
        Criterion clientCriteria = new Criterion(HistoryEntryFieldsConstants.FIELD_PK_OWNER_ID, OperatorId.EQUALS,
                session.getClient().getId());
        Criterion vehicleCriteria = new Criterion(HistoryEntryFieldsConstants.FIELD_VEHICLE_ID, OperatorId.EQUALS,
                vehicleId);
        Criterion permissionCriteria = new Criterion(HistoryEntryFieldsConstants.FIELD_PERMISSION_CODE,
                OperatorId.IN_SET, session.getPermissions());
        Criterion persistedDateCriteria = new Criterion(HistoryEntryFieldsConstants.FIELD_PERSISTED_DATE,
                OperatorId.BETWEEN_INCLUSIVE, start, end);
        // Com ou sem filtro de veículo
        AdvancedCriteria advancedCriteria;
        if (vehicleId != null) {
            advancedCriteria = new AdvancedCriteria(OperatorId.AND, new Criterion[] { clientCriteria, vehicleCriteria,
                    permissionCriteria, persistedDateCriteria });
        } else {
            // se o usuário é gestor, pesquisa pelo cliente apenas
            if (session.isMasterContext()) {
                advancedCriteria = new AdvancedCriteria(OperatorId.AND, new Criterion[] { clientCriteria,
                        permissionCriteria, persistedDateCriteria });
            } else {
                // se o usuário é comum, é necessário passar a lista de veículo associados a ele
                List<VehicleDTO> userVehicles = session.getUser().getVehicleDTOs();
                List<String> userVehicleIdList = new ArrayList<String>();
                for (VehicleDTO vehicleDTO : userVehicles) {
                    userVehicleIdList.add(vehicleDTO.getId().toString());
                }
                String[] arrayUserVehicleId = (String[]) userVehicleIdList.toArray();
                Criterion userVehicleCriteria = new Criterion(HistoryEntryFieldsConstants.FIELD_VEHICLE_ID,
                        OperatorId.IN_SET, arrayUserVehicleId);
                advancedCriteria = new AdvancedCriteria(OperatorId.AND, new Criterion[] { clientCriteria,
                        userVehicleCriteria, permissionCriteria, persistedDateCriteria });
            }
        }
        return advancedCriteria;
    }
    private void configureFilter(BaseListGrid grid) {
        grid.addFilterEditorSubmitHandler(new FilterEditorSubmitHandler() {
            @Override
            public void onFilterEditorSubmit(FilterEditorSubmitEvent event) {
                if (criteria == null) {
                    // evita que seja feito o fecth sem criteria
                    event.cancel();
                    return;
                }
            }
        });
    }
Grid Presenter
Code:
	
	        public void bind() {
    
            CommonEventBus.getInstance().addHandler(ClientChangedInContextEvent.TYPE,
                    new ClientChangedInContextEventHandler() {
                        @Override
                        public void onClientChangedInContext(ClientChangedInContextEvent event) {
                            if (Dashboard.getInstance().isVisible()) {
                                view.clientChanged();
                                bindGridActions();
                                bindPdfButton();
                                CommonEventBus.getInstance().fireEvent(new ResetVehicleHistoryTitleEvent());
                            }
                        }
                    });
    
            bindGridActions();
            bindPdfButton();
    
            // Handler para tratamento do retorno de pontos do grupo de histórico
            CommonEventBus.getInstance().addHandler(ResponsePointsByGroupMapEvent.TYPE,
                    new ResponsePointsByGroupMapEventHandler() {
                        @Override
                        public void onResponse(ResponsePointsByGroupMapEvent event) {
                            validateGridAndMapPoints(event.getResult(), event.getToken());
                        }
                    });
    
            // Handler para filtrar por veículo quando for solicitado pelo grid Situação Atual
            CommonEventBus.getInstance().addHandler(ShowVehicleHistoryEvent.TYPE, new ShowVehicleHistoryEventHandler() {
                @Override
                public void onShowVehicleHistory(ShowVehicleHistoryEvent event) {
                    VehicleDTO vehicle = event.getVehicle();
                    Date finalDate = event.getFinalDate();
                    Date startDate = event.getFinalDate();
                    refreshGrid(startDate, finalDate, vehicle.getId(), vehicle.getVehicleNamePlate());
                }
            });
    
            CommonEventBus.getInstance().addHandler(VehicleHistoryFilterEvent.TYPE, new VehicleHistoryFilterEventHandler() {
                @Override
                public void onNewFilter(Date startDate, Date finalDate, Long vehicleId, String vehicleName) {
                    refreshGrid(startDate, finalDate, vehicleId, vehicleName);
                }
            });
    
            CommonEventBus.getInstance().addHandler(ShowVehicleHistoryTitleEvent.TYPE,
                    new ShowVehicleHistoryTitleEventHandler() {
                        @Override
                        public void onShowVehicleTitleHistory(ShowVehicleHistoryTitleEvent event) {
                            Canvas reportHeader = ReportHeaderBuilder.build(SessionData.getInstance(),
                                    uiConstants.vehicleHistoryTitle(), event.getStartDate(), event.getEndDate());
                            view.resetPrintHeader(reportHeader);
                        }
                    });
    
            CommonEventBus.getInstance().addHandler(ResetVehicleHistoryTitleEvent.TYPE,
                    new ResetVehicleHistoryTitleEventHandler() {
    
                        @Override
                        public void onResetVehicleTitleHistory(ResetVehicleHistoryTitleEvent event) {
                            Canvas reportHeader = ReportHeaderBuilder.build(SessionData.getInstance(),
                                    uiConstants.vehicleHistoryTitle());
                            view.resetPrintHeader(reportHeader);
                        }
                    });
            
        }
        private void refreshGrid(Date startDate, Date finalDate, Long vehicleId, String vehicleName) {
            if (finalDate == null) {
                finalDate = new Date();
            }
            finalDate = getEndOfDay(finalDate);
    
            if (startDate == null) {
                startDate = getStartDate(finalDate);
            }
            startDate = getBeginOfDay(startDate);
    
            view.refreshFilter(startDate, finalDate, vehicleId, vehicleName);
            
            lastStartPeriodDate = startDate;
            lastEndPeriodDate = finalDate;
            lastSelectedVehicle = vehicleId;        
    
            // Envia Hit de pageview ao Google Analytics
            GoogleAnalytics.sendPageViewHit("VehicleHistory", "Histórico de eventos");
        }
Implementation of ListGrid
Code:
	
	public class BaseListGrid extends ListGrid {
    // Definição das seções da barra de ferramentas do grid
    // Seção de ações básicas (Ex.: Adicionar)
    public static final int BASIC_ACTIONS = 0;
    // Seção de ações customizadas (Ex.: Botão ajuda)
    public static final int CUSTOM_ACTIONS = 1;
    // Seção de ações extras (Ex.: Exportar)
    public static final int EXTRA_ACTIONS = 2;
    // Interface para os métodos de criação da barra de ferramentas
    private interface ToolbarBuilder {
        void create();
    };
    // Construtor da seção de ações básicas
    public class CreateBasicActions implements ToolbarBuilder {
        @Override
        public void create() {
            createBasicActions();
        }
    }
    // Construtor da seção de ações customizadas
    public class CreateCustomActions implements ToolbarBuilder {
        @Override
        public void create() {
            createCustomActions();
        }
    }
    // Construtor da seção de ações extras
    public class CreateExtraActions implements ToolbarBuilder {
        @Override
        public void create() {
            createExtraActions();
        }
    }
    private static final UserInterfaceConstants CONSTANTS = GWT.create(UserInterfaceConstants.class);
    private static final UserInterfaceMessages MESSAGES = GWT.create(UserInterfaceMessages.class);
    private static final String HAS_DETAIL_ATTRIBUTE = "BaseListGrid.hasDetail";
    private static final String CAN_SELECT_ATTRIBUTE = "BaseListGrid.canSelect";
    private static final String CAN_PRINT_ATTRIBUTE = "BaseListGrid.canPrint";
    private static final String CAN_EXPORT_ATTRIBUTE = "BaseListGrid.canExport";
    private static final String CAN_ADD_ATTRIBUTE = "BaseListGrid.canAdd";
    private static final String CAN_UPDATE_ATTRIBUTE = "BaseListGrid.canUpdate";
    private static final String CAN_UPDATE_CELL_ATTRIBUTE = "BaseListGrid.canUpdateCell";
    private static final String CAN_REMOVE_ATTRIBUTE = "BaseListGrid.canRemove";
    private static final String CAN_MOVE_ATTRIBUTE = "BaseListGrid.canMove";
    private static final Logger logger = Logger.getLogger(BaseListGrid.class.getName());
    private static final String PRINT_HEADER_ATTRIBUTE = "BaseListGrid.printHeader";
    private static final String EXPORT_FILENAME_ATTRIBUTE = "BaseListGrid.exportFilename";
    private static final String EXPORT_CLIENT_SIDE_ATTRIBUTE = "BaseListGrid.exportClientSide";
    public static final String ACTIONS_FIELD = "BaseListGrid_actionsField";
    private ToolStrip tools;
    private List<Canvas> customToolbarMembers = null;
    private ToolbarBuilder createFirstGroupActions = null;
    private ToolbarBuilder createSecondGroupActions = null;
    private ToolbarBuilder createThirdGroupActions = null;
    private Layout rollOver = null;
    private int countActions = 0;
    private int actionsColumnPosition = -1;
    private boolean editConfigDone = false;
    private CanDoActionRecordRuler hasDetailRecordRuler = null;
    private CanDoActionRecordRuler canUpdateRecordRuler = null;
    private CanDoActionRecordRuler canRemoveRecordRuler = null;
    private ConfirmActionHandler confirmDetailHandler = null;
    private ConfirmActionHandler confirmAddHandler = null;
    private ConfirmActionHandler confirmUpdateHandler = null;
    private ConfirmActionHandler confirmRemoveHandler = null;
    private RecordValidator recordAddValidator = null;
    private RecordValidator recordUpdateValidator = null;
    private Collection<ActionDoneHandler> recordAddedHandlers = new LinkedList<ActionDoneHandler>();
    private Collection<ActionDoneHandler> recordUpdatedHandlers = new LinkedList<ActionDoneHandler>();
    private Collection<ActionDoneHandler> recordRemovedHandlers = new LinkedList<ActionDoneHandler>();
    private CustomListGridRecordAction detailAction = null;
    private CustomListGridRecordAction updateAction = null;
    private CustomListGridRecordAction removeAction = null;
    private CustomListGridRecordAction upAction = null;
    private CustomListGridRecordAction downAction = null;
    private List<CustomListGridRecordAction> customActions = null;
    private Map<Integer, CustomListGridRecordAction> updateActionMap = new HashMap<Integer, CustomListGridRecordAction>();
    private DetailLayoutProvider detailLayoutProvider = null;
    private HoverCustomizer hoverCustomizer = null;
    private Map<String, ComponentListGridField> componentFields = new HashMap<String, ComponentListGridField>();
    private boolean showToolBar;
    private HandlerRegistration dataArrivedRegistration;
    
    private Canvas printHeader;
    private ReportHeaderProvider reportHeaderProvider;
    private ImgButton excelTool;
    private ImgButton csvTool;
    private ImgButton pdfTool;
    private ImgButton printTool;
     
    private boolean useDefaultPdfTool = true;
     
    private List<String> fieldsToShow = new ArrayList<String>();
    // TODO [helenov] avaliar tratamento de inserção de novo registro
    // private ListGridRecord addedRecord;
     
    public static BaseListGrid getOrCreateRef(JavaScriptObject jsObj) {
        if (jsObj == null)
            return null;
        BaseWidget obj = BaseWidget.getRef(jsObj);
        if (obj != null) {
            return (BaseListGrid) obj;
        } else {
            return new BaseListGrid(jsObj);
        }
    }
     
    public BaseListGrid(JavaScriptObject jsObj) {
        super(jsObj);
    }
     
    public BaseListGrid(int firstGroupType, int secondGroupType, int thirdGroupType) {
        this.useDefaultPdfTool = false;
        createFirstGroupActions = makeToolbarBuilder(firstGroupType);
        createSecondGroupActions = makeToolbarBuilder(secondGroupType);
        createThirdGroupActions = makeToolbarBuilder(thirdGroupType);
        initBaseListGrid();
    }
    private ToolbarBuilder makeToolbarBuilder(int groupType) {
        ToolbarBuilder toolbarBuilder = null;
        if (groupType == BASIC_ACTIONS) {
            toolbarBuilder = new CreateBasicActions();
        } else if (groupType == CUSTOM_ACTIONS) {
            toolbarBuilder = new CreateCustomActions();
        } else if (groupType == EXTRA_ACTIONS) {
            toolbarBuilder = new CreateExtraActions();
        }
        return toolbarBuilder;
    }
     
    public BaseListGrid() {
        this(true);
    }
     
    public BaseListGrid(boolean useDefaultPdfTool) {
        this.useDefaultPdfTool = useDefaultPdfTool;
        // Define a ordem padrão de criação das seções da barra de ferramentas.
        this.createFirstGroupActions = new CreateBasicActions();
        this.createSecondGroupActions = new CreateCustomActions();
        this.createThirdGroupActions = new CreateExtraActions();
        initBaseListGrid();
    }
     
    public BaseListGrid(boolean useDefaultPdfTool, int actionsColumnPos) {
        this.useDefaultPdfTool = useDefaultPdfTool;
        // Define a ordem padrão de criação das seções da barra de ferramentas.
        this.createFirstGroupActions = new CreateBasicActions();
        this.createSecondGroupActions = new CreateCustomActions();
        this.createThirdGroupActions = new CreateExtraActions();
        actionsColumnPosition = actionsColumnPos;
        initBaseListGrid();
    }
    private void initBaseListGrid() {
        // Evita que após a edição de uma linha do grid, a mesma seja eliminada do cache.
        ResultSet resultSetProperties = new ResultSet();
        resultSetProperties.setNeverDropUpdatedRows(true);
        setDataProperties(resultSetProperties);
        
        // configuração padrão de ações
        this.setCanSelect(false);
        this.setCanAdd(false);
        this.setCanUpdate(false);
        this.setCanRemove(false);
        this.setWarnOnRemoval(true);
        this.setWarnOnRemovalMessage(MESSAGES.confirmRemove());
        // configuração geral
        this.setBooleanTrueImage(DesignConstants.CHECKED_ICON);
        this.setShowRecordComponents(true);
        this.setShowRecordComponentsByCell(true);
        this.setWrapCells(true);
        this.setShowAlternate(true, 1);
        this.setExportAll(false);
        // configuração de scroll
        this.setLeaveScrollbarGap(true);
        this.setBodyOverflow(Overflow.AUTO);
        this.setFixedRecordHeights(false);
        this.setVirtualScrolling(true);
        // configuração do rollover
        this.setShowRollOver(true);
        // configuração da consulta
        this.setAutoFetchData(true);
        this.setDataFetchMode(FetchMode.PAGED);
        // [TODO] Parametrizar tamanho das páginas
        this.setDataPageSize(30);
        this.setFetchDelay(500);
        this.setLoadingMessage(CONSTANTS.loading());
        this.setDrawAheadRatio(4.0f);
        // configuração do cabeçalho
        this.setShowHeader(true);
        // configuração do filtro
        this.setShowFilter(true);
        // configuração dos componentes
        this.setGridComponents(new Object[] { createToolbar(), ListGridComponent.HEADER,
                ListGridComponent.FILTER_EDITOR, ListGridComponent.BODY });
         
        this.addFilterEditorSubmitHandler(new FilterEditorSubmitHandler() {
            @Override
            public void onFilterEditorSubmit(FilterEditorSubmitEvent event) {
                invalidateCache();
            }
        });
        this.addDrawHandler(new DrawHandler() {
            @Override
            public void onDraw(DrawEvent event) {
                if (showToolBar) {
                    tools.show();
                } else {
                    tools.hide();
                }
            }
        });
        addFetchDataHandler(new FetchDataHandler() {
            public void onFilterData(FetchDataEvent event) {
                putTimeZoneParameter(event.getRequestProperties());
            }
        });
    }
     
    public void setShowAlternate(boolean showAlternate) {
        this.setShowAlternate(showAlternate, 1);
    }
     
    public void enableEditUnsavedRecord() {
        if (getCanAdd()) {
            this.addCellClickHandler(new CellClickHandler() {
                @Override
                public void onCellClick(CellClickEvent event) {
                    ListGridRecord record = event.getRecord();
                    if (record == null) {
                        setEditEvent(ListGridEditEvent.CLICK);
                    } else {
                        setEditEvent(ListGridEditEvent.NONE);
                    }
                }
            });
        }
    }
     
    public void setShowAlternate(boolean showAlternate, int frequency) {
        this.setAlternateRecordStyles(showAlternate);
        this.setAlternateRecordFrequency(frequency);
    }
     
    public void setShowHeader(Boolean showHeader) {
        super.setShowHeader(showHeader);
        this.setShowHeaderContextMenu(false);
        this.setShowHeaderMenuButton(false);
        this.setCanAddSummaryFields(false);
    }
     
    public void setShowFilter(Boolean showFilter) {
        this.setShowFilterEditor(showFilter);
        this.setFilterButtonPrompt(CONSTANTS.filter());
        this.setFilterOnKeypress(true);
    }
     
    public void setFields(List<ListGridField> fields) {
        this.setFields(fields.toArray(new ListGridField[fields.size()]));
    }
     
    @Override
    public void setFields(ListGridField... fields) {
        super.setFields(getFieldsWithActions(fields));
        this.setExportFields(getExportableFields(fields)); // define fields que poderão ser exportados
    }
     
    private ListGridField[] getFieldsWithActions(ListGridField... fields) {
        countActions = countActions();
        if (countActions > 0) {
            // copia fields originais
            ArrayList<ListGridField> fieldsList = new ArrayList<ListGridField>(fields.length + countActions);
            for (ListGridField field : fields) {
                fieldsList.add(field);
                if (hoverCustomizer != null && field.getShowHover() != null && !field.getShowHover()) {
                    field.setShowHover(true);
                    field.setHoverCustomizer(hoverCustomizer);
                }
            }
            // acrescenta coluna de ações
            if (actionsColumnPosition == -1) {
                fieldsList.add(createActionsField(countActions));
            } else {
                fieldsList.add(actionsColumnPosition, createActionsField(countActions));
            }
            // reconverte lista em array
            fields = fieldsList.toArray(fields);
        }
        // faz configurações de campos customizados
        for (ListGridField field : fields) {
            if (field instanceof CustomListGridField) {
                CustomListGridField customField = (CustomListGridField) field;
                configCustomField(customField);
            }
            if (field instanceof ComponentListGridField) {
                ComponentListGridField compField = (ComponentListGridField) field;
                configComponentField(compField);
            }
        }
        return fields;
    }
     
    @Override
    public void setDataSource(DataSource ds, ListGridField... fields) {
        if (!isCreated()) {
            super.setDataSource(ds, fields);
        } else {
            super.setDataSource(ds, getFieldsWithActions(fields));
        }
    }
     
    private void configCustomField(CustomListGridField field) {
        // configura refresh automático de detalhes
        if (field.getRefreshDetailOnChanged()) {
            field.addChangedHandler(new ChangedHandler() {
                @Override
                public void onChanged(ChangedEvent event) {
                    int rowNum = event.getRowNum();
                    ListGridRecord record = (ListGridRecord) getEditedRecord(rowNum);
                    if (isDetailExpanded(record)) {
                        DynamicForm form = getDetailForm(record);
                        form.markForRedraw(); // força reavaliar showIfConditions
                    }
                }
            });
        }
        // configura refresh automático de campos do registro
        final String[] refreshFields = field.getRefreshFieldOnChanged();
        if (refreshFields != null) {
            field.addChangedHandler(new ChangedHandler() {
                @Override
                public void onChanged(ChangedEvent event) {
                    int rowNum = event.getRowNum();
                    refreshCells(rowNum, refreshFields);
                }
            });
        }
    }
     
    private void configComponentField(final ComponentListGridField compField) {
        componentFields.put(compField.getName(), compField);
        this.addRecordRemovedHandler(new ActionDoneHandler() {
            @Override
            public void onActionDone(ListGridRecord record) {
                compField.removeComponent(record);
            }
        });
        if (compField instanceof AssociationListGridField) {
            final AssociationListGridField assocField = (AssociationListGridField) compField;
            assocField.addAssociationRefreshHandler(new AssociationRefreshHandler() {
                @Override
                public void onAssociationRefresh(String id, boolean associated) {
                    // atualiza valor do field de associação
                    String fieldId = assocField.getIdField();
                    Record record = getRecordList().find(fieldId, id);
                    record.setAttribute(assocField.getName(), associated);
                    // atualiza células dependes
                    final String[] refreshFields = assocField.getRefreshFieldOnChanged();
                    if (refreshFields != null) {
                        int rowNum = getRecordIndex(record);
                        refreshCells(rowNum, refreshFields);
                    }
                }
            });
        }
    }
     
    public void refreshCells(int rowNum, String[] fields) {
        for (String fieldName : fields) {
            int colNum = getFieldNum(fieldName);
            refreshCell(rowNum, colNum);
        }
    }
     
    @Override
    public void refreshCell(int rowNum, int colNum) {
        super.refreshCell(rowNum, colNum);
        this.refreshRecordComponent(rowNum, colNum);
    }
     
    private int countActions() {
        int actions = 0;
        if (this.getHasDetail()) {
            actions += 1;
        }
        if (this.getCanUpdate()) {
            actions += 1;
        }
        if (customActions != null) {
            actions += customActions.size();
        }
        if (this.getCanRemove()) {
            actions += 1;
        }
        if (this.getCanMove()) {
            actions += 2;
        }
        return actions;
    }
     
    private ListGridField createActionsField(int actions) {
        ListGridField actionsField = new ListGridField(ACTIONS_FIELD, CONSTANTS.actions());
        actionsField.setCanFilter(false);
        actionsField.setCanEdit(false);
        actionsField.setCanSort(false);
        actionsField.setShouldPrint(false);
        actionsField.setCanExport(false);
        actionsField.setAlign(Alignment.CENTER);
        int width = (DesignConstants.ACTION_ICON_SIZE + 3) * actions;
        actionsField.setWidth(width);
        if (actions <= 1) {
            actionsField.setTitle(" ");
        }
        return actionsField;
    }
     
    @Override
    protected Canvas createRecordComponent(ListGridRecord record, Integer colNum) {
        HStack panel = null;
        String field = this.getFieldName(colNum);
        // TODO [helenov] fazer do action field um component field
        if (field.equals(ACTIONS_FIELD)) {
            // FIXME: [ERROR] [Main] - TMR8:WARN:Log:Specified ID: isc_Canvas_15 collides with the ID for
            // an existing SmartGWT component or object. The existing object will be destroyed and the ID bound to the
            // new object.
            panel = new HStack();
            panel.setHeight(DesignConstants.ACTION_ICON_SIZE);
            panel.setMembersMargin(3);
            this.addDetailAction(record, panel);
            this.addUpdateAction(record, panel);
            this.addCustomActions(record, panel);
            this.addRemoveAction(record, panel);
            this.addUpAction(record, panel);
            this.addDownAction(record, panel);
        }
        if (componentFields.containsKey(field)) {
            ComponentListGridField compField = componentFields.get(field);
            int rowNum = getRecordIndex(record);
            return compField.createComponent(record, rowNum);
        }
        return panel;
    }
     
    @Deprecated
    public void setCustomActions(CustomListGridRecordAction... actions) {
        setCustomAction(Arrays.asList(actions));
    }
     
    public void setCustomAction(List<CustomListGridRecordAction> actions) {
        customActions = actions;
    }
     
    public void setCustomToolbarMembers(Canvas... customToolbarMembers) {
        setCustomToolbarMembers(Arrays.asList(customToolbarMembers));
    }
     
    public void setCustomToolbarMembers(List<Canvas> customToolbarMembers) {
        this.customToolbarMembers = customToolbarMembers;
        this.resetToolbar();
    }
     
    private ToolStrip createToolbar() {
        tools = new ToolStrip();
        tools.setHeight(DesignConstants.ACTION_ICON_SIZE + 4);
        tools.setMembersMargin(3);
        this.resetToolbar();
        return tools;
    }
     
    private void resetToolbar() {
        if (tools != null) {
            tools.removeMembers(tools.getMembers());
            showToolBar = false;
            createFirstGroupActions.create();
            createSecondGroupActions.create();
            createThirdGroupActions.create();
        }
    }
    private void createBasicActions() {
        if (this.addAddAction()) {
            showToolBar = true;
        }
    }
    private void createCustomActions() {
        // ações customizadas
        if (customToolbarMembers != null) {
            showToolBar = true;
            for (Canvas action : customToolbarMembers) {
                if (action instanceof ToolStripButton) {
                    tools.addButton((ToolStripButton) action);
                } else {
                    tools.addMember(action);
                }
            }
        }
    }
    private void createExtraActions() {
        // ações build-in auxiliares
        boolean haveExport = this.addExportAction();
        boolean havePrint = this.addPrintAction();
        if (haveExport || havePrint) {
            showToolBar = true;
        }
    }
     
    private ImgButton createGridAction(String icon, String prompt) {
        ImgButton action = new ImgButton();
        action.setSrc(icon);
        action.setShowDown(false);
        action.setSize(DesignConstants.ACTION_ICON_SIZE);
        action.setPrompt(prompt);
        return action;
    }
    // Suporte a seleção de registros do grid.
    // /////////////////////////////////////////////////////////////////////////////
     
    public Boolean getCanSelect() {
        Boolean canSelect = getAttributeAsBoolean(CAN_SELECT_ATTRIBUTE);
        return canSelect != null ? canSelect : false;
    }
     
    public void setCanSelect(Boolean canSelect) {
        this.setAttribute(CAN_SELECT_ATTRIBUTE, canSelect, true);
        if (canSelect) {
            this.setSelectionType(SelectionStyle.SIMPLE);
            this.setSelectionAppearance(SelectionAppearance.CHECKBOX);
            this.setCanSelectAll(true);
            this.setCheckboxFieldTrueImage(DesignConstants.CHECKBOX_CHECKED);
            this.setCheckboxFieldFalseImage(DesignConstants.CHECKBOX_UNCHECKED);
            this.addRecordClickHandler(new RecordClickHandler() {
                @Override
                public void onRecordClick(RecordClickEvent event) {
                    if (!event.getField().getName().equals("_checkboxField")) { // Ignora o click no checkbox para não
                                                                                // fazer 2x
                        ListGridRecord record = getRecord(event.getRecordNum());
                        boolean newState = !isSelected(record);
                        selectRecord(record, newState);
                    }
                }
            });
        } else {
            this.setSelectionType(SelectionStyle.NONE);
        }
    }
    // [helenov] TODO verificar pq não funcionou
    // private ListGridField getSelectionField() {
    // return ListGridField.getOrCreateRef(getAttributeAsJavaScriptObject("checkboxField"));
    // }
    // Suporte a exibição de detalhes dos registros no grid.
    // /////////////////////////////////////////////////////////////////////////////
     
    public Boolean getHasDetail() {
        Boolean hasDetail = getAttributeAsBoolean(HAS_DETAIL_ATTRIBUTE);
        return hasDetail != null ? hasDetail : false;
    }
     
    private boolean getHasDetail(ListGridRecord record) {
        boolean hasDetail = false;
        if (getHasDetail()) {
            hasDetail = getDetailAction().getCanDoAction(record);
        }
        return hasDetail;
    }
     
    public void setHasDetail(Boolean hasDetail) {
        this.setAttribute(HAS_DETAIL_ATTRIBUTE, hasDetail, true);
        if (hasDetail) {
            this.setExpansionMode(ExpansionMode.EDITOR);
        }
    }
     
    public CanDoActionRecordRuler getHasDetailRecordRuler() {
        return hasDetailRecordRuler;
    }
     
    public void setHasDetailRecordRuler(CanDoActionRecordRuler ruler) {
        hasDetailRecordRuler = ruler;
        if (detailAction != null) {
            detailAction.setCanDoActionRecordRuler(ruler);
        }
    }
     
    public ConfirmActionHandler getConfirmDetailHandler() {
        return confirmDetailHandler;
    }
     
    public void setConfirmDetailHandler(ConfirmActionHandler handler) {
        confirmDetailHandler = handler;
        if (detailAction != null) {
            detailAction.setConfirmActionHandler(handler);
        }
    }
     
    public DetailLayoutProvider getDetailLayoutProvider() {
        return detailLayoutProvider;
    }
     
    public void setDetailLayoutProvider(DetailLayoutProvider provider) {
        this.detailLayoutProvider = provider;
    }
     
    @Override
    public void setHoverCustomizer(HoverCustomizer hoverCustomizer) {
        this.hoverCustomizer = hoverCustomizer;
    }
     
    private void addDetailAction(ListGridRecord record, Layout panel) {
        if (getHasDetail()) {
            CustomListGridRecordAction action = getDetailAction();
            panel.addMember(action.getActionIcon(record));
        }
    }
     
    private CustomListGridRecordAction getDetailAction() {
        if (detailAction == null) {
            detailAction = createDetailAction();
        }
        return detailAction;
    }
     
    private CustomListGridRecordAction createDetailAction() {
        CustomListGridRecordAction action = new CustomListGridRecordAction(DesignConstants.VIEW_RECORD_IMAGE,
                CONSTANTS.view());
        action.setCanDoActionRecordRuler(hasDetailRecordRuler);
        action.setConfirmActionHandler(confirmDetailHandler);
        action.setExecuteActionHandler(new ExecuteActionHandler() {
            @Override
            public void execute(ListGridRecord record) {
                // se está editando já está aberto e não deve fechar
                if (!isEditing(record)) {
                    toggleDetail(record);
                }
            }
        });
        return action;
    }
     
    private void toggleDetail(ListGridRecord record) {
        if (getHasDetail(record)) {
            if (!isDetailExpanded(record)) {
                this.expandRecord(record);
            } else {
                this.collapseRecord(record);
            }
        }
    }
     
    public void expandRecord(ListGridRecord record) {
        this.collapseRecord(record);
        super.expandRecord(record);
    }
     
    @Override
    protected Canvas getExpansionComponent(ListGridRecord record) {
        Canvas canvas;
        if (detailLayoutProvider != null) {
            boolean canEdit = isEditing(record);
            canvas = this.getDetailLayout(record, canEdit);
        } else {
            canvas = super.getExpansionComponent(record);
        }
        return canvas;
    }
     
    private Layout getDetailLayout(final ListGridRecord record, boolean canEdit) {
        final DynamicForm form = detailLayoutProvider.getDetailLayout(record, canEdit);
        form.setWidth100();
        form.setHeight100();
        form.setCanEdit(canEdit);
        form.setDataSource(this.getDataSource());
        form.addDrawHandler(new DrawHandler() {
            public void onDraw(DrawEvent event) {
                form.editRecord(record);
            }
        });
        form.setCanSubmit(false);
        if (canEdit) {
            form.setSaveOnEnter(true);
            form.addSubmitValuesHandler(new SubmitValuesHandler() {
                @Override
                public void onSubmitValues(SubmitValuesEvent event) {
                    endEditing();
                }
            });
        }
        Layout layout = new VLayout();
        layout.setWidth100();
        layout.setHeight100();
        layout.addMember(form);
        return layout;
    }
     
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private ListGridRecord completeRecord(int rowNum, ListGridRecord record) {
        // valores do registro
        Map values = this.getEditValues(record);
        // acrescenta valores do form de detalhes
        DynamicForm form = getDetailForm(record);
        FormItem[] itens = form.getFields();
        for (FormItem item : itens) {
            String itemName = item.getName();
            Object itemValue = form.getValue(itemName);
            values.put(itemName, itemValue);
        }
        this.setEditValues(rowNum, values);
        return getEditedRecord(record);
    }
     
    public boolean isDetailExpanded(ListGridRecord record) {
        Canvas expansion = this.getCurrentExpansionComponent(record);
        return expansion != null;
    }
     
    public DynamicForm getDetailForm(ListGridRecord record) {
        Layout expansion = (Layout) this.getCurrentExpansionComponent(record);
        return (DynamicForm) expansion.getMember(0);
    }
    // Suporte a impressão da grid.
    // /////////////////////////////////////////////////////////////////////////////
     
    public Boolean getCanPrint() {
        Boolean canPrint = getAttributeAsBoolean(CAN_PRINT_ATTRIBUTE);
        return canPrint != null ? canPrint : false;
    }
     
    public void setCanPrint(Boolean canPrint) {
        this.setAttribute(CAN_PRINT_ATTRIBUTE, canPrint, true);
        this.resetToolbar();
    }
    
     
    public void setReportHeaderProvider(ReportHeaderProvider reportHeaderProvider) {
        this.reportHeaderProvider = reportHeaderProvider;
    }
     
    public Canvas getPrintHeader() {
        return printHeader;
    }
    
     
    public void setPrintHeader(Canvas printHeader) {
        this.printHeader = printHeader;
    }
    
     
    private boolean addPrintAction() {
        boolean show = false;
        if (this.getCanPrint()) {
            show = true;
            printTool = createGridAction(DesignConstants.PRINT_IMAGE, CONSTANTS.print());
            printTool.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    disableButtons();
                    endEditing();
                    startPrinting();
                }
            });
            tools.addMember(printTool);
        }
        return show;
    }
    private void setResultSetFetchModePostCreate(FetchMode fetchMode) {
        getOriginalResultSet().setAttribute("fetchMode", fetchMode.getValue(), true);
    }
    private static native void downloadAsPDF(String html, DSRequest requestProperties)  ;
    private Canvas[] getPrintView() {
        if (reportHeaderProvider != null) {
            Canvas printHeader = reportHeaderProvider.getReportHeader();
            return new Canvas[] { printHeader, this };
        } else {
            return new Canvas[] { this };
        }
    }
    private String generateReportFileName() {
        String title = getExportFilename();
        if (title == null) {
            title = "Relatório";
        }
        DateTimeFormat dateTimeFormatter = DateTimeFormat.getFormat(CONSTANTS.patternDateTimeSeconds());
        String timestamp = dateTimeFormatter.format(new Date());
        String fileName = title + " " + timestamp;
        return fileName.replaceAll("[/: ]", "_");
    }
    protected void startExportingPdf() {
        CommonEventBus.getInstance().fireEvent(
                new MessageIssueEvent(new Message(MessageType.INFO, MESSAGES.infoExportWaiting())));
        expandGroups();
        
        runAfterFullFetch(new Runnable() {
            @Override
            public void run() {
                Canvas.getPrintHTML(getPrintView(), new PrintProperties(), new PrintHTMLCallback() {
                    @Override
                    public void setHTML(final String html) {
                        logger.info("callback getPrintHTML");
                        DSRequest requestProperties = new DSRequest();
                        requestProperties.setAttribute("skinName", "Linker");
                        requestProperties.setAttribute("pdfName", generateReportFileName());
                        requestProperties.setExportDisplay(ExportDisplay.DOWNLOAD);
                        downloadAsPDF(html, requestProperties);
                        logger.info("after exportContent");
                        enableButtons(); // Reabilita os botões
                        enablePrintFetch();
                    }
                });
            }
        });
    }
     
    protected void startPrinting() {
        expandGroups();
        
        runAfterFullFetch(new Runnable() {
            @Override
            public void run() {
                Canvas.showPrintPreview(getPrintView(), new PrintProperties(), null, new PrintPreviewCallback() {
                    @Override
                    public void execute(PrintCanvas printCanvas, PrintWindow printWindow) {
                        logger.info("callback showPrintPreview");
                        enableButtons();
                        enablePrintFetch();
                    }
                });
            }
        });
    }
     
    private void expandGroups() {
        Tree groupTree = getGroupTree();
        
        if (groupTree != null) {
            groupTree.openAll();
        }
    }
    private boolean fetchDisabledByPrint = false;
    private boolean fetchDisabledByEdition = false;
    private boolean delayFetch = false;
    private void runAfterFullFetch(final Runnable runnable) {
        if (getOriginalResultSet() == null) {
            logger.info("resultSet is null!");
            runnable.run();
            return;
        }
        dataArrivedRegistration = addDataArrivedHandler(new DataArrivedHandler() {
            @Override
            public void onDataArrived(DataArrivedEvent event) {
                logger.info("data arrived!");
                removeDataArrivedRegistration();
                fetchDisabledByPrint = true;
                runnable.run();
            }
        });
        // Configura o grid para buscar todos os registros
        setResultSetFetchModePostCreate(FetchMode.BASIC);
        // Força refetch
        invalidateCache();
    }
    private void enablePrintFetch() {
        fetchDisabledByPrint = false;
        // logger.info("Retorna grid para modo PAGED");
        setResultSetFetchModePostCreate(FetchMode.PAGED);
        // Força refetch
        invalidateCache();
        // logger.info("done");
    }
    @Override
    public void invalidateCache() {
        logger.info("invalidateCache called");
        if (fetchDisabledByPrint) {
            logger.info("invalidateCache skiped!");
            return;
        }
        super.invalidateCache();
    }
    private void removeDataArrivedRegistration() {
        if (dataArrivedRegistration != null) {
            dataArrivedRegistration.removeHandler();
        }
        dataArrivedRegistration = null;
    }
    // Suporte a exportação da grid.
    // /////////////////////////////////////////////////////////////////////////////
     
    public Boolean getCanExport() {
        Boolean canExport = getAttributeAsBoolean(CAN_EXPORT_ATTRIBUTE);
        return canExport != null ? canExport : false;
    }
     
    public void setCanExport(Boolean canExport) {
        this.setAttribute(CAN_EXPORT_ATTRIBUTE, canExport, true);
        this.resetToolbar();
    }
     
    public Boolean getExportClientSide() {
        Boolean exportClient = getAttributeAsBoolean(EXPORT_CLIENT_SIDE_ATTRIBUTE);
        if (exportClient == null) {
            exportClient = true;
        }
        return exportClient;
    }
     
    public void setExportClientSide(Boolean exportClient) {
        this.setAttribute(EXPORT_CLIENT_SIDE_ATTRIBUTE, exportClient, true);
    }
     
    public String getExportFilename() {
        return getAttribute(EXPORT_FILENAME_ATTRIBUTE);
    }
     
    public void setExportFilename(String filename) {
        filename = StringUtilsClient.filterHtmlEntities(filename);
        this.setAttribute(EXPORT_FILENAME_ATTRIBUTE, filename, true);
    }
    public void addFieldToExport(String field) {
        fieldsToShow.add(field);
    }
    private void export(ExportFormat format) {
        disableButtons();
        endEditing();
        for (String field : fieldsToShow) {
            showField(field);
        }
        startExporting(format);
        for (String field : fieldsToShow) {
            hideField(field);
        }
    }
     
    private boolean addExportAction() {
        boolean show = false;
        if (this.getCanExport()) {
            show = true;
            excelTool = createGridAction(DesignConstants.EXPORT_EXCEL_IMAGE, CONSTANTS.exportExcel());
            excelTool.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    export(ExportFormat.XLS);
                }
            });
            tools.addMember(excelTool);
            csvTool = createGridAction(DesignConstants.EXPORT_CSV_IMAGE, CONSTANTS.exportCsv());
            csvTool.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    export(ExportFormat.CSV);
                }
            });
            tools.addMember(csvTool);
            pdfTool = createGridAction(DesignConstants.EXPORT_PDF_IMAGE, CONSTANTS.exportPdf());
            if (useDefaultPdfTool) {
                pdfTool.addClickHandler(new ClickHandler() {
                    public void onClick(ClickEvent event) {
                        disableButtons();
                        endEditing();
                        startExportingPdf();
                    }
                });
            }
            tools.addMember(pdfTool);
        }
        return show;
    }
     
    public ImgButton getPdfTool() {
        return pdfTool;
    }
    private void disableButtons() {
        excelTool.disable();
        csvTool.disable();
        pdfTool.disable();
        if (printTool != null) {
            printTool.disable();
        }
    }
    private void enableButtons() {
        excelTool.enable();
        csvTool.enable();
        pdfTool.enable();
        if (printTool != null) {
            printTool.enable();
        }
    }
     
    private void startExporting(ExportFormat format) {
        if (!this.getDataAsRecordList().isEmpty()) {
            DSRequest dsRequestProps = new DSRequest();
            dsRequestProps.setExportAs(format);
            dsRequestProps.setExportDisplay(ExportDisplay.DOWNLOAD);
            dsRequestProps.setExportDelimiter(";");
            // coloca o TimeZone
            putTimeZoneParameter(dsRequestProps);
            dsRequestProps.setExportFilename(generateReportFileName());
            if (!this.getExportClientSide()) {
                prepareDataSourceTitles();
                this.exportData(dsRequestProps);
            } else {
                this.exportClientData(dsRequestProps);
            }
        } else {
            CommonEventBus.getInstance().fireEvent(
                    new MessageIssueEvent(new Message(MessageType.INFO, MESSAGES.infoNoRecordToExport())));
        }
        // Reabilita os botões de exportação
        enableButtons();
    }
     
    private void putTimeZoneParameter(DSRequest dsRequestProps) {
        // pega o TimeZone do client
        Date today = new Date();
        String timeZone = DateTimeFormat.getFormat("v").format(today);
        // coloca o parametro TimeZone no DSrequest
        Map<String, String> param = new HashMap<String, String>();
        param.put("timeZone", timeZone);
        dsRequestProps.setParams(param);
    }
     
    private void prepareDataSourceTitles() {
        Date date = new Date();
        DataSource ds = this.getDataSource();
        ListGridField[] fields = this.getAllFields();
        for (ListGridField field : fields) {
            String title = null;
            if (fieldIsExportable(field)) {
                title = StringUtilsClient.filterHtmlEntities(field.getTitle());
                if (ListGridFieldType.DATETIME.equals(field.getType())) {
                    title = title + " (" + DateTimeFormat.getFormat("ZZZZ").format(date) + ")";
                }
            }
            DataSourceField dsField = ds.getField(field.getName());
            if (dsField != null && title != null) {
                dsField.setTitle(title);
            }
        }
    }
     
    protected String[] getExportableFields(ListGridField... fields) {
        List<String> exportFields = new ArrayList<String>(fields.length);
        for (ListGridField field : fields) {
            if (fieldIsExportable(field)) {
                exportFields.add(field.getName());
            }
        }
        return exportFields.toArray(new String[exportFields.size()]);
    }
    private boolean fieldIsVisible(ListGridField field) {
        String showIf = field.getAttribute("showIf");
        return showIf == null || "true".equals(showIf);
    }
     
    protected boolean fieldIsExportable(ListGridField field) {
        Object canExportString = field.getAttributeAsObject("canExport");
        if (canExportString == null) {
            return fieldIsVisible(field);
        } else {
            return (Boolean) canExportString;
        }
    }
    // Suporte a inclusão de registros no grid.
    // /////////////////////////////////////////////////////////////////////////////
     
    public Boolean getCanAdd() {
        Boolean canAdd = getAttributeAsBoolean(CAN_ADD_ATTRIBUTE);
        return canAdd != null ? canAdd : false;
    }
     
    public void setCanAdd(Boolean canAdd) {
        this.setAttribute(CAN_ADD_ATTRIBUTE, canAdd, true);
        this.resetToolbar();
        this.setCanEdit();
    }
     
    public ConfirmActionHandler getConfirmAddHandler() {
        return confirmAddHandler;
    }
     
    public void setConfirmAddHandler(ConfirmActionHandler handler) {
        confirmAddHandler = handler;
    }
     
    public RecordValidator getRecordAddValidator() {
        return recordAddValidator;
    }
     
    public void setRecordAddValidator(RecordValidator validator) {
        recordAddValidator = validator;
    }
     
    public synchronized void addRecordAddedHandler(ActionDoneHandler handler) {
        recordAddedHandlers.add(handler);
    }
     
    private boolean addAddAction() {
        boolean show = false;
        if (this.getCanAdd()) {
            show = true;
            ImgButton addTool = createGridAction(DesignConstants.ADD_LINE_IMAGE, CONSTANTS.add());
            addTool.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    boolean confirm = confirmAdding();
                    if (confirm) {
                        startAdding();
                    }
                }
            });
            tools.addMember(addTool);
        }
        return show;
    }
     
    private boolean confirmAdding() {
        boolean confirm = true;
        if (confirmAddHandler != null) {
            confirm = confirmAddHandler.confirm(null, new BooleanCallback() {
                @Override
                public void execute(Boolean confirm) {
                    if (confirm) {
                        startAdding();
                    }
                }
            });
        }
        return confirm;
    }
     
    private void startAdding() {
        this.startEditingNew();
    }
     
    private boolean validateAdd(ListGridRecord record) {
        boolean valid = true;
        if (recordAddValidator != null) {
            valid = recordAddValidator.validate(record);
        }
        return valid;
    }
     
    private synchronized void notifyRecordAdded(ListGridRecord record) {
        for (ActionDoneHandler handler : recordAddedHandlers) {
            handler.onActionDone(record);
        }
    }
    // Suporte a edição de registros no grid.
    // /////////////////////////////////////////////////////////////////////////////
     
    public Boolean getCanUpdate() {
        Boolean canUpdate = getAttributeAsBoolean(CAN_UPDATE_ATTRIBUTE);
        return canUpdate != null ? canUpdate : false;
    }
     
    public void setCanUpdate(Boolean canUpdate) {
        this.setAttribute(CAN_UPDATE_ATTRIBUTE, canUpdate, true);
        this.setCanEdit();
    }
     
    public CanDoActionRecordRuler getCanUpdateRecordRuler() {
        return canUpdateRecordRuler;
    }
     
    public void setCanUpdateRecordRuler(CanDoActionRecordRuler ruler) {
        canUpdateRecordRuler = ruler;
        if (updateAction != null) {
            updateAction.setCanDoActionRecordRuler(ruler);
        }
        if (!updateActionMap.isEmpty()) {
            for (CustomListGridRecordAction action : updateActionMap.values()) {
                action.setCanDoActionRecordRuler(ruler);
            }
        }
    }
     
    public ConfirmActionHandler getConfirmUpdateHandler() {
        return confirmUpdateHandler;
    }
     
    public void setConfirmUpdateHandler(ConfirmActionHandler handler) {
        confirmUpdateHandler = handler;
        if (updateAction != null) {
            updateAction.setConfirmActionHandler(handler);
        }
        if (!updateActionMap.isEmpty()) {
            for (CustomListGridRecordAction action : updateActionMap.values()) {
                action.setConfirmActionHandler(handler);
            }
        }
    }
     
    public RecordValidator getRecordUpdateValidator() {
        return recordUpdateValidator;
    }
     
    public void setRecordUpdateValidator(RecordValidator validator) {
        recordUpdateValidator = validator;
    }
     
    public synchronized void addRecordUpdatedHandler(ActionDoneHandler handler) {
        recordUpdatedHandlers.add(handler);
    }
    private void addUpdateAction(ListGridRecord record, Layout panel) {
        if (getCanUpdate()) {
            CustomListGridRecordAction action = getUpdateAction();
            panel.addMember(action.getActionIcon(record));
            if (!action.getCanDoAction(record)) {
                record.setAttribute(getRecordEditProperty(), false);
            }
        }
    }
     
    private CustomListGridRecordAction getUpdateAction() {
        if (updateAction == null) {
            updateAction = createUpdateAction();
        }
        return updateAction;
    }
    private CustomListGridRecordAction createUpdateAction() {
        CustomListGridRecordAction action = new CustomListGridRecordAction(DesignConstants.CHANGE_RECORD_IMAGE,
                CONSTANTS.edit());
        action.setCanDoActionRecordRuler(canUpdateRecordRuler);
        action.setConfirmActionHandler(confirmUpdateHandler);
        action.setExecuteActionHandler(new ExecuteActionHandler() {
            @Override
            public void execute(ListGridRecord record) {
                startUpdating(record, null);
                CommonEventBus.getInstance().fireEvent(new MessageClearEvent());
            }
        });
        return action;
    }
    private void startUpdating(ListGridRecord record, Integer colNum) {
        // se já está editando, já está aberto e não deve mudar
        if (!isEditing(record)) {
            int rowNum = this.getRecordIndex(record);
            if (colNum == null) {
                colNum = this.getFirstEditableColumn(rowNum);
            }
            if (startEditing(rowNum, colNum, false)) {
                 if (getHasDetail(record)) {
                    expandRecord(record);
                }
            }
        }
    }
    private boolean validateUpdate(ListGridRecord record) {
        boolean valid = true;
        if (recordUpdateValidator != null) {
            valid = recordUpdateValidator.validate(record);
        }
        return valid;
    }
     
    private synchronized void notifyRecordUpdated(ListGridRecord record) {
        for (ActionDoneHandler handler : recordUpdatedHandlers) {
            handler.onActionDone(record);
        }
    }
    public Boolean getCanUpdateCell() {
        Boolean canUpdate = getAttributeAsBoolean(CAN_UPDATE_CELL_ATTRIBUTE);
        return canUpdate != null ? canUpdate : false;
    }
    public void setCanUpdateCell(Boolean canUpdate) {
        this.setAttribute(CAN_UPDATE_CELL_ATTRIBUTE, canUpdate, true);
        this.setCanEdit();
        this.setEditByCell(canUpdate);
        this.setShowRollOverCanvas(canUpdate);
        this.setUseCellRollOvers(canUpdate);
    }
    @Override
    protected Canvas getRollOverCanvas(Integer rowNum, Integer colNum) {
        if (isEditing()) {
            return null;
        }
        Layout layout = null;
        if (CustomListGridField.getCanUpdateCell(getField(colNum))) {
            CustomListGridRecordAction action = getUpdateCellAction(colNum);
            ListGridRecord record = (ListGridRecord) this.getEditedRecord(rowNum);
            if (action.getCanDoAction(record)) {
                    layout = createRollOverCanvas(record, colNum);
                layout.addMember(action.getActionIcon(record));
            }
        }
        return layout;
    }
    private Layout createRollOverCanvas(ListGridRecord record, Integer colNum) {
        if (rollOver == null) {
            rollOver = new HLayout(2);
            rollOver.setSnapTo("TR");
            rollOver.setWidth(22);
            rollOver.setHeight(22);
        } else {
            rollOver.removeMembers(rollOver.getMembers());
        }
        return rollOver;
    }
    private CustomListGridRecordAction getUpdateCellAction(int colNum) {
        CustomListGridRecordAction action = updateActionMap.get(colNum);
        if (action == null) {
            action = createUpdateAction(colNum);
        }
        return action;
    }
     
    private CustomListGridRecordAction createUpdateAction(final int colNum) {
        CustomListGridRecordAction action = new CustomListGridRecordAction(DesignConstants.CHANGE_RECORD_IMAGE,
                CONSTANTS.edit());
        action.setCanDoActionRecordRuler(canUpdateRecordRuler);
        action.setConfirmActionHandler(confirmUpdateHandler);
        action.setExecuteActionHandler(new ExecuteActionHandler() {
            @Override
            public void execute(ListGridRecord record) {
                startUpdating(record, colNum);
                CommonEventBus.getInstance().fireEvent(new MessageClearEvent());
            }
        });
        return action;
    }
    public Boolean getCanRemove() {
        Boolean canRemove = getAttributeAsBoolean(CAN_REMOVE_ATTRIBUTE);
        return canRemove != null ? canRemove : false;
    }
    public void setCanRemove(Boolean canRemove) {
        this.setAttribute(CAN_REMOVE_ATTRIBUTE, canRemove, true);
    }
    public CanDoActionRecordRuler getCanRemoveRecordRuler() {
        return canRemoveRecordRuler;
    }
     
    public void setCanRemoveRecordRuler(CanDoActionRecordRuler ruler) {
        canRemoveRecordRuler = ruler;
        if (removeAction != null) {
            removeAction.setCanDoActionRecordRuler(ruler);
        }
    }
    public ConfirmActionHandler getConfirmRemoveHandler() {
        return confirmRemoveHandler;
    }
    public void setConfirmRemoveHandler(ConfirmActionHandler handler) {
        confirmRemoveHandler = handler;
        if (removeAction != null) {
            removeAction.setConfirmActionHandler(handler);
        }
    }
    public synchronized void addRecordRemovedHandler(ActionDoneHandler removeRecordListener) {
        recordRemovedHandlers.add(removeRecordListener);
    }
    private void addRemoveAction(ListGridRecord record, Layout panel) {
        if (getCanRemove()) {
            CustomListGridRecordAction action = getRemoveAction();
            panel.addMember(action.getActionIcon(record));
        }
    }
    private CustomListGridRecordAction getRemoveAction() {
        if (removeAction == null) {
            removeAction = createRemoveAction();
        }
        return removeAction;
    }
    private CustomListGridRecordAction createRemoveAction() {
        CustomListGridRecordAction action = new CustomListGridRecordAction(DesignConstants.DELETE_RECORD_IMAGE,
                CONSTANTS.delete());
        action.setCanDoActionRecordRuler(canRemoveRecordRuler);
        action.setConfirmActionHandler(confirmRemoveHandler);
        action.setExecuteActionHandler(new ExecuteActionHandler() {
            @Override
            public void execute(ListGridRecord record) {
                startRemoving(record);
            }
        });
        return action;
    }
    private void startRemoving(final ListGridRecord record) {
        if (this.getWarnOnRemoval()) {
            this.warnRemove(record);
        } else {
            this.doRemove(record);
        }
    }
     
    private void warnRemove(final ListGridRecord record) {
        Dialog dialog = new Dialog();
        dialog.setBodyColor("#EDEDED");
        SC.ask(CONSTANTS.warning(), this.getWarnOnRemovalMessage(), new BooleanCallback() {
            @Override
            public void execute(Boolean ok) {
                if (ok) {
                    doRemove(record);
                }
            }
        }, dialog);
    }
    private void doRemove(final ListGridRecord record) {
        removeData(record, new DSCallback() {
            @Override
            public void execute(DSResponse response, Object rawData, DSRequest request) {
                if (response.getStatus() == DSResponse.STATUS_SUCCESS) {
                    notifyRecordRemoved(record);
                }
            }
        });
    }
    private synchronized void notifyRecordRemoved(ListGridRecord record) {
        for (ActionDoneHandler handler : recordRemovedHandlers) {
            handler.onActionDone(record);
        }
    }
    private void addUpAction(ListGridRecord record, Layout panel) {
        if (getCanMove()) {
            CustomListGridRecordAction action = getUpAction();
            panel.addMember(action.getActionIcon(record));
        }
    }
    private void addDownAction(ListGridRecord record, Layout panel) {
        if (getCanMove()) {
            CustomListGridRecordAction action = getDownAction();
            panel.addMember(action.getActionIcon(record));
        }
    }
    private CustomListGridRecordAction getUpAction() {
        if (upAction == null) {
            upAction = createUpAction();
        }
        return upAction;
    }
    private CustomListGridRecordAction getDownAction() {
        if (downAction == null) {
            downAction = createDownAction();
        }
        return downAction;
    }
     
    private CustomListGridRecordAction createUpAction() {
        CustomListGridRecordAction upAction = new CustomListGridRecordAction(DesignConstants.MOVE_UP_RECORD_IMAGE,
                CONSTANTS.moveUp());
        upAction.setExecuteActionHandler(new ExecuteActionHandler() {
            @Override
            public void execute(ListGridRecord record) {
                RecordList rs = getRecordList();
                int index = getRecordIndex(record);
                if (index > 0) {
                    rs.removeAt(index);
                    rs.addAt(record, index - 1);
                }
            }
        });
        return upAction;
    }
private CustomListGridRecordAction createDownAction() {
        CustomListGridRecordAction downAction = new CustomListGridRecordAction(DesignConstants.MOVE_DOWN_RECORD_IMAGE,
                CONSTANTS.moveDown());
        downAction.setExecuteActionHandler(new ExecuteActionHandler() {
            @Override
            public void execute(ListGridRecord record) {
                RecordList rs = getRecordList();
                int index = getRecordIndex(record);
                int numRecords = rs.getLength();
                if (index < numRecords - 1) {
                    rs.removeAt(index);
                    rs.addAt(record, index + 1);
                }
            }
        });
        return downAction;
    }
    public Boolean getCanMove() {
        Boolean canMove = getAttributeAsBoolean(CAN_MOVE_ATTRIBUTE);
        return canMove != null ? canMove : false;
    }
    public void setCanMove(Boolean canMove) {
        this.setAttribute(CAN_MOVE_ATTRIBUTE, canMove, true);
    }
    private void addCustomActions(ListGridRecord record, Layout panel) {
        if (customActions != null) {
            for (CustomListGridRecordAction action : customActions) {
                panel.addMember(action.getActionIcon(record));
            }
        }
    }
    private void setCanEdit() {
        boolean canEdit = this.getCanAdd() || this.getCanUpdate() || this.getCanUpdateCell();
        setCanEdit(canEdit);
    }
    public void setCanEdit(Boolean canEdit) {
        super.setCanEdit(canEdit);
        configEdit();
    }
    private void configEdit() {
        if (getCanEdit() && !editConfigDone) {
            editConfigDone = true;
            this.setEditEvent(ListGridEditEvent.NONE);
            this.setAutoSaveEdits(true);
            this.setSelectOnEdit(false);
            this.setModalEditing(false);
            this.setConfirmCancelEditing(false);
            this.setConfirmDiscardEdits(false);
            bindEditHandlers();
        }
    }
    private void bindEditHandlers() {
        this.addRecordClickHandler(new RecordClickHandler() {
            @Override
            public void onRecordClick(RecordClickEvent event) {
                int editingRow = getEditRow();
                int clickedRow = event.getRecordNum();
                if (editingRow >= 0 && editingRow != clickedRow) {
                    endEditing();
                }
            }
        });
        // salva registro quando usuario clica em coluna não editável do mesmo registro
        this.addCellClickHandler(new CellClickHandler() {
            @Override
            public void onCellClick(CellClickEvent event) {
                if (getCanUpdateCell()) {
                    int editingRow = getEditRow();
                    int clickedRow = event.getRowNum();
                    if (editingRow >= 0 && editingRow == clickedRow) {
                        int editingCol = getEditCol();
                        int clickedCol = event.getColNum();
                        if (editingCol >= 0 && editingCol != clickedCol) {
                            endEditing();
                        }
                    }
                }
            }
        });
        // inclui detalhes, se houver, nos dados a serem salvos
        this.addRowEditorExitHandler(new RowEditorExitHandler() {
            @Override
            public void onRowEditorExit(RowEditorExitEvent event) {
                int editingRow = getEditRow();
                ListGridRecord record = (ListGridRecord) getEditedRecord(editingRow);
                if (event.getEditCompletionEvent() != EditCompletionEvent.ESCAPE_KEYPRESS) {
                    if (isEditing(record) && isDetailExpanded(record)) {
                        record = completeRecord(editingRow, record);
                    }
                    if (!validateEdit(record)) {
                        event.cancel();
                    }
                } else {
                    // fecha os detalhes
                    collapseRecord(record);
                }
            }
        });
        this.addEditCompleteHandler(new EditCompleteHandler() {
            @Override
            public void onEditComplete(EditCompleteEvent event) {
                final ListGridRecord record = getEditedRecord(event);
                if (record != null) {
                    collapseRecord(record);
                    notifyRecordEdited(event, record);
                }
            }
        });
    }
    private void notifyRecordEdited(EditCompleteEvent event, final ListGridRecord record) {
        if (event.getOldValues() == null) {
            // na inserção, se tem detalhes, emenda uma edição
            if (getHasDetail(record)) {
                startUpdating(record, null);
            }
            notifyRecordAdded(record);
        } else {
            notifyRecordUpdated(record);
        }
    }
    // TODO [helenov] avaliar tratamento de inserção de novo registro
    // private void bindChangedHandler() {
    // ResultSet rs = this.getResultSet();
    // rs.addDataChangedHandler( new DataChangedHandler() {
    // @Override
    // public void onDataChanged(DataChangedEvent event) {
    // // TODO implementar destaque do registro editado
    // if ( addedRecord != null ) {
    // addedRecord = null;
    // redraw();
    // scrollToRow(getRecordIndex(addedRecord));
    // // selectRecord(record);
    // }
    // }
    // });
    // }
     
    private boolean validateEdit(ListGridRecord record) {
        if (record.getAttribute("id") == null) {
            return validateAdd(record);
        } else {
            return validateUpdate(record);
        }
    }
     
    @Override
    public boolean canEditCell(int rowNum, int colNum) {
        CustomListGridField customField = new CustomListGridField(this.getField(colNum));
        CanDoActionRecordRuler ruler = customField.getCanEditCellRuler();
        if (ruler != null) {
            ListGridRecord record = (ListGridRecord) this.getEditedRecord(rowNum);
            return ruler.getCanDoAction(record);
        }
        return super.canEditCell(rowNum, colNum);
    }
     
    public boolean isEditing() {
        return this.getEditRow() != -1;
    }
     
    public boolean isEditing(ListGridRecord record) {
        int editRow = this.getEditRow();
        int recordRow = this.getRecordIndex(record);
        boolean canEdit = false;
        // registro está sendo editado
        if (editRow != -1 && editRow == recordRow) {
            canEdit = true;
        }
        return canEdit;
    }
     
    public Record getEditedRecord(int rowNum) {
        return ListGridRecord.getOrCreateRef(super.getEditedRecord(rowNum).getJsObj());
    }
     
    public ListGridRecord getEditedRecord(Record record) {
        int rowNum = getRecordIndex(record);
        return (ListGridRecord) getEditedRecord(rowNum);
    }
     
    private ListGridRecord getEditedRecord(EditCompleteEvent event) {
        ListGridRecord edited = null;
        String pkField = getDataSource().getPrimaryKeyFieldName();
        Record[] response = event.getDsResponse().getData();
        // TODO [helenov] verificar porque às vezes response vem vazio.
        if (response.length > 0) {
            String pkValue = response[0].getAttribute(pkField);
            Record record = getRecordList().find(pkField, pkValue);
            edited = ListGridRecord.getOrCreateRef(record.getJsObj());
        }
        return edited;
    }
     
    private int getFirstEditableColumn(int rowNum) {
        ListGridField[] fields = this.getFields();
        for (int i = 0; i < fields.length; i++) {
            if (canEditCell(rowNum, i)) {
                return i;
            }
        }
        return 0;
    }
     
    public String getFilterState() {
        JSONEncoder encoder = new JSONEncoder();
        return encoder.encode(this.getFilterEditorCriteria().getJsObj());
    }
     
    public void setFilterState(String state) {
        AdvancedCriteria criteria = new AdvancedCriteria(JSONEncoder.decode(state));
        this.setCriteria(criteria);
    }
    public void setActionsColumnPosition(int actionsColumnPosition) {
        this.actionsColumnPosition = actionsColumnPosition;
    }
    public void setCustomAction(Canvas customAction, int position) {
        tools.addMember(customAction, position);
    }
Best regards
Comment