/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Copyright (c) 2013, MPL CodeInside http://codeinside.ru
*/
package org.vaadin.dialogs;
import java.lang.reflect.Method;
import java.text.NumberFormat;
import java.util.Locale;
import org.vaadin.dialogs.ConfirmDialog.Factory;
import com.vaadin.event.ShortcutAction.KeyCode;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Component;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.Window.CloseEvent;
import com.vaadin.ui.themes.Reindeer;
public class DefaultConfirmDialogFactory implements Factory {
/** Generated serial UID. */
private static final long serialVersionUID = -5412321247707480466L;
// System wide defaults
protected static final String DEFAULT_CAPTION = "Confirm";
protected static final String DEFAULT_MESSAGE = "Are You sure?";
protected static final String DEFAULT_OK_CAPTION = "Ok";
protected static final String DEFAULT_CANCEL_CAPTION = "Cancel";
// System wide defaults
private static final double MIN_WIDTH = 20d;
private static final double MAX_WIDTH = 40d;
private static final double MIN_HEIGHT = 1d;
private static final double MAX_HEIGHT = 30d;
private static final double BUTTON_HEIGHT = 2.5;
public ConfirmDialog create(final String caption, final String message,
final String okCaption, final String cancelCaption) {
// Create a confirm dialog
final ConfirmDialog confirm = new ConfirmDialog();
confirm.setCaption(caption != null ? caption : DEFAULT_CAPTION);
// Close listener implementation
confirm.addListener(new Window.CloseListener() {
private static final long serialVersionUID = 1971800928047045825L;
public void windowClose(CloseEvent ce) {
// Only process if still enabled
if (confirm.isEnabled()) {
confirm.setEnabled(false); // avoid double processing
confirm.setConfirmed(false);
if (confirm.getListener() != null) {
confirm.getListener().onClose(confirm);
}
}
}
});
// Create content
VerticalLayout c = (VerticalLayout) confirm.getContent();
c.setSizeFull();
c.setSpacing(true);
// Panel for scrolling lengthty messages.
Panel scroll = new Panel(new VerticalLayout());
scroll.setScrollable(true);
c.addComponent(scroll);
scroll.setWidth("100%");
scroll.setHeight("100%");
scroll.setStyleName(Reindeer.PANEL_LIGHT);
c.setExpandRatio(scroll, 1f);
// Always HTML, but escape
Label text = new Label("", Label.CONTENT_RAW);
scroll.addComponent(text);
confirm.setMessageLabel(text);
confirm.setMessage(message);
HorizontalLayout buttons = new HorizontalLayout();
c.addComponent(buttons);
buttons.setSpacing(true);
buttons.setHeight(format(BUTTON_HEIGHT) + "em");
buttons.setWidth("100%");
Label spacerLeft = new Label("");
buttons.addComponent(spacerLeft);
spacerLeft.setWidth("100%");
buttons.setExpandRatio(spacerLeft, 1f);
final Button cancel = new Button(cancelCaption != null ? cancelCaption
: DEFAULT_CANCEL_CAPTION);
cancel.setData(false);
cancel.setClickShortcut(KeyCode.ESCAPE, null);
buttons.addComponent(cancel);
confirm.setCancelButton(cancel);
final Button ok = new Button(okCaption != null ? okCaption
: DEFAULT_OK_CAPTION);
ok.setData(true);
ok.setClickShortcut(KeyCode.ENTER, null);
ok.focus();
buttons.addComponent(ok);
confirm.setOkButton(ok);
Label spacerRight = new Label("");
buttons.addComponent(spacerRight);
spacerRight.setWidth("100%");
buttons.setExpandRatio(spacerRight, 1f);
// Create a listener for buttons
Button.ClickListener cb = new Button.ClickListener() {
private static final long serialVersionUID = 3525060915814334881L;
public void buttonClick(ClickEvent event) {
// Copy the button date to window for passing through either
// "OK" or "CANCEL". Only process id still enabled.
if (confirm.isEnabled()) {
confirm.setEnabled(false); // Avoid double processing
confirm.setConfirmed(event.getButton() == ok);
// We need to cast this way, because of the backward
// compatibility issue in 6.4 series.
Component parent = confirm.getParent();
if (parent instanceof Window) {
try {
Method m = Window.class.getDeclaredMethod(
"removeWindow", Window.class);
m.invoke(parent, confirm);
} catch (Exception e) {
throw new RuntimeException("Failed to remove confirmation dialog from the parent window.", e);
}
}
// This has to be invoked as the window.close
// event is not fired when removed.
if (confirm.getListener() != null) {
confirm.getListener().onClose(confirm);
}
}
}
};
cancel.addListener(cb);
ok.addListener(cb);
// Approximate the size of the dialog
double[] dim = getDialogDimensions(message,
ConfirmDialog.CONTENT_TEXT_WITH_NEWLINES);
confirm.setWidth(format(dim[0]) + "em");
confirm.setHeight(format(dim[1]) + "em");
confirm.setResizable(false);
return confirm;
}
/**
* Approximates the dialog dimensions based on its message length.
*
* @param message
* Message string
* @return
*/
protected double[] getDialogDimensions(String message, int style) {
// Based on Reindeer style:
double chrW = 0.5d;
double chrH = 1.5d;
double length = chrW * message.length();
double rows = Math.ceil(length / MAX_WIDTH);
// Estimate extra lines
if (style == ConfirmDialog.CONTENT_TEXT_WITH_NEWLINES) {
rows += count("\n", message);
}
// System.out.println(message.length() + " = " + length + "em");
// System.out.println("Rows: " + (length / MAX_WIDTH) + " = " + rows);
// Obey maximum size
double width = Math.min(MAX_WIDTH, length);
double height = Math.ceil(Math.min(MAX_HEIGHT, rows * chrH));
// Obey the minimum size
width = Math.max(width, MIN_WIDTH);
height = Math.max(height, MIN_HEIGHT);
// Based on Reindeer style:
double btnHeight = 2.5d;
double vmargin = 8d;
double hmargin = 2d;
double[] res = new double[] { width + hmargin,
height + btnHeight + vmargin };
// System.out.println(res[0] + "," + res[1]);
return res;
}
/**
* Count the number of needles within a haystack.
*
* @param needle
* The string to search for.
* @param haystack
* The string to process.
* @return
*/
private static int count(final String needle, final String haystack) {
int count = 0;
int pos = -1;
while ((pos = haystack.indexOf(needle, pos + 1)) >= 0) {
count++;
}
return count;
}
/**
* Format a double single fraction digit.
*
* @param n
* @return
*/
private String format(double n) {
NumberFormat nf = NumberFormat.getNumberInstance(Locale.ENGLISH);
nf.setMaximumFractionDigits(1);
nf.setGroupingUsed(false);
return nf.format(n);
}
}