/* * DBeaver - Universal Database Manager * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jkiss.dbeaver.ui.controls; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ControlEditor; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBIcon; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.ProxyProgressMonitor; import org.jkiss.dbeaver.model.runtime.load.ILoadService; import org.jkiss.dbeaver.model.runtime.load.ILoadVisualizer; import org.jkiss.dbeaver.ui.DBeaverIcons; import org.jkiss.dbeaver.ui.UIIcon; import org.jkiss.utils.CommonUtils; import java.lang.reflect.InvocationTargetException; /** * PairListControl */ public class ProgressLoaderVisualizer<RESULT> implements ILoadVisualizer<RESULT> { private static final Log log = Log.getLog(ProgressLoaderVisualizer.class); protected static final int PROGRESS_VISUALIZE_PERIOD = 100; static final DBIcon[] PROGRESS_IMAGES = { UIIcon.PROGRESS0, UIIcon.PROGRESS1, UIIcon.PROGRESS2, UIIcon.PROGRESS3, UIIcon.PROGRESS4, UIIcon.PROGRESS5, UIIcon.PROGRESS6, UIIcon.PROGRESS7, UIIcon.PROGRESS8, UIIcon.PROGRESS9 }; private final ILoadService<RESULT> loadService; private final Composite progressPane; private volatile boolean finished = false; private ControlEditor progressOverlay; private volatile int drawCount = 0; private Button cancelButton; private PaintListener painListener; private Color shadowColor; private String progressMessage; private long loadStartTime; public ProgressLoaderVisualizer(ILoadService<RESULT> loadService, Composite progressPane) { this.loadService = loadService; this.progressPane = progressPane; this.progressMessage = "Initializing"; this.loadStartTime = System.currentTimeMillis(); } @Override public DBRProgressMonitor overwriteMonitor(DBRProgressMonitor monitor) { DBRProgressMonitor progressMonitor = new ProxyProgressMonitor(monitor) { @Override public void subTask(String name) { progressMessage = name; super.subTask(name); } }; return progressMonitor; } @Override public boolean isCompleted() { return finished; } @Override public void completeLoading(RESULT result) { this.finished = true; } @Override public void visualizeLoading() { if (!progressPane.isDisposed()) { if (shadowColor == null) { shadowColor = progressPane.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW); } if (!finished) { try { showProgress(); } catch (Exception e) { log.error("Internal error during progress visualization", e); // Something went terribly wrong // We shouldn't be here ever. In any case we must finish the job finishProgress(); } } if (finished) { finishProgress(); } } } private void showProgress() { if (progressOverlay == null) { // Start progress visualization cancelButton = new Button(progressPane, SWT.PUSH); cancelButton.setText("Cancel"); GridData gd = new GridData(GridData.FILL_BOTH); gd.verticalIndent = DBeaverIcons.getImage(UIIcon.PROGRESS0).getBounds().height * 2; cancelButton.setLayoutData(gd); cancelButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { cancelButton.setText("Canceled"); cancelButton.setEnabled(false); Point buttonSize = cancelButton.computeSize(SWT.DEFAULT, SWT.DEFAULT); progressOverlay.minimumWidth = buttonSize.x; progressOverlay.minimumHeight = buttonSize.y; progressOverlay.layout(); try { loadService.cancel(); } catch (InvocationTargetException e1) { log.error(e1.getTargetException()); } } }); painListener = new PaintListener() { @Override public void paintControl(PaintEvent e) { Image image = DBeaverIcons.getImage(PROGRESS_IMAGES[drawCount % PROGRESS_IMAGES.length]); Rectangle buttonBounds = cancelButton.getBounds(); Rectangle imageBounds = image.getBounds(); e.gc.drawImage( image, (buttonBounds.x + buttonBounds.width / 2) - imageBounds.width / 2, buttonBounds.y - imageBounds.height - 5); long elapsedTime = System.currentTimeMillis() - loadStartTime; String elapsedString = elapsedTime > 10000 ? String.valueOf(elapsedTime / 1000) : String.valueOf(((double) (elapsedTime / 100)) / 10); String statusMessage = CommonUtils.truncateString( progressMessage.replaceAll("\\s", " "), 64); String status = statusMessage + " - " + elapsedString + "s"; Point statusSize = e.gc.textExtent(status); int statusX = (buttonBounds.x + buttonBounds.width / 2) - statusSize.x / 2; int statusY = buttonBounds.y - imageBounds.height - 10 - statusSize.y; e.gc.setForeground(progressPane.getForeground()); e.gc.setBackground(progressPane.getBackground()); e.gc.fillRectangle(statusX - 2, statusY - 2, statusSize.x + 4, statusSize.y + 4); e.gc.drawText(status, statusX, statusY, true); e.gc.setForeground(shadowColor); e.gc.drawRoundRectangle(statusX - 3, statusY - 3, statusSize.x + 5, statusSize.y + 5, 5, 5); } }; progressPane.addPaintListener(painListener); progressOverlay = new ControlEditor(progressPane); Point buttonSize = cancelButton.computeSize(SWT.DEFAULT, SWT.DEFAULT); progressOverlay.minimumWidth = buttonSize.x; progressOverlay.minimumHeight = buttonSize.y; progressOverlay.setEditor(cancelButton); } drawCount++; progressOverlay.layout(); progressPane.redraw(); } private void finishProgress() { // Last update - remove progress visualization if (progressOverlay != null) { if (!progressPane.isDisposed()) { progressPane.removePaintListener(painListener); progressOverlay.dispose(); } progressOverlay = null; if (!cancelButton.isDisposed()) { cancelButton.dispose(); } progressPane.redraw(); } } }