edu.caltech.ipac.firefly.ui.background.DownloadGroupPanel.java Source code

Java tutorial

Introduction

Here is the source code for edu.caltech.ipac.firefly.ui.background.DownloadGroupPanel.java

Source

/*
 * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt
 */
package edu.caltech.ipac.firefly.ui.background;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.resources.client.TextResource;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.widgetideas.client.ProgressBar;
import edu.caltech.ipac.firefly.core.Application;
import edu.caltech.ipac.firefly.core.background.ActivationFactory;
import edu.caltech.ipac.firefly.core.background.BackgroundMonitor;
import edu.caltech.ipac.firefly.core.background.BackgroundState;
import edu.caltech.ipac.firefly.core.background.BackgroundStatus;
import edu.caltech.ipac.firefly.core.background.MonitorItem;
import edu.caltech.ipac.firefly.core.background.PackageProgress;
import edu.caltech.ipac.firefly.resbundle.css.CssData;
import edu.caltech.ipac.firefly.resbundle.css.FireflyCss;
import edu.caltech.ipac.firefly.resbundle.images.IconCreator;
import edu.caltech.ipac.firefly.ui.GwtUtil;
import edu.caltech.ipac.firefly.ui.PopupUtil;
import edu.caltech.ipac.firefly.util.PropFile;
import edu.caltech.ipac.firefly.util.WebAssert;
import edu.caltech.ipac.firefly.util.WebClassProperties;
import edu.caltech.ipac.firefly.util.event.Notifications;
import edu.caltech.ipac.util.ComparisonUtil;
import edu.caltech.ipac.util.StringUtils;

import static edu.caltech.ipac.firefly.core.background.JobAttributes.DownloadScript;
import static edu.caltech.ipac.firefly.core.background.JobAttributes.EmailSent;

/**
 * User: roby
 * Date: Oct 24, 2008
 * Time: 9:57:02 AM
 */

/**
 * @author Trey Roby
 */
public class DownloadGroupPanel extends Composite {

    public enum FileDownloadStatus {
        NONE, WORKING, DONE
    }

    interface PFile extends PropFile {
        @Source("DownloadGroupPanel.prop")
        TextResource get();
    }

    private static WebClassProperties _prop = new WebClassProperties(DownloadGroupPanel.class,
            (PFile) GWT.create(PFile.class));
    private static final String STATUS_STARTING_TXT = _prop.getName("status.starting");
    private static final String STATUS_STARTING_PART_TXT = _prop.getName("status.starting.part");
    private static final String STATUS_WAITING_PART_TXT = _prop.getName("status.waiting.part");
    private static final String STATUS_CANCELED_TXT = _prop.getName("status.canceled");
    private static final String STATUS_NODETAIL_DETAIL_TXT = _prop.getName("noDetails.detail");

    private static final String STATUS_ABORTED_DETAIL_TXT = _prop.getName("status.aborted.detail");
    private static final String STATUS_FAIL_DETAIL_TXT = _prop.getName("status.fail.detail");
    private static final String STATUS_CANCELED_DETAIL_TXT = _prop.getName("status.canceled.detail");

    private static final String ABORT_TIP = _prop.getTip("abort");
    private static final String DELETE_TIP = _prop.getTip("delete");

    private static final String EMAIL_SENT = _prop.getName("emailSent");
    private static final String EMAIL_NOT_SENT = _prop.getName("emailNotSent");

    private static final FireflyCss _ffCss = CssData.Creator.getInstance().getFireflyCss();

    private final static int TITLE_POS = 0;
    private final static int SUBTITLE_POS = 1;
    private final static int STATUS_POS = 2;
    private final static int STATUS_ALT_POS = 0;
    private final static int WORKING_ICON_POS = 4;
    private final static int ABORT_BUTTON_POS = 5;
    private final static int WARNINGS_POS = 2;
    private final static int FOOTNOTE_POS_A = 0;
    private final static int FOOTNOTE_POS_B = 2;

    private final FlexTable _content = new FlexTable();
    private final String _title;
    private final MonitorItem _monItem;
    private final Image _workingIcon = new Image(GwtUtil.LOADING_ICON_URL);
    private Widget _warningsButton;
    private BackgroundStatus _oldBgStat;
    private DetailUIInfo _detailUI[];
    private FocusPanel _abortButton;
    private boolean _aborted = false;
    private boolean _success = false;
    private boolean _calledAutoActivation = false;
    private String _waitingMsg;

    //======================================================================
    //----------------------- Constructors ---------------------------------
    //======================================================================

    DownloadGroupPanel(MonitorItem monItem) {
        SimplePanel topPanel = new SimplePanel();
        initWidget(topPanel);
        topPanel.setWidget(_content);
        _monItem = monItem;
        _oldBgStat = monItem.getStatus();
        _title = monItem.getTitle();
        _waitingMsg = ActivationFactory.getInstance().getWaitingMsg(monItem.getUIHint());
        layout();

    }

    //======================================================================
    //----------------------- Public Methods -------------------------------
    //======================================================================

    /**
     * update the package report and the display the user see.
     * @return true, if the updated report requires the users attention, usually the
     *         is a download ready. false, otherwise
     */
    boolean updateUI() {
        BackgroundStatus bgStat = _monItem.getStatus();
        WebAssert.argTst(ComparisonUtil.equals(bgStat.getID(), _oldBgStat.getID()),
                "You cannot update the report to one with " + "a different package id.");
        boolean retval = update();
        _oldBgStat = _monItem.getStatus();
        if (_monItem.isDone()) {
            if (_monItem.getState() == BackgroundState.SUCCESS) {
                if (!_success) {
                    String name = Application.getInstance().getAppName();
                    Notifications.notify(name + " Task Completed",
                            _monItem.getReportDesc() + ", " + _monItem.getTitle() + " has completed.");
                }
                _success = true;
                if (!_calledAutoActivation && _monItem.getActivateOnCompletion()
                        && !_monItem.getStatus().isMultiPart()) {
                    _calledAutoActivation = true;
                    ActivationFactory.getInstance().activate(_monItem, 0, !_monItem.getImmediately());
                }
            }
        }

        return retval;

    }

    int getPartsLines() {
        int retval;
        int size = getPartCount(_monItem);
        if (_monItem.getStatus().isFail()) {
            retval = 1;
        } else if (size == 0) {
            retval = 1;
        } else if (size < 3) {
            retval = size;
        } else {
            retval = size / 2 + 1;
        }
        return retval;
    }

    MonitorItem getMonitorItem() {
        return _monItem;
    }

    private static int getPartCount(MonitorItem item) {
        return getPartCount(item, item.getStatus());
    }

    private static int getPartCount(MonitorItem item, BackgroundStatus bgStat) {
        return bgStat.getPackageCount();
    }

    public BackgroundManager.AttnState getAttentionState() {

        BackgroundStatus bgStat = _monItem.getStatus();
        boolean ready = false;
        boolean working = !bgStat.isDone();
        boolean fail = bgStat.isFail();
        if (!fail) {
            int partCnt = getPartCount(_monItem);
            if (partCnt > 0) {
                for (int i = 0; (i < partCnt); i++) {
                    if (bgStat.getPartProgress(i).isDone() && !_monItem.isActivated(i))
                        ready = true;
                }
            }
        }

        BackgroundManager.AttnState attnState;

        if (fail)
            attnState = BackgroundManager.AttnState.FAIL;
        else if (ready && working)
            attnState = BackgroundManager.AttnState.READY_WORKING;
        else if (ready)
            attnState = BackgroundManager.AttnState.READY;
        else if (working)
            attnState = BackgroundManager.AttnState.WORKING;
        else
            attnState = BackgroundManager.AttnState.NONE;

        return attnState;
    }

    public int getUndownloadCnt() {
        int total = 0;
        BackgroundStatus bgStat = _monItem.getStatus();
        if (!bgStat.isFail()) {
            for (int i = 0; (i < getPartCount(_monItem)); i++) {
                if (bgStat.getPartProgress(i).isDone() && !_monItem.isActivated(i)) {
                    total++;
                }
            }
        }
        return total;
    }

    public int getWorkingCnt() {
        int total = 0;
        BackgroundStatus bgStat = _monItem.getStatus();
        if (bgStat.isActive()) {
            for (int i = 0; (i < getPartCount(_monItem)); i++) {
                if (!bgStat.getPartProgress(i).isDone())
                    total++;
            }
        }
        return total;
    }

    //======================================================================
    //------------------ Private / Protected Methods -----------------------
    //======================================================================

    private void layout() {

        _abortButton = makeAbortButton();
        _abortButton.addStyleName("download-group-abort");
        _content.addStyleName("download-group-content");
        _detailUI = new DetailUIInfo[getPartCount(_monItem)];
        layoutDetails();
    }

    //    private HTML getTitleWidget() {
    //        HTML titleHTML= new HTML(_title);
    //        titleHTML.addStyleName("group-title");
    //        return titleHTML;
    //    }

    private void updateItemTitle() {
        HTML titleHTML = new HTML(_title);
        titleHTML.addStyleName("group-title");
        FlexTable.FlexCellFormatter formatter = _content.getFlexCellFormatter();
        _content.setWidget(0, TITLE_POS, titleHTML);
        String width;
        if (_title.length() < 20)
            width = "200px";
        else if (_title.length() < 20)
            width = "250px";
        else if (_title.length() < 25)
            width = "300px";
        else
            width = "350px";

        formatter.setWidth(0, TITLE_POS, width);
    }

    private void layoutDetails() {

        _content.clear();

        _content.addStyleName("download-group-header");

        updateItemTitle();
        _content.setWidget(0, SUBTITLE_POS, _workingIcon);
        _content.setWidget(0, ABORT_BUTTON_POS, _abortButton);
        _content.setWidget(0, WORKING_ICON_POS, _workingIcon);
        FlexTable.FlexCellFormatter formatter = _content.getFlexCellFormatter();
        formatter.setWidth(0, ABORT_BUTTON_POS, "35px");
        formatter.setWidth(0, WORKING_ICON_POS, "20px");

        BackgroundStatus bgStat = _monItem.getStatus();
        int partCnt = getPartCount(_monItem);
        if (_monItem.getState() == BackgroundState.STARTING || (partCnt == 0 && !bgStat.isDone())) {
            Widget w = createWorkingWidget(STATUS_STARTING_TXT);
            _content.setWidget(0, STATUS_POS, w);
        } else {
            TablePos tpos;
            for (int i = 0; (i < partCnt); i++) {
                _detailUI[i] = new DetailUIInfo();
                tpos = getStatusPosCol(_monItem, i);
                _content.setWidget(tpos.getRow(), tpos.getCol(), makeDetailStateWidget(i));
                formatter.setWidth(tpos.getRow(), tpos.getCol(), "250px");
                if (tpos.getCol() == STATUS_ALT_POS) {
                    formatter.setHorizontalAlignment(tpos.getRow(), tpos.getCol(),
                            HasHorizontalAlignment.ALIGN_RIGHT);
                }
                formatter.setWidth(i, ABORT_BUTTON_POS, "35px");
            }
        }

        _warningsButton = GwtUtil.makeLinkButton(_prop.makeBase("warnings"), new ClickHandler() {
            public void onClick(ClickEvent ev) {
                showWarnings((Widget) ev.getSource());
            }
        });
        _content.setWidget(partCnt, WARNINGS_POS, _warningsButton);
        //        formatter.setColSpan(partCnt,WARNINGS_POS,3 );
        formatter.setHorizontalAlignment(partCnt, WARNINGS_POS, HasHorizontalAlignment.ALIGN_RIGHT);
        _warningsButton.setVisible(false);
    }

    private void showWarnings(Widget p) {
        BackgroundStatus bgStat = _monItem.getStatus();
        StringBuilder txt = new StringBuilder(300);
        if (bgStat.getNumMessages() > 0) {
            txt.append(_prop.getName("warnings.desc"));
            txt.append("<br><ul>");
            for (String m : bgStat.getMessageList()) {
                txt.append("<li>").append(m).append("</li>");
            }
            txt.append("</ul>");
        } else {
            txt.append(_prop.getName("nowarnings"));
        }
        PopupUtil.showInfo(p, _prop.getTitle("warnings"), txt.toString());
    }

    private boolean update() {

        boolean retval;
        if (getPartCount(_monItem) == getPartCount(_monItem, _oldBgStat)) {
            retval = updateDetails();
        } else {
            _detailUI = new DetailUIInfo[getPartCount(_monItem)];
            layoutDetails();
            retval = updateDetails();
        }
        return retval;
    }

    private boolean updateDetails() {
        BackgroundStatus newStat = _monItem.getStatus();
        int partCount = getPartCount(_monItem);
        boolean open = false;

        if (newStat.isDone())
            disableIndicators();

        if (newStat.isFail()) {
            disableIndicators();
            _content.clear();
            Label label;
            switch (newStat.getState()) {
            case USER_ABORTED:
                label = new Label(STATUS_ABORTED_DETAIL_TXT);
                break;
            case CANCELED:
                label = new Label(STATUS_CANCELED_DETAIL_TXT);
                break;
            case FAIL:
                label = new Label(STATUS_FAIL_DETAIL_TXT);
                break;
            default:
                label = new Label("");
                break;

            }
            updateItemTitle();
            //            _content.setWidget(0, TITLE_POS,getTitleWidget());
            _content.setWidget(0, SUBTITLE_POS, label);
            _content.setWidget(0, ABORT_BUTTON_POS, _abortButton);
            _content.getCellFormatter().setWidth(0, SUBTITLE_POS, "300px");
            DOM.setStyleAttribute(label.getElement(), "float", "right");
            if (_content.getRowCount() > 1) {
                for (int i = 1; (i < _content.getRowCount()); i++) {
                    _content.removeRow(i);
                }
            }
        } else if (partCount == 0 && !newStat.isDone()) {
            String msg = STATUS_NODETAIL_DETAIL_TXT;
            if (newStat.getState() == BackgroundState.WAITING && !StringUtils.isEmpty(_waitingMsg)) {
                msg = _waitingMsg;
            }
            Label label = new Label(msg);
            _content.setWidget(0, STATUS_POS, label);
        } else {
            TablePos tpos;
            for (int i = 0; (i < partCount); i++) {
                if (i >= getPartCount(_monItem, _oldBgStat) || _monItem.getStatus().getPartProgress(i)
                        .isDone() != _oldBgStat.getPartProgress(i).isDone()) {
                    tpos = getStatusPosCol(_monItem, i);
                    _content.setWidget(tpos.getRow(), tpos.getCol(), makeDetailStateWidget(i));
                }

                if (newStat.isSuccess() && !_monItem.isActivated(i)) {
                    open = true;
                } else if (newStat.getState() == BackgroundState.WORKING && !newStat.getPartProgress(i).isDone()) {
                    _detailUI[i].setProgressDetail(newStat, i);
                }
            }
            if (partCount == 0) {
                tpos = getStatusPosCol(_monItem, 0);
                _content.setWidget(tpos.getRow(), tpos.getCol(), makeDetailStateWidget(0));

            }
            _warningsButton.setVisible(newStat.getNumMessages() > 0);

        }

        if (newStat.isSuccess())
            addSuccessFootnote(newStat);

        return open;
    }

    private void addSuccessFootnote(BackgroundStatus bgStat) {

        if (bgStat.isSuccess()) {
            int partCount = getPartCount(_monItem);
            TablePos tpos = getStatusPosCol(_monItem, partCount - 1);
            int currentRow = tpos.getRow() + 1 + (partCount % 2);
            String paddingTop = "15px";

            if (bgStat.hasAttribute(EmailSent)) {
                Label msgL = new Label(EMAIL_SENT);
                GwtUtil.setStyle(msgL, "paddingTop", paddingTop);
                _content.setWidget(currentRow, FOOTNOTE_POS_B, msgL);
                _content.getFlexCellFormatter().setAlignment(currentRow, FOOTNOTE_POS_A,
                        HasHorizontalAlignment.ALIGN_LEFT, HasVerticalAlignment.ALIGN_MIDDLE);
            }

            if (partCount > 1 && bgStat.hasAttribute(DownloadScript)) {
                Widget downButton = GwtUtil.makeLinkButton(_prop.makeBase("downloadScript"), new ClickHandler() {
                    public void onClick(ClickEvent event) {
                        showDialogScriptDialog(_monItem.getID(), _monItem.getStatus().getDataSource());
                    }
                });
                _content.getFlexCellFormatter().setAlignment(currentRow, FOOTNOTE_POS_B,
                        HasHorizontalAlignment.ALIGN_LEFT, HasVerticalAlignment.ALIGN_MIDDLE);
                _content.setWidget(currentRow, FOOTNOTE_POS_A, downButton);
                GwtUtil.setStyle(downButton, "paddingTop", paddingTop);
            }
        }

    }

    private void showDialogScriptDialog(String id, String dataSource) {
        DownloadScriptDialog.show(this, id, dataSource);
    }

    private static TablePos getStatusPosCol(MonitorItem monItem, int idx) {
        TablePos retval;
        int size = getPartCount(monItem);
        if (size <= 3) {
            retval = new TablePos(idx, STATUS_POS);
        } else {
            int totalRows = size / 2;
            if (size % 2 == 1)
                totalRows++;

            if (idx < totalRows) {
                retval = new TablePos(idx + 1, STATUS_ALT_POS);
            } else {
                retval = new TablePos((idx - totalRows) + 1, STATUS_POS);
            }
        }
        return retval;
    }

    private Widget makeDetailStateWidget(int idx) {

        Widget retval;
        String desc;

        int partCount = getPartCount(_monItem);
        if (partCount == 0)
            partCount = 1;
        switch (_monItem.getState()) {

        case STARTING:
            desc = (partCount == 1) ? STATUS_STARTING_TXT : STATUS_STARTING_PART_TXT + (idx + 1);
            retval = createWorkingWidget(desc);
            break;
        case WAITING:
            desc = (partCount == 1) ? _waitingMsg : STATUS_WAITING_PART_TXT + (idx + 1);
            retval = createWorkingWidget(desc);
            break;
        case WORKING:
            if (_monItem.getStatus().getPartProgress(idx).isDone()) {
                retval = makeSuccessWidget(idx);
                retval.addStyleName("downloadElement-download");
            } else {
                _detailUI[idx].setProgressDetail(_monItem.getStatus(), idx);
                retval = _detailUI[idx].getProgressDetailWidget();
            }
            break;
        case SUCCESS:
            retval = makeSuccessWidget(idx);
            retval.addStyleName("downloadElement-download");
            break;
        case USER_ABORTED:
        case FAIL:
        case CANCELED:
            retval = new Label(STATUS_CANCELED_TXT);
            retval.addStyleName("downloadElement-warning");
            break;
        default:
            retval = new Label("Unknown: " + _monItem.getState().toString());
            retval.addStyleName("downloadElement-unknown");
            break;
        }

        return retval;

    }

    private Widget makeSuccessWidget(int idx) {
        return ActivationFactory.getInstance().buildActivationUI(_monItem, idx);
    }

    //    private Widget createHeaderWaitingWidget() {
    //        return createWorkingWidget(STATUS_WAITING_TXT);
    //    }

    private Widget createWorkingWidget(String txt) {
        Label l = new Label(txt);
        l.addStyleName("downloadElement-normal");
        l.addStyleName(_ffCss.markedText());
        return l;
    }

    private void abort() {
        if (_monItem.getStatus().isDone()) {
            _content.clear();
            _content.setHeight("3px");
            BackgroundMonitor mon = Application.getInstance().getBackgroundMonitor();
            mon.removeItem(_monItem);
        } else if (!_aborted && !_monItem.isDone()) {
            DeferredCommand.addPause();
            DeferredCommand.addCommand(new Command() {
                public void execute() {
                    confirmAbort();
                }
            });
        }
    }

    private void confirmAbort() {
        PopupUtil.showConfirmMsg(_abortButton, _prop.getTitle("abort.message") + " " + _title,
                _prop.getName("abort.message"), new ClickHandler() {
                    public void onClick(ClickEvent ev) {
                        doAbort();
                    }
                });
    }

    private void doAbort() {
        DeferredCommand.addCommand(new Command() {
            public void execute() {
                _monItem.cancel();
                _aborted = true;
                disableIndicators();
            }
        });
    }

    private void disableIndicators() {
        _abortButton.clear();
        //        Label l= new Label();
        //        l.setWidth("15px");
        //        l.setHeight("15px");
        //        _abortButton.setWidget(l);

        IconCreator ic = IconCreator.Creator.getInstance();
        Image image = new Image(ic.getBlueDelete10x10());
        image.setPixelSize(10, 10);

        _abortButton.setSize("15px", "15px");
        _abortButton.setWidget(image);
        _abortButton.setTitle(DELETE_TIP);

        _workingIcon.setVisible(false);
    }

    private FocusPanel makeAbortButton() {
        FocusPanel fp = new FocusPanel();
        Image image = new Image(GWT.getModuleBaseURL() + "images/stop.gif");
        image.setPixelSize(15, 15);
        fp.setWidget(image);
        fp.addClickHandler(new AbortHandler());
        fp.setTitle(ABORT_TIP);
        return fp;

    }

    // =====================================================================
    // -------------------- Inner Classes --------------------------------
    // =====================================================================

    private class AbortHandler implements ClickHandler {
        public void onClick(ClickEvent sender) {
            abort();
        }
    }

    private static class DetailUIInfo {
        private static final String RETRIEVED_ICON = GWT.getModuleBaseURL() + "images/blue_check-on_10x10.gif";
        private static final String PROCESSING_ICON = GWT.getModuleBaseURL() + "images/blue-downloading-15x15.png";
        private final FlexTable _progressDetail = new FlexTable();
        private final Image _icon = new Image(PROCESSING_ICON);
        private ProgressBar _progressBar = null;
        private final FlowPanel _pbarHolder = new FlowPanel();
        private boolean _firstPbarUpdate = true;

        DetailUIInfo() {
            _progressDetail.addStyleName("downloadElement-progress");
            _pbarHolder.setWidth("200px");
            _progressDetail.setWidget(0, 1, _pbarHolder);
            _progressDetail.getFlexCellFormatter().setColSpan(0, 1, 5);
            _icon.setVisible(false);
        }

        public void setShowRetrivedIcon(FileDownloadStatus status) {
            switch (status) {
            case DONE:
                _icon.setVisible(true);
                _icon.setUrl(RETRIEVED_ICON);
                break;
            case NONE:
                _icon.setVisible(false);
                break;
            case WORKING:
                _icon.setVisible(true);
                _icon.setUrl(PROCESSING_ICON);
                break;
            default:
                assert false;
                break;
            }
        }

        public Widget getDownloadMsgWidget() {
            return _icon;
        }

        void setProgressDetail(BackgroundStatus bgStat, int idx) {

            if (_pbarHolder.isVisible() && _pbarHolder.getAbsoluteLeft() > 0) {
                PackageProgress progress = bgStat.getPartProgress(idx);
                long tot = progress.getTotalByes();
                boolean progressBySize = (tot > 0);
                float percent = progressBySize ? ((float) progress.getProcessedBytes() / (float) tot)
                        : ((float) progress.getProcessedFiles() / (float) progress.getTotalFiles());
                int pInt = (int) (percent * 100);
                if (_firstPbarUpdate) {
                    getProgressBar().setTextFormatter(new PText("Starting background Processing"));
                    getProgressBar().setProgress(percent);
                } else {
                    getProgressBar().setTextFormatter(new PText("Zipped " + pInt + "%" + " of "
                            + (progressBySize ? StringUtils.getSizeAsString(tot, true)
                                    : (progress.getTotalFiles() + " files"))));
                    getProgressBar().setProgress(percent);
                }
            }
            _firstPbarUpdate = false;
        }

        public Widget getProgressDetailWidget() {
            return _progressDetail;
        }

        public ProgressBar getProgressBar() {
            if (_progressBar == null) {
                _progressBar = new ProgressBar(0F, 1F);
                _progressBar.setWidth("100%");
                _pbarHolder.add(_progressBar);

            }
            return _progressBar;
        }
    }

    private static class TablePos {
        final int _row;
        final int _col;

        public TablePos(int row, int col) {
            _row = row;
            _col = col;
        }

        public int getRow() {
            return _row;
        }

        public int getCol() {
            return _col;
        }
    }

    private static class PText extends ProgressBar.TextFormatter {
        private final String _s;

        public PText(String s) {
            _s = s;
        }

        protected String getText(ProgressBar bar, double curProgress) {
            return _s;
        }
    }
}