| /* |
| * Copyright (c) 2010-2018 BSI Business Systems Integration AG. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * BSI Business Systems Integration AG - initial API and implementation |
| */ |
| package org.eclipse.scout.rt.client.ui.messagebox; |
| |
| import java.beans.PropertyChangeListener; |
| import java.util.List; |
| import java.util.concurrent.TimeUnit; |
| |
| import javax.annotation.PostConstruct; |
| |
| import org.eclipse.scout.rt.client.ModelContextProxy; |
| import org.eclipse.scout.rt.client.ModelContextProxy.ModelContext; |
| import org.eclipse.scout.rt.client.context.ClientRunContexts; |
| import org.eclipse.scout.rt.client.job.ModelJobs; |
| import org.eclipse.scout.rt.client.session.ClientSessionProvider; |
| import org.eclipse.scout.rt.client.ui.AbstractWidget; |
| import org.eclipse.scout.rt.client.ui.IDisplayParent; |
| import org.eclipse.scout.rt.client.ui.desktop.IDesktop; |
| import org.eclipse.scout.rt.client.ui.form.DisplayParentResolver; |
| import org.eclipse.scout.rt.platform.BEANS; |
| import org.eclipse.scout.rt.platform.Bean; |
| import org.eclipse.scout.rt.platform.classid.ClassId; |
| import org.eclipse.scout.rt.platform.html.HtmlHelper; |
| import org.eclipse.scout.rt.platform.html.IHtmlContent; |
| import org.eclipse.scout.rt.platform.job.IBlockingCondition; |
| import org.eclipse.scout.rt.platform.job.IFuture; |
| import org.eclipse.scout.rt.platform.job.Jobs; |
| import org.eclipse.scout.rt.platform.util.Assertions; |
| import org.eclipse.scout.rt.platform.util.CollectionUtility; |
| import org.eclipse.scout.rt.platform.util.ImmutablePair; |
| import org.eclipse.scout.rt.platform.util.Pair; |
| import org.eclipse.scout.rt.platform.util.StringUtility; |
| import org.eclipse.scout.rt.platform.util.event.FastListenerList; |
| import org.eclipse.scout.rt.platform.util.event.IFastListenerList; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Implementation of message box.<br/> |
| * Use {@link MessageBoxes} to create a message box. |
| */ |
| @Bean |
| @ClassId("b22a87dc-b40c-4c4f-96cc-356aef66e508") |
| public class MessageBox extends AbstractWidget implements IMessageBox { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(MessageBox.class); |
| |
| private final FastListenerList<MessageBoxListener> m_listenerList = new FastListenerList<>(); |
| private final IMessageBoxUIFacade m_uiFacade = BEANS.get(ModelContextProxy.class).newProxy(new P_UIFacade(), ModelContext.copyCurrent()); |
| |
| private IDisplayParent m_displayParent; |
| |
| private long m_autoCloseMillis = -1; |
| |
| private String m_iconId; |
| |
| private String m_header; |
| private String m_body; |
| private IHtmlContent m_html; |
| |
| private String m_yesButtonText; |
| private String m_noButtonText; |
| private String m_cancelButtonText; |
| |
| private String m_hiddenText; |
| private String m_copyPasteText; |
| // cached |
| private String m_copyPasteTextInternal; |
| // modality |
| private final IBlockingCondition m_blockingCondition = Jobs.newBlockingCondition(false); |
| // result |
| private int m_answer; |
| private boolean m_answerSet; |
| private int m_severity; |
| |
| @Override |
| @PostConstruct |
| protected void initConfig() { |
| super.initConfig(); |
| m_displayParent = BEANS.get(DisplayParentResolver.class).resolve(this); |
| } |
| |
| @Override |
| public IDisplayParent getDisplayParent() { |
| return m_displayParent; |
| } |
| |
| @Override |
| public IMessageBox withDisplayParent(IDisplayParent displayParent) { |
| Assertions.assertFalse(ClientSessionProvider.currentSession().getDesktop().isShowing(this), "Property 'displayParent' cannot be changed because message box is already showing [messageBox={}]", this); |
| |
| if (displayParent == null) { |
| displayParent = BEANS.get(DisplayParentResolver.class).resolve(this); |
| } |
| |
| m_displayParent = Assertions.assertNotNull(displayParent, "'displayParent' must not be null"); |
| |
| return this; |
| } |
| |
| @Override |
| public void addPropertyChangeListener(PropertyChangeListener listener) { |
| propertySupport.addPropertyChangeListener(listener); |
| } |
| |
| @Override |
| public void removePropertyChangeListener(PropertyChangeListener listener) { |
| propertySupport.removePropertyChangeListener(listener); |
| } |
| |
| @Override |
| public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { |
| propertySupport.addPropertyChangeListener(propertyName, listener); |
| } |
| |
| @Override |
| public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { |
| propertySupport.removePropertyChangeListener(propertyName, listener); |
| } |
| |
| @Override |
| public String getHeader() { |
| return m_header; |
| } |
| |
| @Override |
| public MessageBox withHeader(String header) { |
| m_header = header; |
| m_copyPasteTextInternal = null; |
| return this; |
| } |
| |
| @Override |
| public String getBody() { |
| return m_body; |
| } |
| |
| @Override |
| public MessageBox withBody(String body) { |
| m_body = body; |
| m_copyPasteTextInternal = null; |
| return this; |
| } |
| |
| @Override |
| public IHtmlContent getHtml() { |
| return m_html; |
| } |
| |
| @Override |
| public MessageBox withHtml(IHtmlContent html) { |
| m_html = html; |
| m_copyPasteTextInternal = null; |
| return this; |
| } |
| |
| @Override |
| public String getHiddenText() { |
| return m_hiddenText; |
| } |
| |
| @Override |
| public MessageBox withHiddenText(String hiddenText) { |
| m_hiddenText = hiddenText; |
| m_copyPasteTextInternal = null; |
| return this; |
| } |
| |
| @Override |
| public String getYesButtonText() { |
| return m_yesButtonText; |
| } |
| |
| @Override |
| public MessageBox withYesButtonText(String yesButtonText) { |
| m_yesButtonText = yesButtonText; |
| return this; |
| } |
| |
| @Override |
| public String getNoButtonText() { |
| return m_noButtonText; |
| } |
| |
| @Override |
| public MessageBox withNoButtonText(String noButtonText) { |
| m_noButtonText = noButtonText; |
| return this; |
| } |
| |
| @Override |
| public String getCancelButtonText() { |
| return m_cancelButtonText; |
| } |
| |
| @Override |
| public MessageBox withCancelButtonText(String cancelButtonText) { |
| m_cancelButtonText = cancelButtonText; |
| return this; |
| } |
| |
| @Override |
| public String getIconId() { |
| return m_iconId; |
| } |
| |
| @Override |
| public MessageBox withIconId(String iconId) { |
| m_iconId = iconId; |
| return this; |
| } |
| |
| @Override |
| public int getSeverity() { |
| return m_severity; |
| } |
| |
| @Override |
| public MessageBox withSeverity(int severity) { |
| m_severity = severity; |
| return this; |
| } |
| |
| @Override |
| public long getAutoCloseMillis() { |
| return m_autoCloseMillis; |
| } |
| |
| @Override |
| public MessageBox withAutoCloseMillis(long autoCloseMillis) { |
| m_autoCloseMillis = autoCloseMillis; |
| return this; |
| } |
| |
| @Override |
| public String getCopyPasteText() { |
| if (m_copyPasteText == null) { |
| updateCopyPasteTextInternal(); |
| return m_copyPasteTextInternal; |
| } |
| else { |
| return m_copyPasteText; |
| } |
| } |
| |
| @Override |
| public MessageBox withCopyPasteText(String copyPasteText) { |
| m_copyPasteText = copyPasteText; |
| return this; |
| } |
| |
| protected void updateCopyPasteTextInternal() { |
| if (m_copyPasteTextInternal == null) { |
| m_copyPasteTextInternal = StringUtility.join("\n\n", |
| m_header, |
| m_body, |
| m_html == null ? null : BEANS.get(HtmlHelper.class).toPlainText(m_html.toHtml()), |
| m_hiddenText); |
| } |
| } |
| |
| @Override |
| public IFastListenerList<MessageBoxListener> messageBoxListeners() { |
| return m_listenerList; |
| } |
| |
| protected void fireClosed() { |
| fireMessageBoxEvent(new MessageBoxEvent(this, MessageBoxEvent.TYPE_CLOSED)); |
| } |
| |
| protected void fireMessageBoxEvent(MessageBoxEvent e) { |
| messageBoxListeners().list().forEach(listener -> listener.messageBoxChanged(e)); |
| } |
| |
| @Override |
| public IMessageBoxUIFacade getUIFacade() { |
| return m_uiFacade; |
| } |
| |
| @Override |
| public boolean isOpen() { |
| return m_blockingCondition.isBlocking(); |
| } |
| |
| /** |
| * Displays the message box and waits for a response. |
| * <p> |
| * If {@link #getAutoCloseMillis()} is set, the message box will return with {@link IMessageBox#CANCEL_OPTION} after |
| * the specific time. |
| */ |
| @Override |
| public int show() { |
| return show(CANCEL_OPTION); |
| } |
| |
| /** |
| * Displays the message box and waits for a response. |
| * <p> |
| * If {@link #getAutoCloseMillis()} is set, the message box will return with given response after the specific time. |
| */ |
| @Override |
| public int show(int defaultResult) { |
| m_answerSet = false; |
| m_answer = defaultResult; |
| |
| if (ClientSessionProvider.currentSession() == null) { |
| LOG.warn("outside ScoutSessionThread, default answer is CANCEL", new IllegalStateException()); |
| m_answerSet = true; |
| m_answer = CANCEL_OPTION; |
| return m_answer; |
| } |
| |
| m_blockingCondition.setBlocking(true); |
| IDesktop desktop = ClientSessionProvider.currentSession().getDesktop(); |
| try { |
| // check if the desktop is observing this process |
| if (desktop == null || !desktop.isOpened()) { |
| LOG.warn("there is no desktop or the desktop has not yet been opened in the ui, default answer is CANCEL", new IllegalStateException()); |
| m_answerSet = true; |
| m_answer = CANCEL_OPTION; |
| } |
| else { |
| // request a gui |
| desktop.showMessageBox(this); |
| // attach auto-cancel timer |
| IFuture<Void> autoCloseFuture = null; |
| if (getAutoCloseMillis() > 0) { |
| final long closeDelay = getAutoCloseMillis(); |
| autoCloseFuture = Jobs.schedule(this::closeMessageBox, Jobs.newInput() |
| .withName("Closing message box") |
| .withRunContext(ClientRunContexts.copyCurrent()) |
| .withExecutionTrigger(Jobs.newExecutionTrigger() |
| .withStartIn(closeDelay, TimeUnit.MILLISECONDS))); |
| } |
| // start sub event dispatch thread |
| waitFor(); |
| |
| if (autoCloseFuture != null && !autoCloseFuture.isDone()) { |
| autoCloseFuture.cancel(true); |
| } |
| } |
| } |
| finally { |
| if (desktop != null) { |
| desktop.hideMessageBox(this); |
| } |
| fireClosed(); |
| } |
| return m_answer; |
| } |
| |
| protected void waitFor() { |
| // Do not exit upon ui cancel request, as the file chooser would be closed immediately otherwise. |
| m_blockingCondition.waitFor(ModelJobs.EXECUTION_HINT_UI_INTERACTION_REQUIRED); |
| } |
| |
| protected void closeMessageBox() { |
| m_blockingCondition.setBlocking(false); |
| } |
| |
| @Override |
| public void doOk() { |
| closeInternal(CollectionUtility.arrayList( |
| new ImmutablePair<>(m_yesButtonText, YES_OPTION), |
| new ImmutablePair<>(m_noButtonText, NO_OPTION), |
| new ImmutablePair<>(m_cancelButtonText, CANCEL_OPTION))); |
| } |
| |
| @Override |
| public void doClose() { |
| closeInternal(CollectionUtility.arrayList( |
| new ImmutablePair<>(m_cancelButtonText, CANCEL_OPTION), |
| new ImmutablePair<>(m_noButtonText, NO_OPTION), |
| new ImmutablePair<>(m_yesButtonText, YES_OPTION))); |
| } |
| |
| private void closeInternal(List<Pair<String, Integer>> options) { |
| if (!m_answerSet) { |
| m_answerSet = true; |
| options.stream() |
| .filter(option -> StringUtility.hasText(option.getLeft())) |
| .findFirst() |
| .ifPresent(option -> m_answer = option.getRight()); |
| } |
| closeMessageBox(); |
| } |
| |
| protected class P_UIFacade implements IMessageBoxUIFacade { |
| |
| @Override |
| public void setResultFromUI(int option) { |
| switch (option) { |
| case YES_OPTION: |
| case NO_OPTION: |
| case CANCEL_OPTION: { |
| if (!m_answerSet) { |
| m_answerSet = true; |
| m_answer = option; |
| } |
| closeMessageBox(); |
| break; |
| } |
| } |
| } |
| } |
| } |