blob: b26403fbdf25e5bfe7f347fb9595f2b9edb2d4ca [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2015 Tasktop Technologies and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Tasktop Technologies - initial API and implementation
* Frank Becker - improvements
*******************************************************************************/
package org.eclipse.mylyn.tasks.ui.wizards;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.wizard.ProgressMonitorPart;
import org.eclipse.mylyn.commons.ui.CommonUiUtil;
import org.eclipse.mylyn.commons.ui.ProgressContainer;
import org.eclipse.mylyn.commons.workbench.forms.SectionComposite;
import org.eclipse.mylyn.internal.tasks.core.RepositoryQuery;
import org.eclipse.mylyn.internal.tasks.ui.wizards.Messages;
import org.eclipse.mylyn.internal.tasks.ui.wizards.QueryWizardDialog;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
import org.eclipse.mylyn.tasks.core.IRepositoryQuery;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressService;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
/**
* @author Steffen Pingel
* @author Frank Becker
* @since 3.7
*/
public abstract class AbstractRepositoryQueryPage2 extends AbstractRepositoryQueryPage {
private Button cancelButton;
private final AbstractRepositoryConnector connector;
private boolean firstTime = true;
private SectionComposite innerComposite;
/**
* Determines whether a 'Clear Fields' button is shown on the page.
*/
private boolean needsClear;
/**
* Determines whether a 'Refresh' button is shown on the page.
*/
private boolean needsRefresh = true;
private ProgressContainer progressContainer;
private Button refreshButton;
private Text titleText;
private boolean titleWasSuggested;
private boolean editQueryTitleInProgress;
public AbstractRepositoryQueryPage2(String pageName, TaskRepository repository, IRepositoryQuery query) {
super(pageName, repository, query);
this.connector = TasksUi.getRepositoryConnector(getTaskRepository().getConnectorKind());
setTitle(Messages.AbstractRepositoryQueryPage2_Enter_query_parameters);
}
public void createControl(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(composite);
GridLayout layout = new GridLayout(2, false);
if (inSearchContainer()) {
layout.marginWidth = 0;
layout.marginHeight = 0;
}
composite.setLayout(layout);
createTitleGroup(composite);
innerComposite = new SectionComposite(composite, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).span(2, 1).applyTo(innerComposite);
createPageContent(innerComposite);
createButtonGroup(composite);
if (!needsRefresh) {
setDescription(Messages.AbstractRepositoryQueryPage2_Create_a_Query_Page_Description);
}
Dialog.applyDialogFont(composite);
setControl(composite);
}
@Override
public String getQueryTitle() {
return (titleText != null) ? titleText.getText() : null;
}
public boolean handleExtraButtonPressed(int buttonId) {
if (buttonId == QueryWizardDialog.REFRESH_BUTTON_ID) {
if (getTaskRepository() != null) {
refreshConfiguration(true);
} else {
MessageDialog.openInformation(
Display.getCurrent().getActiveShell(),
Messages.AbstractRepositoryQueryPage2_Update_Attributes_Failed,
Messages.AbstractRepositoryQueryPage2_No_repository_available_please_add_one_using_the_Task_Repositories_view);
}
return true;
} else if (buttonId == QueryWizardDialog.CLEAR_BUTTON_ID) {
doClearControls();
return true;
}
return false;
}
@Override
public boolean isPageComplete() {
if (titleText != null && titleText.getText().length() > 0) {
return true;
}
setMessage(Messages.AbstractRepositoryQueryPage2_Enter_a_title);
return false;
}
/**
* Allows connectors to suggest a query title. As long as the title field does not contain edits made by the user,
* the field will be updated with the suggestion whenever the button enablement is updated.
*
* @return a query title suggested based on the query parameters, or the empty string
* @since 3.18
*/
@NonNull
protected String suggestQueryTitle() {
return ""; //$NON-NLS-1$
}
/**
* Called by the framework to update the title from the suggestion.
*/
void updateTitleFromSuggestion() {
if (editQueryTitleInProgress) {
titleWasSuggested = false;
} else if (titleWasSuggested
|| Strings.isNullOrEmpty(getQueryTitle())
|| (getQuery() != null && Objects.equal(getQuery().getSummary(), getQueryTitle()) && suggestQueryTitle().equals(
getQueryTitle()))) {
setQueryTitle(suggestQueryTitle());
titleWasSuggested = true;
}
}
public boolean needsClear() {
return needsClear;
}
public boolean needsRefresh() {
return needsRefresh;
}
@Override
public boolean performSearch() {
if (inSearchContainer()) {
saveState();
}
return super.performSearch();
}
@Override
public void saveState() {
if (inSearchContainer()) {
RepositoryQuery query = new RepositoryQuery(getTaskRepository().getConnectorKind(), "handle"); //$NON-NLS-1$
applyTo(query);
IDialogSettings settings = getDialogSettings();
if (settings != null) {
settings.put(getSavedStateSettingKey(), query.getUrl());
}
}
}
public void setExtraButtonState(Button button) {
Integer obj = (Integer) button.getData();
if (obj == QueryWizardDialog.REFRESH_BUTTON_ID) {
if (needsRefresh) {
if (!button.isVisible()) {
button.setVisible(true);
}
button.setEnabled(true);
} else {
if (button != null && button.isVisible()) {
button.setVisible(false);
}
}
} else if (obj == QueryWizardDialog.CLEAR_BUTTON_ID) {
if (!button.isVisible()) {
button.setVisible(true);
}
button.setEnabled(true);
}
}
public void setNeedsClear(boolean needsClearButton) {
this.needsClear = needsClearButton;
}
public void setNeedsRefresh(boolean needsRefresh) {
this.needsRefresh = needsRefresh;
}
public void setQueryTitle(String text) {
if (titleText != null) {
titleText.setText(text);
}
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
if (getSearchContainer() != null) {
getSearchContainer().setPerformActionEnabled(true);
}
if (visible && firstTime) {
firstTime = false;
if (!hasRepositoryConfiguration() && needsRefresh) {
// delay the execution so the dialog's progress bar is visible
// when the attributes are updated
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (getControl() != null && !getControl().isDisposed()) {
initializePage();
}
}
});
} else {
// no remote connection is needed to get attributes therefore do
// not use delayed execution to avoid flickering
initializePage();
}
}
}
private void createButtonGroup(Composite parent) {
Composite buttonComposite = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(1, false);
layout.marginWidth = 0;
layout.marginHeight = 0;
buttonComposite.setLayout(layout);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP).grab(true, false).span(2, 1).applyTo(buttonComposite);
createButtons(buttonComposite);
if (buttonComposite.getChildren().length > 0) {
layout.numColumns = buttonComposite.getChildren().length;
} else {
// remove composite to avoid spacing
buttonComposite.dispose();
}
}
private void createTitleGroup(Composite control) {
if (inSearchContainer()) {
return;
}
Label titleLabel = new Label(control, SWT.NONE);
titleLabel.setText(Messages.AbstractRepositoryQueryPage2__Title_);
titleText = new Text(control, SWT.BORDER);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP).grab(true, false).applyTo(titleText);
titleText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
try {
editQueryTitleInProgress = true;
getContainer().updateButtons();
} finally {
editQueryTitleInProgress = false;
}
}
});
}
private void initializePage() {
if (needsRefresh) {
boolean refreshed = refreshConfiguration(false);
if (!refreshed) {
// always do a refresh when page is initially shown
if (!innerComposite.isDisposed()) {
doRefreshControls();
}
}
}
boolean restored = false;
if (getQuery() != null) {
titleText.setText(getQuery().getSummary());
restored |= restoreState(getQuery());
} else if (inSearchContainer()) {
restored |= restoreSavedState();
}
if (!restored) {
// initialize with default values
if (!innerComposite.isDisposed()) {
doClearControls();
}
}
}
protected boolean refreshConfiguration(final boolean force) {
if (force || !hasRepositoryConfiguration()) {
setErrorMessage(null);
try {
doRefreshConfiguration();
if (!innerComposite.isDisposed()) {
doRefreshControls();
}
return true;
} catch (InvocationTargetException e) {
if (e.getCause() instanceof CoreException) {
setErrorMessage(((CoreException) e.getCause()).getStatus().getMessage());
} else {
setErrorMessage(e.getCause().getMessage());
}
} catch (InterruptedException e) {
// canceled
}
}
return false;
}
private void doRefreshConfiguration() throws InvocationTargetException, InterruptedException {
IRunnableWithProgress runnable = new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
monitor = SubMonitor.convert(monitor);
monitor.beginTask(Messages.AbstractRepositoryQueryPage2_Refresh_Configuration_Button_Label,
IProgressMonitor.UNKNOWN);
try {
connector.updateRepositoryConfiguration(getTaskRepository(), monitor);
} catch (CoreException e) {
throw new InvocationTargetException(e);
} catch (OperationCanceledException e) {
throw new InterruptedException();
} finally {
monitor.done();
}
}
};
if (getContainer() != null) {
getContainer().run(true, true, runnable);
} else if (progressContainer != null) {
progressContainer.run(true, true, runnable);
} else if (getSearchContainer() != null) {
getSearchContainer().getRunnableContext().run(true, true, runnable);
} else {
IProgressService service = PlatformUI.getWorkbench().getProgressService();
service.busyCursorWhile(runnable);
}
}
protected void createButtons(final Composite composite) {
if (getContainer() instanceof QueryWizardDialog) {
// refresh and clear buttons are provided by the dialog
return;
}
if (needsRefresh) {
refreshButton = new Button(composite, SWT.PUSH);
refreshButton.setText(Messages.AbstractRepositoryQueryPage2__Refresh_From_Repository);
refreshButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (getTaskRepository() != null) {
refreshConfiguration(true);
} else {
MessageDialog.openInformation(
Display.getCurrent().getActiveShell(),
Messages.AbstractRepositoryQueryPage2_Update_Attributes_Failed,
Messages.AbstractRepositoryQueryPage2_No_repository_available_please_add_one_using_the_Task_Repositories_view);
}
}
});
}
if (needsClear) {
Button clearButton = new Button(composite, SWT.PUSH);
clearButton.setText(Messages.AbstractRepositoryQueryPage2_Clear_Fields);
clearButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
doClearControls();
}
});
}
if (getContainer() == null) {
final ProgressMonitorPart progressMonitorPart = new ProgressMonitorPart(composite, null);
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.BEGINNING)
.grab(true, false)
.applyTo(progressMonitorPart);
progressMonitorPart.setVisible(false);
progressContainer = new ProgressContainer(composite.getShell(), progressMonitorPart) {
@Override
protected void restoreUiState(java.util.Map<Object, Object> state) {
cancelButton.setVisible(false);
CommonUiUtil.setEnabled(innerComposite, true);
for (Control control : composite.getChildren()) {
if (control instanceof ProgressMonitorPart) {
break;
}
control.setEnabled(true);
}
}
@Override
protected void saveUiState(java.util.Map<Object, Object> savedState) {
CommonUiUtil.setEnabled(innerComposite, false);
for (Control control : composite.getChildren()) {
if (control instanceof ProgressMonitorPart) {
break;
}
control.setEnabled(false);
}
cancelButton.setEnabled(true);
cancelButton.setVisible(true);
}
};
cancelButton = new Button(composite, SWT.PUSH);
cancelButton.setText(IDialogConstants.CANCEL_LABEL);
cancelButton.setVisible(false);
progressContainer.setCancelButton(cancelButton);
}
}
protected abstract void createPageContent(@NonNull SectionComposite parent);
protected void doClearControls() {
}
protected abstract void doRefreshControls();
protected AbstractRepositoryConnector getConnector() {
return connector;
}
protected String getSavedStateSettingKey() {
return getName() + "." + getTaskRepository().getRepositoryUrl(); //$NON-NLS-1$
}
protected abstract boolean hasRepositoryConfiguration();
protected boolean restoreSavedState() {
IDialogSettings settings = getDialogSettings();
if (settings != null) {
String queryUrl = settings.get(getSavedStateSettingKey());
if (queryUrl != null) {
RepositoryQuery query = new RepositoryQuery(getTaskRepository().getConnectorKind(), "handle"); //$NON-NLS-1$
query.setUrl(queryUrl);
return restoreState(query);
}
}
return false;
}
protected abstract boolean restoreState(@NonNull IRepositoryQuery query);
/**
* Reflows the page and resizes the shell as necessary. Clients should invoke this if the content of the page is
* changed dynamically.
*
* @see SectionComposite#resizeAndReflow()
* @since 3.10
*/
public void reflow() {
innerComposite.resizeAndReflow();
}
}