org.sigmah.client.ui.view.reports.ReportsView.java Source code

Java tutorial

Introduction

Here is the source code for org.sigmah.client.ui.view.reports.ReportsView.java

Source

package org.sigmah.client.ui.view.reports;

/*
 * #%L
 * Sigmah
 * %%
 * Copyright (C) 2010 - 2016 URD
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */

import java.util.Arrays;
import java.util.Date;

import org.sigmah.client.i18n.I18N;
import org.sigmah.client.ui.presenter.reports.ReportActionsHandler;
import org.sigmah.client.ui.presenter.reports.ReportsPresenter;
import org.sigmah.client.ui.presenter.reports.ReportsPresenter.DocumentNameColumnActionHandler;
import org.sigmah.client.ui.res.icon.IconImageBundle;
import org.sigmah.client.ui.res.icon.reports.ToolbarImages;
import org.sigmah.client.ui.view.base.AbstractView;
import org.sigmah.client.ui.widget.button.Button;
import org.sigmah.client.ui.widget.form.Forms;
import org.sigmah.client.ui.widget.layout.Layouts;
import org.sigmah.client.ui.widget.layout.Layouts.Margin;
import org.sigmah.client.ui.widget.panel.FoldPanel;
import org.sigmah.client.ui.widget.panel.Panels;
import org.sigmah.client.util.ClientUtils;
import org.sigmah.client.util.DateUtils;
import org.sigmah.shared.dto.report.KeyQuestionDTO;
import org.sigmah.shared.dto.report.ProjectReportDTO;
import org.sigmah.shared.dto.report.ReportReference;
import org.sigmah.shared.dto.report.RichTextElementDTO;

import com.extjs.gxt.ui.client.Style.HorizontalAlignment;
import com.extjs.gxt.ui.client.Style.LayoutRegion;
import com.extjs.gxt.ui.client.Style.Scroll;
import com.extjs.gxt.ui.client.event.ButtonEvent;
import com.extjs.gxt.ui.client.event.IconButtonEvent;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.Component;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.Dialog;
import com.extjs.gxt.ui.client.widget.Label;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.button.ToolButton;
import com.extjs.gxt.ui.client.widget.form.TextField;
import com.extjs.gxt.ui.client.widget.grid.ColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnData;
import com.extjs.gxt.ui.client.widget.grid.ColumnModel;
import com.extjs.gxt.ui.client.widget.grid.Grid;
import com.extjs.gxt.ui.client.widget.grid.GridCellRenderer;
import com.extjs.gxt.ui.client.widget.layout.BorderLayoutData;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.extjs.gxt.ui.client.widget.layout.FormLayout;
import com.extjs.gxt.ui.client.widget.toolbar.SeparatorToolItem;
import com.extjs.gxt.ui.client.widget.toolbar.ToolBar;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.RichTextArea;

/**
 * Reports and Documents widget view.
 * 
 * @author Denis Colliot (dcolliot@ideia.fr) (v2.0)
 */
public class ReportsView extends AbstractView implements ReportsPresenter.View {

    // CSS style names.
    private static final String STYLE_TOOL_CLOSE_ICON = "x-tool-close";
    private static final String STYLE_PROJECT_REPORT_DRAFT = "project-report-draft";
    private static final String STYLE_PROJECT_REPORT_PERSONALDRAFT = "project-report-personalDraft";
    private static final String STYLE_PROJECT_REPORT_DRAFT_BUTTON = "project-report-draft-button";
    private static final String STYLE_PROJECT_REPORT_LEVEl = "project-report-level-"; // Prefix.
    private static final String STYLE_PROJECT_REPORT = "project-report";
    private static final String STYLE_PROJECT_REPORT_FIELD_EMPTY = "project-report-field-empty";
    private static final String STYLE_PROJECT_REPORT_FIELD = "project-report-field";
    private static final String STYLE_FLEXIBILITY_ACTION = "flexibility-action";

    private ContentPanel reportsListPanel;
    private Grid<ReportReference> reportsListGrid;
    private ToolBar reportsListToolbar;
    private Button attachButton;
    private Button createReportButton;

    private LayoutContainer mainPanel;
    private KeyQuestionState keyQuestionState;

    /**
     * The global rich text formatter instance.
     */
    private final RichTextArea.Formatter[] globalFormatterArray = new RichTextArea.Formatter[1];

    /**
     * The document column name
     */
    private DocumentNameColumnActionHandler documentNameColumnActionHandler;

    /**
     * {@inheritDoc}
     */
    @Override
    public void initialize() {
        keyQuestionState = new KeyQuestionState();

        add(createReportsListPanel(), Layouts.borderLayoutData(LayoutRegion.WEST, 400f, true));
        add(createMainPanel(), Layouts.borderLayoutData(LayoutRegion.CENTER, Margin.LEFT));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setDocumentNameColumnActionHandler(
            final DocumentNameColumnActionHandler documentNameColumnActionHandler) {
        this.documentNameColumnActionHandler = documentNameColumnActionHandler;
    }

    /**
     * Creates the main container.
     * 
     * @return The component.
     */
    private Component createMainPanel() {

        mainPanel = Layouts.border(false);

        return mainPanel;
    }

    /**
     * Creates the reports list panel.
     * 
     * @return The component.
     */
    private Component createReportsListPanel() {

        reportsListPanel = Panels.content(I18N.CONSTANTS.projectTabReports());

        // Toolbar.
        reportsListToolbar = new ToolBar();
        reportsListToolbar.setAlignment(HorizontalAlignment.RIGHT);
        reportsListPanel.setTopComponent(reportsListToolbar);

        // Buttons.
        createReportButton = Forms.button(I18N.CONSTANTS.reportCreateReport(), IconImageBundle.ICONS.add());
        attachButton = Forms.button(I18N.CONSTANTS.flexibleElementFilesListAddDocument(),
                IconImageBundle.ICONS.attach());

        // Grid and columns model.
        final ColumnConfig editDate = new ColumnConfig(ReportReference.LAST_EDIT_DATE,
                I18N.CONSTANTS.reportLastEditDate(), 200);
        editDate.setDateTimeFormat(DateUtils.DATE_SHORT);
        final ColumnConfig editorName = new ColumnConfig(ReportReference.EDITOR_NAME, I18N.CONSTANTS.reportEditor(),
                200);
        final ColumnConfig iconColumn = new ColumnConfig("icon", "", 20);
        final ColumnConfig reportName = new ColumnConfig(ReportReference.NAME, I18N.CONSTANTS.reportName(), 200);
        final ColumnConfig typeColumn = new ColumnConfig(ReportReference.FLEXIBLE_ELEMENT_LABEL,
                I18N.CONSTANTS.reportType(), 200);

        iconColumn.setRenderer(new GridCellRenderer<ReportReference>() {

            @Override
            public Object render(final ReportReference model, final String property, final ColumnData config,
                    final int rowIndex, final int colIndex, final ListStore<ReportReference> store,
                    final Grid<ReportReference> grid) {

                if (model.isDocument()) {
                    return IconImageBundle.ICONS.attach().createImage();

                } else {
                    return IconImageBundle.ICONS.report().createImage();
                }
            }
        });

        reportName.setRenderer(new GridCellRenderer<ReportReference>() {

            @Override
            public Object render(final ReportReference model, final String property, final ColumnData config,
                    final int rowIndex, final int colIndex, final ListStore<ReportReference> store,
                    final Grid<ReportReference> grid) {

                final com.google.gwt.user.client.ui.Label documrentButton = new com.google.gwt.user.client.ui.Label(
                        (String) model.get(property));
                documrentButton.addStyleName(STYLE_FLEXIBILITY_ACTION);

                // Button click handler.
                documrentButton.addClickHandler(new ClickHandler() {

                    @Override
                    public void onClick(final ClickEvent e) {
                        documentNameColumnActionHandler.onDocumentNameClicked(model, model.isDocument());
                    }
                });

                return documrentButton;
            }
        });

        final ColumnModel reportColumnModel = new ColumnModel(
                Arrays.asList(editDate, editorName, iconColumn, reportName, typeColumn));
        reportsListGrid = new Grid<ReportReference>(new ListStore<ReportReference>(), reportColumnModel);
        reportsListGrid.setAutoExpandColumn(ReportReference.NAME);
        reportsListGrid.getView().setForceFit(true);

        reportsListPanel.add(reportsListGrid);

        return reportsListPanel;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setReportsListButtonsVisibility(final boolean buttonsVisibility) {

        reportsListToolbar.removeAll();

        if (buttonsVisibility) {
            reportsListToolbar.add(attachButton);
            reportsListToolbar.add(new SeparatorToolItem());
            reportsListToolbar.add(createReportButton);
        }

        reportsListToolbar.layout();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Button getReportsListAttachButton() {
        return attachButton;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Button getReportsListCreateButton() {
        return createReportButton;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ListStore<ReportReference> getReportsStore() {
        return reportsListGrid.getStore();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Component getMainPanel() {
        return mainPanel;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public FoldPanel loadReport(final ProjectReportDTO report, final ReportActionsHandler actionsHandler) {

        // --
        // Reset.
        // --

        mainPanel.removeAll();
        keyQuestionState.clear();

        if (report == null) {
            return null;
        }

        // --
        // Report loading.
        // --

        final ContentPanel reportPanel = new ContentPanel(new FitLayout());
        reportPanel.setScrollMode(Scroll.AUTOY);
        reportPanel.setHeadingHtml(report.getName());
        reportPanel.getHeader()
                .addTool(new ToolButton(STYLE_TOOL_CLOSE_ICON, new SelectionListener<IconButtonEvent>() {

                    @Override
                    public void componentSelected(final IconButtonEvent be) {
                        actionsHandler.onCloseReport();
                    }
                }));

        // Report container
        final FlowPanel flowPanel = new FlowPanel();

        // Report
        final FoldPanel root = addSection(null, null, 0);

        // Toolbar
        final ToolBar toolBar = new ToolBar();

        if (report.isDraft()) {

            // Draft banner
            final HorizontalPanel header = new HorizontalPanel();
            header.addStyleName(STYLE_PROJECT_REPORT_DRAFT);

            // The "Personal Draft"
            final Label personalDraft = new Label(I18N.MESSAGES.personalDraft());
            personalDraft.addStyleName(STYLE_PROJECT_REPORT_PERSONALDRAFT);

            final DateTimeFormat dateFormat = DateTimeFormat.getFormat(PredefinedFormat.DATE_MEDIUM);
            final DateTimeFormat timeFormat = DateTimeFormat.getFormat(PredefinedFormat.TIME_MEDIUM);

            // The label showing the last changed time
            final Label draftLastChangedTime = new Label(I18N.MESSAGES.reportDraftLastChanged(
                    dateFormat.format(report.getLastEditDate()), timeFormat.format(report.getLastEditDate())));

            // Add the two labels
            header.add(personalDraft);
            header.add(draftLastChangedTime);

            final HorizontalPanel buttons = new HorizontalPanel();
            buttons.setSpacing(5);
            buttons.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
            buttons.addStyleName(STYLE_PROJECT_REPORT_DRAFT_BUTTON);

            // --
            // Delete button.
            // --

            buttons.add(Forms.button(I18N.CONSTANTS.delete(), new SelectionListener<ButtonEvent>() {

                @Override
                public void componentSelected(final ButtonEvent ce) {
                    actionsHandler.onDeleteReport();
                }
            }));

            // --
            // Send report button.
            // --

            buttons.add(Forms.button(I18N.CONSTANTS.sendReportDraft(), new SelectionListener<ButtonEvent>() {

                @Override
                public void componentSelected(final ButtonEvent ce) {
                    actionsHandler.onShareReport();
                }
            }));

            header.add(buttons);
            header.setCellHorizontalAlignment(buttons, HasHorizontalAlignment.ALIGN_RIGHT);

            flowPanel.add(header);

            // --
            // Save button.
            // --

            toolBar.add(Forms.button(I18N.CONSTANTS.save(), IconImageBundle.ICONS.save(),
                    new SelectionListener<ButtonEvent>() {

                        @Override
                        public void componentSelected(final ButtonEvent be) {

                            actionsHandler.onSaveReport();

                            final Date now = new Date();
                            header.clear();
                            draftLastChangedTime.setTitle(I18N.MESSAGES
                                    .reportDraftLastChanged(dateFormat.format(now), timeFormat.format(now)));
                            personalDraft.setTitle(I18N.MESSAGES.personalDraft());
                            header.add(personalDraft);
                            header.add(draftLastChangedTime);
                            header.add(buttons);
                        }
                    }));

            toolBar.add(new SeparatorToolItem());

        } else {

            // --
            // Edit report button.
            // --

            if (actionsHandler.isEditionEnabled()) {
                toolBar.add(Forms.button(I18N.CONSTANTS.edit(), IconImageBundle.ICONS.editPage(),
                        new SelectionListener<ButtonEvent>() {

                            @Override
                            public void componentSelected(final ButtonEvent ce) {
                                actionsHandler.onEditReport();
                            }
                        }));
            }

            // --
            // Word export button.
            // --

            toolBar.add(Forms.button(I18N.CONSTANTS.exportToWord(), IconImageBundle.ICONS.msword(),
                    new SelectionListener<ButtonEvent>() {

                        @Override
                        public void componentSelected(final ButtonEvent ce) {
                            actionsHandler.onExportReport();
                        }
                    }));

            toolBar.add(new SeparatorToolItem());
        }

        // Key question info
        final Label keyQuestionLabel = keyQuestionState.getLabel();
        toolBar.add(keyQuestionLabel);
        toolBar.add(new SeparatorToolItem());

        // Overview mode
        final Button foldButton = Forms.button(I18N.CONSTANTS.reportOverviewMode());
        foldButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent be) {
                root.expand(true);
                root.fold(true);
            }
        });
        // Expanded mode
        final Button expandButton = Forms.button(I18N.CONSTANTS.reportFullMode());
        expandButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent be) {
                root.expand(true);
            }
        });

        toolBar.add(foldButton);
        toolBar.add(expandButton);

        if (report.isDraft()) {
            toolBar.add(new SeparatorToolItem());
            createRichTextToolbar(toolBar);
        }

        flowPanel.add(root);
        reportPanel.add(flowPanel);
        reportPanel.setTopComponent(toolBar);

        // Display
        mainPanel.add(reportPanel, new BorderLayoutData(LayoutRegion.CENTER));
        mainPanel.layout();

        mainPanel.unmask();

        return root;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public FoldPanel addSection(final String sectionName, final String prefix, final int level) {

        final FoldPanel sectionPanel = new FoldPanel();

        if (level > 0) {
            sectionPanel.setHeading(prefix.toString() + ' ' + sectionName);
            sectionPanel.addStyleName(STYLE_PROJECT_REPORT_LEVEl + level);

        } else {
            sectionPanel.addStyleName(STYLE_PROJECT_REPORT);
        }

        return sectionPanel;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public HasHTML addTextArea(final RichTextElementDTO richTextElement, final FoldPanel sectionPanel,
            final boolean draftMode) {

        if (draftMode) {

            final RichTextArea textArea = new RichTextArea();

            textArea.setHTML(richTextElement.getText());
            textArea.addFocusHandler(new FocusHandler() {

                @Override
                public void onFocus(FocusEvent event) {
                    globalFormatterArray[0] = textArea.getFormatter();
                }
            });

            sectionPanel.add(textArea);
            return textArea;

        } else {

            final HTML html = new HTML();
            final String value = richTextElement.getText();

            if (ClientUtils.isBlank(value)) {
                html.setText(I18N.CONSTANTS.reportEmptySection());
                html.addStyleName(STYLE_PROJECT_REPORT_FIELD_EMPTY);

            } else {
                html.setHTML(value);
                html.addStyleName(STYLE_PROJECT_REPORT_FIELD);
            }

            sectionPanel.add(html);
            return null;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public HasHTML addKeyQuestion(final KeyQuestionDTO keyQuestion, final FoldPanel sectionPanel,
            final boolean draftMode) {

        keyQuestionState.increaseCount();

        keyQuestion.setNumber(keyQuestionState.getCount());

        // Rich text field.
        final RichTextArea textArea = new RichTextArea();
        final RichTextElementDTO richTextElementDTO = keyQuestion.getRichTextElementDTO();

        if (richTextElementDTO != null) {
            textArea.setHTML(richTextElementDTO.getText());
        }

        // Compas icon.
        final ImageResource icon;
        if (ClientUtils.isBlank(textArea.getText())) {
            icon = ToolbarImages.IMAGES.compasRed();

        } else {
            icon = ToolbarImages.IMAGES.compasGreen();
            keyQuestionState.increaseValids();
        }

        sectionPanel.addToolButton(icon, new ClickHandler() {

            @Override
            public void onClick(ClickEvent event) {
                KeyQuestionDialog.getDialog(keyQuestion, textArea, sectionPanel, sectionPanel.getToolButtonCount(),
                        keyQuestionState, draftMode).show();
            }
        });

        return textArea;
    }

    // -------------------------------------------------------------------------------------------------------------
    //
    // UTILITY METHODS.
    //
    // -------------------------------------------------------------------------------------------------------------

    /**
     * Creates a rich text toolbar responding to the {@link #globalRichTextFormatter} events.
     * 
     * @param toolbar
     *          The toolbar to which the rich text toolbar actions buttons should be append.
     */
    private void createRichTextToolbar(final ToolBar toolbar) {
        createRichTextToolbar(toolbar, globalFormatterArray);
    }

    /**
     * Creates a rich text toolbar responding to the given {@code formatter} events.
     * 
     * @param toolbar
     *          The toolbar to which the rich text toolbar actions buttons should be append.
     * @param formatter
     *          The rich text formatter instance.
     */
    static void createRichTextToolbar(final ToolBar toolbar, final RichTextArea.Formatter[] formatter) {

        // Fonts
        final ListBox fontListBox = new ListBox();
        fontListBox.addItem(I18N.CONSTANTS.font());
        fontListBox.addItem("Arial");
        fontListBox.addItem("Times New Roman");
        fontListBox.addItem("Courier New");
        fontListBox.addItem("Georgia");
        fontListBox.addItem("Trebuchet");
        fontListBox.addItem("Verdana");
        fontListBox.addChangeHandler(new ChangeHandler() {

            @Override
            public void onChange(ChangeEvent event) {
                formatter[0].setFontName(fontListBox.getValue(fontListBox.getSelectedIndex()));
            }
        });
        final LayoutContainer fontListBoxWrapper = new LayoutContainer(new FitLayout());
        fontListBoxWrapper.add(fontListBox);
        toolbar.add(fontListBoxWrapper);

        // Bold
        final Button boldButton = Forms.button(null,
                AbstractImagePrototype.create(ToolbarImages.IMAGES.textBold()));
        boldButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent be) {
                formatter[0].toggleBold();
            }
        });
        toolbar.add(boldButton);

        // Italic
        final Button italicButton = Forms.button(null,
                AbstractImagePrototype.create(ToolbarImages.IMAGES.textItalic()));
        italicButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent be) {
                formatter[0].toggleItalic();
            }
        });
        toolbar.add(italicButton);

        // Underline
        final Button underlineButton = Forms.button(null,
                AbstractImagePrototype.create(ToolbarImages.IMAGES.textUnderline()));
        underlineButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent be) {
                formatter[0].toggleUnderline();
            }
        });
        toolbar.add(underlineButton);

        // Strike
        final Button strikeButton = Forms.button(null,
                AbstractImagePrototype.create(ToolbarImages.IMAGES.textStrike()));
        strikeButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent be) {
                formatter[0].toggleStrikethrough();
            }
        });
        toolbar.add(strikeButton);

        // Align left
        final Button alignLeftButton = Forms.button(null,
                AbstractImagePrototype.create(ToolbarImages.IMAGES.textAlignLeft()));
        alignLeftButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent be) {
                formatter[0].setJustification(RichTextArea.Justification.LEFT);
            }
        });
        toolbar.add(alignLeftButton);

        // Align center
        final Button alignCenterButton = Forms.button(null,
                AbstractImagePrototype.create(ToolbarImages.IMAGES.textAlignCenter()));
        alignCenterButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent be) {
                formatter[0].setJustification(RichTextArea.Justification.CENTER);
            }
        });
        toolbar.add(alignCenterButton);

        // Align right
        final Button alignRightButton = Forms.button(null,
                AbstractImagePrototype.create(ToolbarImages.IMAGES.textAlignRight()));
        alignRightButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent be) {
                formatter[0].setJustification(RichTextArea.Justification.RIGHT);
            }
        });
        toolbar.add(alignRightButton);

        // Justify
        final Button alignJustifyButton = Forms.button(null,
                AbstractImagePrototype.create(ToolbarImages.IMAGES.textAlignJustify()));
        alignJustifyButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent be) {
                formatter[0].setJustification(RichTextArea.Justification.FULL);
            }
        });
        toolbar.add(alignJustifyButton);

        // List with numbers
        final Button listNumbersButton = Forms.button(null,
                AbstractImagePrototype.create(ToolbarImages.IMAGES.textListNumbers()));
        listNumbersButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent be) {
                formatter[0].insertOrderedList();
            }
        });
        toolbar.add(listNumbersButton);

        // List with bullets
        final Button listBulletsButton = Forms.button(null,
                AbstractImagePrototype.create(ToolbarImages.IMAGES.textListBullets()));
        listBulletsButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent be) {
                formatter[0].insertUnorderedList();
            }
        });
        toolbar.add(listBulletsButton);

        // Images
        final Button imageAddButton = Forms.button(null,
                AbstractImagePrototype.create(ToolbarImages.IMAGES.imageAdd()));
        imageAddButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

            private Dialog imageAddDialog;
            private TextField<String> imageURLField;

            @Override
            public void componentSelected(ButtonEvent be) {
                if (imageAddDialog == null) {
                    imageAddDialog = new Dialog();

                    imageAddDialog.setButtons(Dialog.OKCANCEL);
                    imageAddDialog.setHeadingHtml(I18N.CONSTANTS.reportAddImageDialogTitle());
                    imageAddDialog.setModal(true);

                    imageAddDialog.setResizable(false);
                    imageAddDialog.setWidth("340px");

                    imageAddDialog.setLayout(new FormLayout());

                    // Report name
                    imageURLField = new TextField<String>();
                    imageURLField.setFieldLabel(I18N.CONSTANTS.reportImageURL());
                    imageURLField.setAllowBlank(false);
                    imageURLField.setName("url");
                    imageAddDialog.add(imageURLField);

                    // OK button
                    imageAddDialog.getButtonById(Dialog.OK)
                            .addSelectionListener(new SelectionListener<ButtonEvent>() {

                                @Override
                                public void componentSelected(ButtonEvent ce) {
                                    formatter[0].insertImage(imageURLField.getValue());
                                    imageAddDialog.hide();
                                }
                            });

                    // Cancel button
                    imageAddDialog.getButtonById(Dialog.CANCEL)
                            .addSelectionListener(new SelectionListener<ButtonEvent>() {

                                @Override
                                public void componentSelected(ButtonEvent ce) {
                                    imageAddDialog.hide();
                                }
                            });
                }

                imageURLField.setValue(null);
                imageAddDialog.show();
            }
        });
        toolbar.add(imageAddButton);
    }

}