blob: 9851ee8840c23995fd7bbae6ac426e86232de5e8 [file] [log] [blame]
/*
* 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;
}
}
}
}
}