| /******************************************************************************* |
| * 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(); |
| } |
| |
| } |