org.zanata.webtrans.client.editor.table.TableEditorPresenter.java Source code

Java tutorial

Introduction

Here is the source code for org.zanata.webtrans.client.editor.table.TableEditorPresenter.java

Source

/*
 * Copyright 2010, Red Hat, Inc. and individual contributors as indicated by the
 * @author tags. See the copyright.txt file in the distribution for a full
 * listing of individual contributors.
 * 
 * This is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 * 
 * This software is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
 * site: http://www.fsf.org.
 */
package org.zanata.webtrans.client.editor.table;

import static org.zanata.webtrans.client.editor.table.TableConstants.*;

import java.util.ArrayList;
import java.util.List;

import org.zanata.common.EditState;
import org.zanata.webtrans.client.action.UndoableAction;
import org.zanata.webtrans.client.action.UndoableTransUnitUpdateAction;
import org.zanata.webtrans.client.action.UndoableTransUnitUpdateHandler;
import org.zanata.webtrans.client.editor.DocumentEditorPresenter;
import org.zanata.webtrans.client.editor.HasPageNavigation;
import org.zanata.webtrans.client.events.DocumentSelectionEvent;
import org.zanata.webtrans.client.events.DocumentSelectionHandler;
import org.zanata.webtrans.client.events.FindMessageEvent;
import org.zanata.webtrans.client.events.FindMessageHandler;
import org.zanata.webtrans.client.events.NavTransUnitEvent;
import org.zanata.webtrans.client.events.NavTransUnitHandler;
import org.zanata.webtrans.client.events.NotificationEvent;
import org.zanata.webtrans.client.events.TransMemoryCopyEvent;
import org.zanata.webtrans.client.events.TransMemoryCopyHandler;
import org.zanata.webtrans.client.events.TransUnitEditEvent;
import org.zanata.webtrans.client.events.TransUnitEditEventHandler;
import org.zanata.webtrans.client.events.TransUnitSelectionEvent;
import org.zanata.webtrans.client.events.TransUnitUpdatedEvent;
import org.zanata.webtrans.client.events.TransUnitUpdatedEventHandler;
import org.zanata.webtrans.client.events.UndoAddEvent;
import org.zanata.webtrans.client.events.NavTransUnitEvent.NavigationType;
import org.zanata.webtrans.client.events.NotificationEvent.Severity;
import org.zanata.webtrans.client.rpc.CachingDispatchAsync;
import org.zanata.webtrans.shared.auth.AuthenticationError;
import org.zanata.webtrans.shared.auth.AuthorizationError;
import org.zanata.webtrans.shared.auth.Identity;
import org.zanata.webtrans.shared.model.DocumentId;
import org.zanata.webtrans.shared.model.TransUnit;
import org.zanata.webtrans.shared.model.TransUnitId;
import org.zanata.webtrans.shared.rpc.EditingTranslationAction;
import org.zanata.webtrans.shared.rpc.EditingTranslationResult;
import org.zanata.webtrans.shared.rpc.GetTransUnitList;
import org.zanata.webtrans.shared.rpc.GetTransUnitListResult;
import org.zanata.webtrans.shared.rpc.GetTransUnitsNavigation;
import org.zanata.webtrans.shared.rpc.GetTransUnitsNavigationResult;
import org.zanata.webtrans.shared.rpc.UpdateTransUnit;
import org.zanata.webtrans.shared.rpc.UpdateTransUnitResult;

import net.customware.gwt.dispatch.client.DispatchAsync;
import net.customware.gwt.presenter.client.EventBus;
import net.customware.gwt.presenter.client.widget.WidgetDisplay;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.logical.shared.HasSelectionHandlers;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.gen2.table.client.TableModel;
import com.google.gwt.gen2.table.client.TableModel.Callback;
import com.google.gwt.gen2.table.client.TableModelHelper.Request;
import com.google.gwt.gen2.table.client.TableModelHelper.SerializableResponse;
import com.google.gwt.gen2.table.event.client.HasPageChangeHandlers;
import com.google.gwt.gen2.table.event.client.HasPageCountChangeHandlers;
import com.google.gwt.gen2.table.event.client.PageChangeHandler;
import com.google.gwt.gen2.table.event.client.PageCountChangeHandler;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Inject;

public class TableEditorPresenter extends DocumentEditorPresenter<TableEditorPresenter.Display>
        implements HasPageNavigation {
    public interface Display extends WidgetDisplay, HasPageNavigation {
        HasSelectionHandlers<TransUnit> getSelectionHandlers();

        HasPageChangeHandlers getPageChangeHandlers();

        HasPageCountChangeHandlers getPageCountChangeHandlers();

        RedirectingCachedTableModel<TransUnit> getTableModel();

        void setTableModelHandler(TableModelHandler<TransUnit> hadler);

        void reloadPage();

        void setPageSize(int size);

        void gotoRow(int row);

        void gotoRow(int row, boolean andEdit);

        int getCurrentPageNumber();

        TransUnit getTransUnitValue(int row);

        InlineTargetCellEditor getTargetCellEditor();

        List<TransUnit> getRowValues();

        boolean isFirstPage();

        boolean isLastPage();

        int getCurrentPage();

        int getPageSize();

        void setFindMessage(String findMessage);

        void startProcessing();

        void stopProcessing();
    }

    private DocumentId documentId;

    private final DispatchAsync dispatcher;
    private final Identity identity;
    private TransUnit selectedTransUnit;
    // private int lastRowNum;
    private List<Long> transIdNextFuzzyCache = new ArrayList<Long>();
    private List<Long> transIdPrevFuzzyCache = new ArrayList<Long>();

    private int curRowIndex;
    private int curPage;

    private String findMessage;

    private final TableEditorMessages messages;

    private UndoableAction<?> undoInProcessing;

    @Inject
    public TableEditorPresenter(final Display display, final EventBus eventBus,
            final CachingDispatchAsync dispatcher, final Identity identity, final TableEditorMessages messages) {
        super(display, eventBus);
        this.dispatcher = dispatcher;
        this.identity = identity;
        this.messages = messages;
    }

    @Override
    protected void onBind() {
        display.setTableModelHandler(tableModelHandler);
        display.setPageSize(TableConstants.PAGE_SIZE);
        registerHandler(display.getSelectionHandlers().addSelectionHandler(new SelectionHandler<TransUnit>() {
            @Override
            public void onSelection(SelectionEvent<TransUnit> event) {
                if (selectedTransUnit == null
                        || !event.getSelectedItem().getId().equals(selectedTransUnit.getId())) {
                    selectedTransUnit = event.getSelectedItem();
                    Log.info("SelectedTransUnit " + selectedTransUnit.getId());
                    // Clean the cache when we click the new entry
                    if (!transIdNextFuzzyCache.isEmpty())
                        transIdNextFuzzyCache.clear();
                    if (!transIdPrevFuzzyCache.isEmpty())
                        transIdPrevFuzzyCache.clear();
                    eventBus.fireEvent(new TransUnitSelectionEvent(selectedTransUnit));
                }
            }
        }));

        registerHandler(eventBus.addHandler(DocumentSelectionEvent.getType(), new DocumentSelectionHandler() {
            @Override
            public void onDocumentSelected(DocumentSelectionEvent event) {
                if (!event.getDocument().getId().equals(documentId)) {
                    display.startProcessing();
                    documentId = event.getDocument().getId();
                    display.getTableModel().clearCache();
                    display.getTableModel().setRowCount(TableModel.UNKNOWN_ROW_COUNT);
                    display.gotoPage(0, true);
                    display.stopProcessing();
                }
            }
        }));

        registerHandler(eventBus.addHandler(FindMessageEvent.getType(), new FindMessageHandler() {

            @Override
            public void onFindMessage(FindMessageEvent event) {
                Log.info("Find Message Event: " + event.getMessage());
                if (selectedTransUnit != null) {
                    Log.info("cancelling selection");
                    display.getTargetCellEditor().clearSelection();
                }
                display.startProcessing();
                findMessage = event.getMessage();
                display.setFindMessage(findMessage);
                display.getTableModel().clearCache();
                display.getTableModel().setRowCount(TableModel.UNKNOWN_ROW_COUNT);
                display.gotoPage(0, true);
                display.stopProcessing();
            }

        }));

        registerHandler(eventBus.addHandler(TransUnitUpdatedEvent.getType(), new TransUnitUpdatedEventHandler() {
            @Override
            public void onTransUnitUpdated(TransUnitUpdatedEvent event) {
                if (documentId != null && documentId.equals(event.getDocumentId())) {
                    // Clear the cache
                    if (!transIdNextFuzzyCache.isEmpty())
                        transIdNextFuzzyCache.clear();
                    if (!transIdPrevFuzzyCache.isEmpty())
                        transIdPrevFuzzyCache.clear();
                    // TODO this test never succeeds
                    if (selectedTransUnit != null
                            && selectedTransUnit.getId().equals(event.getTransUnit().getId())) {
                        // handle change in current selection
                        // eventBus.fireEvent(new NotificationEvent(Severity.Warning,
                        // "Someone else updated this translation unit. you're in trouble..."));
                        // display.getTableModel().setRowValue(row, rowValue);
                        Log.info("selected TU updated; cancelling edit");
                        display.getTargetCellEditor().cancelEdit();

                        // TODO reload page and return
                    }

                    boolean reloadPage = false;
                    if (reloadPage) {
                        display.getTargetCellEditor().cancelEdit();
                        display.getTableModel().clearCache();
                        display.reloadPage();
                    } else {
                        final Integer rowOffset = getRowOffset(event.getTransUnit().getId());
                        // - add TU index to model
                        if (rowOffset != null) {
                            final int row = display.getCurrentPage() * display.getPageSize() + rowOffset;
                            Log.info("row calculated as " + row);
                            display.getTableModel().setRowValueOverride(row, event.getTransUnit());
                            if (undoInProcessing != null) {
                                Log.info("check current undo");
                                if (undoInProcessing instanceof UndoableTransUnitUpdateAction) {
                                    TransUnit tu = ((UndoableTransUnitUpdateAction) undoInProcessing)
                                            .getPreviousState();
                                    Log.info("" + tu.getId() + " " + event.getTransUnit().getId());
                                    if (tu.getId().equals(event.getTransUnit().getId())) {
                                        Log.info("go to row:" + row);
                                        tableModelHandler.gotoRow(row);
                                        undoInProcessing = null;
                                    }
                                }
                            }
                        } else {
                            display.getTableModel().clearCache();
                            display.getTargetCellEditor().cancelEdit();
                            if (undoInProcessing != null) {
                                Log.info("check current undo");
                                if (undoInProcessing instanceof UndoableTransUnitUpdateAction) {
                                    TransUnit tu = ((UndoableTransUnitUpdateAction) undoInProcessing)
                                            .getPreviousState();
                                    Log.info("" + tu.getId() + " " + event.getTransUnit().getId());
                                    if (tu.getId().equals(event.getTransUnit().getId())) {
                                        int pageNum = ((UndoableTransUnitUpdateAction) undoInProcessing)
                                                .getCurrentPage();
                                        int rowNum = ((UndoableTransUnitUpdateAction) undoInProcessing).getRowNum();
                                        int row = pageNum * PAGE_SIZE + rowNum;
                                        Log.info("go to row:" + row);
                                        Log.info("go to page:" + pageNum);
                                        tableModelHandler.gotoRow(row);
                                        undoInProcessing = null;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }));

        registerHandler(eventBus.addHandler(TransUnitEditEvent.getType(), new TransUnitEditEventHandler() {
            @Override
            public void onTransUnitEdit(TransUnitEditEvent event) {
                if (documentId != null && documentId.equals(event.getDocumentId())) {
                    if (selectedTransUnit != null && selectedTransUnit.getId().equals(event.getTransUnitId())) {
                        // handle change in current selection
                        if (!event.getSessionId().equals(identity.getSessionId()))
                            eventBus.fireEvent(new NotificationEvent(Severity.Warning, messages.notifyInEdit()));
                    }
                    // display.getTableModel().clearCache();
                    // display.reloadPage();
                }
            }
        }));

        registerHandler(eventBus.addHandler(NavTransUnitEvent.getType(), new NavTransUnitHandler() {
            @Override
            public void onNavTransUnit(NavTransUnitEvent event) {
                if (selectedTransUnit != null) {
                    int step = event.getStep();
                    Log.info("Step " + step);
                    // Send message to server to stop editing current selection
                    // stopEditing(selectedTransUnit);

                    InlineTargetCellEditor editor = display.getTargetCellEditor();

                    // If goto Next or Prev Fuzzy/New Trans Unit
                    if (event.getRowType() == NavigationType.PrevEntry) {
                        editor.handlePrev();
                    }

                    if (event.getRowType() == NavigationType.NextEntry) {
                        editor.handleNext();
                    }

                    if (event.getRowType() == NavigationType.PrevFuzzyOrUntranslated) {
                        editor.handlePrevState();
                    }

                    if (event.getRowType() == NavigationType.NextFuzzyOrUntranslated) {
                        editor.handleNextState();
                    }

                }
            }
        }));

        registerHandler(eventBus.addHandler(TransMemoryCopyEvent.getType(), new TransMemoryCopyHandler() {
            @Override
            public void onTransMemoryCopy(TransMemoryCopyEvent event) {
                // When user clicked on copy-to-target anchor, it checks
                // if user is editing any target. Notifies if not.
                if (display.getTargetCellEditor().isEditing()) {
                    display.getTargetCellEditor().setText(event.getTargetResult());
                    eventBus.fireEvent(new NotificationEvent(Severity.Info, messages.notifyCopied()));
                } else
                    eventBus.fireEvent(new NotificationEvent(Severity.Error, messages.notifyUnopened()));
            }
        }));

        Event.addNativePreviewHandler(new NativePreviewHandler() {
            @Override
            public void onPreviewNativeEvent(NativePreviewEvent event) {
                // Only when the Table is showed and editor is closed, the keyboard
                // event will be processed.
                if (display.asWidget().isVisible() && !display.getTargetCellEditor().isFocused()) {
                    NativeEvent nativeEvent = event.getNativeEvent();
                    String nativeEventType = nativeEvent.getType();
                    int keyCode = nativeEvent.getKeyCode();
                    boolean shiftKey = nativeEvent.getShiftKey();
                    boolean altKey = nativeEvent.getAltKey();
                    boolean ctrlKey = nativeEvent.getCtrlKey();
                    if (nativeEventType.equals("keypress") && !shiftKey && !altKey && !ctrlKey) {
                        // PageDown key
                        switch (keyCode) {
                        case KeyCodes.KEY_PAGEDOWN:
                            Log.info("fired event of type " + event.getAssociatedType().getClass().getName());
                            if (!display.isLastPage())
                                gotoNextPage();
                            event.cancel();
                            break;
                        // PageUp key
                        case KeyCodes.KEY_PAGEUP:
                            Log.info("fired event of type " + event.getAssociatedType().getClass().getName());
                            if (!display.isFirstPage())
                                gotoPreviousPage();
                            event.cancel();
                            break;
                        // Home
                        case KeyCodes.KEY_HOME:
                            Log.info("fired event of type " + event.getAssociatedType().getClass().getName());
                            display.gotoFirstPage();
                            event.cancel();
                            break;
                        // End
                        case KeyCodes.KEY_END:
                            Log.info("fired event of type " + event.getAssociatedType().getClass().getName());
                            display.gotoLastPage();
                            event.cancel();
                            break;
                        default:
                            break;
                        }
                    }
                }
            }
        });

        display.gotoFirstPage();
    }

    public Integer getRowOffset(TransUnitId transUnitId) {
        // TODO inefficient!
        for (int i = 0; i < display.getRowValues().size(); i++) {
            if (transUnitId.equals(display.getTransUnitValue(i).getId())) {
                Log.info("getRowOffset returning " + i);
                return i;
            }
        }
        return null;
    }

    private final TableModelHandler<TransUnit> tableModelHandler = new TableModelHandler<TransUnit>() {

        @Override
        public void requestRows(final Request request, final Callback<TransUnit> callback) {

            int numRows = request.getNumRows();
            int startRow = request.getStartRow();
            Log.info("Table requesting " + numRows + " starting from " + startRow);

            if (documentId == null) {
                callback.onFailure(new RuntimeException("No DocumentId"));
                return;
            }

            dispatcher.execute(new GetTransUnitList(documentId, startRow, numRows, findMessage),
                    new AsyncCallback<GetTransUnitListResult>() {
                        @Override
                        public void onSuccess(GetTransUnitListResult result) {
                            Log.info("find message:" + findMessage);
                            SerializableResponse<TransUnit> response = new SerializableResponse<TransUnit>(
                                    result.getUnits());
                            Log.info("Got " + result.getUnits().size() + " rows back");
                            callback.onRowsReady(request, response);
                            Log.info("Total of " + result.getTotalCount() + " rows available");
                            display.getTableModel().setRowCount(result.getTotalCount());
                        }

                        @Override
                        public void onFailure(Throwable caught) {
                            if (caught instanceof AuthenticationError) {
                                eventBus.fireEvent(
                                        new NotificationEvent(Severity.Error, messages.notifyNotLoggedIn()));
                            } else if (caught instanceof AuthorizationError) {
                                eventBus.fireEvent(
                                        new NotificationEvent(Severity.Error, messages.notifyLoadFailed()));
                            } else {
                                Log.error("GetTransUnits failure " + caught, caught);
                                eventBus.fireEvent(
                                        new NotificationEvent(Severity.Error, messages.notifyUnknownError()));
                            }
                        }
                    });
        }

        @Override
        public boolean onSetRowValue(int row, TransUnit rowValue) {
            final UpdateTransUnit updateTransUnit = new UpdateTransUnit(rowValue.getId(), rowValue.getTarget(),
                    rowValue.getStatus());
            dispatcher.execute(updateTransUnit, new AsyncCallback<UpdateTransUnitResult>() {
                @Override
                public void onFailure(Throwable e) {
                    Log.error("UpdateTransUnit failure " + e, e);
                    eventBus.fireEvent(new NotificationEvent(Severity.Error,
                            messages.notifyUpdateFailed(e.getLocalizedMessage())));
                    // put back the old cell value
                    display.getTableModel().clearCache();
                    display.reloadPage();
                }

                @Override
                public void onSuccess(UpdateTransUnitResult result) {
                    eventBus.fireEvent(new NotificationEvent(Severity.Info, messages.notifyUpdateSaved()));
                }
            });

            stopEditing(rowValue);

            return true;
        }

        public void onCancel(TransUnit rowValue) {
            // stopEditing(rowValue);
        }

        @Override
        public void gotoNextRow(int row) {
            curPage = display.getCurrentPage();
            curRowIndex = curPage * 50 + row;
            int rowIndex = curPage * 50 + row + 1;
            if (rowIndex < display.getTableModel().getRowCount()) {
                gotoRow(rowIndex);
            }
        }

        @Override
        public void gotoPrevRow(int row) {
            curPage = display.getCurrentPage();
            int rowIndex = curPage * 50 + row - 1;
            if (rowIndex >= 0) {
                gotoRow(rowIndex);
            }
        }

        @Override
        public void nextFuzzyIndex(int row) {
            // Convert row number to row Index in table
            curPage = display.getCurrentPage();
            curRowIndex = curPage * TableConstants.PAGE_SIZE + row;
            Log.info("Current Row Index" + curRowIndex);
            if (curRowIndex < display.getTableModel().getRowCount())
                gotoNextState();
        }

        @Override
        public void prevFuzzyIndex(int row) {
            // Convert row number to row Index in table
            curPage = display.getCurrentPage();
            curRowIndex = curPage * TableConstants.PAGE_SIZE + row;
            Log.info("Current Row Index" + curRowIndex);
            if (curRowIndex > 0)
                gotoPrevState();
        }

        @Override
        public void gotoRow(int rowIndex) {
            int pageNum = rowIndex / (MAX_PAGE_ROW + 1);
            int rowNum = rowIndex % (MAX_PAGE_ROW + 1);
            if (pageNum != curPage)
                display.gotoPage(pageNum, false);
            selectedTransUnit = display.getTransUnitValue(rowNum);
            display.gotoRow(rowNum);
        }

        @Override
        void addUndoList(UndoableAction<?> undoableAction) {
            if (undoableAction instanceof UndoableTransUnitUpdateAction) {
                UndoableTransUnitUpdateHandler handler = new UndoableTransUnitUpdateHandler() {
                    @Override
                    public void undo(UndoableTransUnitUpdateAction action) {
                        undoInProcessing = action;
                        if (selectedTransUnit != null) {
                            Log.info("cancel edit");
                            cancelEdit();
                        }
                        onSetRowValue(0, action.getPreviousState());
                    }
                };
                ((UndoableTransUnitUpdateAction) undoableAction).setHandler(handler);
            }

            Log.info("add current undo");
            eventBus.fireEvent(new UndoAddEvent(undoableAction));
        }

        @Override
        int getCurrentPage() {
            return display.getCurrentPage();
        }

    };

    private void stopEditing(TransUnit rowValue) {
        dispatcher.execute(new EditingTranslationAction(rowValue.getId(), EditState.StopEditing),
                new AsyncCallback<EditingTranslationResult>() {
                    @Override
                    public void onSuccess(EditingTranslationResult result) {
                        // eventBus.fireEvent(new NotificationEvent(Severity.Warning,
                        // "TransUnit Editing is finished"));
                    }

                    @Override
                    public void onFailure(Throwable caught) {
                        Log.error("EditingTranslationAction failure " + caught, caught);
                        eventBus.fireEvent(new NotificationEvent(Severity.Error, messages.notifyStopFailed()));
                    }

                });
    }

    boolean isReqComplete = true;

    private void cacheNextFuzzy(final NavigationCacheCallback callBack) {
        isReqComplete = false;
        dispatcher.execute(new GetTransUnitsNavigation(selectedTransUnit.getId().getId(), 3, false, findMessage),
                new AsyncCallback<GetTransUnitsNavigationResult>() {
                    @Override
                    public void onSuccess(GetTransUnitsNavigationResult result) {
                        isReqComplete = true;
                        if (!result.getUnits().isEmpty()) {
                            for (Long offset : result.getUnits()) {
                                transIdNextFuzzyCache.add(offset + curRowIndex);
                            }
                            callBack.nextFuzzy();
                        }
                    }

                    @Override
                    public void onFailure(Throwable caught) {
                        Log.error("GetTransUnitsStates failure " + caught, caught);
                    }
                });
    }

    private void cachePrevFuzzy(final NavigationCacheCallback callBack) {
        isReqComplete = false;
        dispatcher.execute(new GetTransUnitsNavigation(selectedTransUnit.getId().getId(), 3, true, findMessage),
                new AsyncCallback<GetTransUnitsNavigationResult>() {
                    @Override
                    public void onSuccess(GetTransUnitsNavigationResult result) {
                        isReqComplete = true;
                        if (!result.getUnits().isEmpty()) {
                            for (Long offset : result.getUnits()) {
                                transIdPrevFuzzyCache.add(curRowIndex - offset);
                            }
                            callBack.prevFuzzy();
                        }
                    }

                    @Override
                    public void onFailure(Throwable caught) {
                        Log.error("GetTransUnitsStates failure " + caught, caught);
                    }
                });
    }

    private void gotoPrevState() {
        Log.info("Previous FuzzyOrUntranslated State");

        // Clean the cache for Next Fuzzy to avoid issues about cache is
        // obsolete
        transIdNextFuzzyCache.clear();
        // If the catch of fuzzy row is empty and request is complete, generate
        // one
        if (transIdPrevFuzzyCache.isEmpty()) {
            if (isReqComplete)
                cachePrevFuzzy(cacheCallback);
        } else {
            int size = transIdPrevFuzzyCache.size();
            int offset = transIdPrevFuzzyCache.get(size - 1).intValue();
            if (curRowIndex > offset) {
                for (int i = 0; i < size; i++) {
                    int fuzzyRowIndex = transIdPrevFuzzyCache.get(i).intValue();
                    if (curRowIndex > fuzzyRowIndex) {
                        cancelEdit();
                        tableModelHandler.gotoRow(fuzzyRowIndex);
                        break;
                    }
                }
            } else {
                transIdPrevFuzzyCache.clear();
                cachePrevFuzzy(cacheCallback);
            }
        }
    }

    NavigationCacheCallback cacheCallback = new NavigationCacheCallback() {
        @Override
        public void nextFuzzy() {
            gotoNextState();
        }

        @Override
        public void prevFuzzy() {
            gotoPrevState();
        }

    };

    private void gotoNextState() {
        Log.info("go to Next FuzzyOrUntranslated State");

        transIdPrevFuzzyCache.clear();
        // If the cache of next fuzzy is empty, generate one
        if (transIdNextFuzzyCache.isEmpty()) {
            if (isReqComplete)
                cacheNextFuzzy(cacheCallback);
        } else {
            int size = transIdNextFuzzyCache.size();
            int offset = transIdNextFuzzyCache.get(size - 1).intValue();
            if (curRowIndex < offset) {
                for (int i = 0; i < size; i++) {
                    int fuzzyRowIndex = transIdNextFuzzyCache.get(i).intValue();
                    if (curRowIndex < fuzzyRowIndex) {
                        cancelEdit();
                        tableModelHandler.gotoRow(fuzzyRowIndex);
                        break;
                    }
                }
            } else {
                transIdNextFuzzyCache.clear();
                cacheNextFuzzy(cacheCallback);
            }
        }
    }

    public TransUnit getSelectedTransUnit() {
        return selectedTransUnit;
    }

    @Override
    protected void onUnbind() {
    }

    @Override
    public void onRevealDisplay() {
    }

    @Override
    public void gotoFirstPage() {
        display.gotoFirstPage();
    }

    @Override
    public void gotoLastPage() {
        display.gotoLastPage();
    }

    @Override
    public void gotoNextPage() {
        display.gotoNextPage();
    }

    @Override
    public void gotoPage(int page, boolean forced) {
        display.gotoPage(page, forced);
    }

    @Override
    public void gotoPreviousPage() {
        display.gotoPreviousPage();
    }

    public void addPageChangeHandler(PageChangeHandler handler) {
        display.getPageChangeHandlers().addPageChangeHandler(handler);
    }

    public void addPageCountChangeHandler(PageCountChangeHandler handler) {
        display.getPageCountChangeHandlers().addPageCountChangeHandler(handler);
    }

    public DocumentId getDocumentId() {
        return documentId;
    }

    public void cancelEdit() {
        display.getTargetCellEditor().cancelEdit();
    }
}