001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ui.editors.messagebundle;
029
030import org.opencms.i18n.CmsLocaleManager;
031import org.opencms.i18n.CmsMessages;
032import org.opencms.ui.FontOpenCms;
033import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.EditMode;
034import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.I_OptionListener;
035
036import java.util.Collection;
037import java.util.Locale;
038
039import com.vaadin.event.FieldEvents.BlurEvent;
040import com.vaadin.event.FieldEvents.BlurListener;
041import com.vaadin.event.FieldEvents.FocusEvent;
042import com.vaadin.event.FieldEvents.FocusListener;
043import com.vaadin.event.ShortcutAction.KeyCode;
044import com.vaadin.event.ShortcutListener;
045import com.vaadin.ui.Alignment;
046import com.vaadin.ui.Button;
047import com.vaadin.ui.Button.ClickEvent;
048import com.vaadin.ui.Button.ClickListener;
049import com.vaadin.ui.Component;
050import com.vaadin.ui.FormLayout;
051import com.vaadin.ui.GridLayout;
052import com.vaadin.ui.Notification;
053import com.vaadin.ui.UI;
054import com.vaadin.v7.data.Property.ValueChangeEvent;
055import com.vaadin.v7.data.Property.ValueChangeListener;
056import com.vaadin.v7.ui.ComboBox;
057import com.vaadin.v7.ui.HorizontalLayout;
058import com.vaadin.v7.ui.Label;
059import com.vaadin.v7.ui.TextField;
060
061/** View of the message bundle editor options, i.e., language/mode switcher, file name display and "Add key" option. */
062public class CmsMessageBundleEditorOptions {
063
064    /** Messages used by the GUI. */
065    CmsMessages m_messages;
066
067    /** Grid with all options (2x2). */
068    private GridLayout m_optionsComponent;
069    /** The upper left component in the options grid (containing the language/mode switches and the label for the file path). */
070    private HorizontalLayout m_upperLeftComponent;
071    /** The lower left component in the options grid (containing the "Add key" label). */
072    private Component m_lowerLeftComponent;
073    /** The lower right component in the options grid (containing the "Add key" input field and the "Add key" button). */
074    private Component m_lowerRightComponent;
075    /** The select box for choosing the currently edited language. */
076    private ComboBox m_languageSelect;
077    /** The component that contains the language switch. */
078    private Component m_languageSwitch;
079    /** The component that contains the mode switch. */
080    private Component m_modeSwitch;
081    /** The select box for the current edit mode. */
082    private ComboBox m_modeSelect;
083    /** The component with the label of the "File"-Display. */
084    private Component m_filePathLabel;
085    /** The input field that displays the path of the currently edited file. */
086    private TextField m_filePathField;
087    /** The "Add key" input field. */
088    TextField m_addKeyInput;
089    /** A flag, indicating if the mode switch should be shown. */
090    private boolean m_showModeSwitch;
091    /** A flag, indicating if the "Add key" row should be shown. */
092    private boolean m_showAddKeyOption;
093    /** The listener for option changes. */
094    I_OptionListener m_listener;
095
096    /**
097     * Default constructor.
098     * @param locales the locales shown in the language switch.
099     * @param currentLocale the currently edited locale.
100     * @param currentMode the current edit mode.
101     * @param optionListener the option listener.
102     */
103    public CmsMessageBundleEditorOptions(
104        final Collection<Locale> locales,
105        final Locale currentLocale,
106        final EditMode currentMode,
107        final I_OptionListener optionListener) {
108
109        m_messages = Messages.get().getBundle(UI.getCurrent().getLocale());
110        m_listener = optionListener;
111        initLanguageSwitch(locales, currentLocale);
112        initModeSwitch(currentMode);
113        initFilePathLabel();
114        initUpperLeftComponent();
115        initLowerLeftComponent();
116        initLowerRightComponent();
117
118        initOptionsComponent();
119    }
120
121    /**
122     * Puts focus on the "Add key" input field, iff it is shown.
123     * @return <code>true</code> if the focus has been set, <code>false</code> otherwise.
124     */
125    public boolean focusAddKey() {
126
127        if (m_showAddKeyOption) {
128            m_addKeyInput.focus();
129        }
130        return m_showAddKeyOption;
131    }
132
133    /**
134     * Returns the options component.
135     * @return the options component.
136     */
137    public Component getOptionsComponent() {
138
139        return m_optionsComponent;
140    }
141
142    /**
143     * Sets the path of the edited file in the corresponding display.
144     * @param editedFilePath path of the edited file to set.
145     */
146    public void setEditedFilePath(final String editedFilePath) {
147
148        m_filePathField.setReadOnly(false);
149        m_filePathField.setValue(editedFilePath);
150        m_filePathField.setReadOnly(true);
151
152    }
153
154    /**
155     * Set the edit mode.
156     * @param mode the edit mode to set.
157     */
158    public void setEditMode(final EditMode mode) {
159
160        if (!m_modeSelect.getValue().equals(mode)) {
161            m_modeSelect.setValue(mode);
162        }
163    }
164
165    /**
166     * Update which options are shown.
167     * @param showModeSwitch flag, indicating if the mode switch should be shown.
168     * @param showAddKeyOption flag, indicating if the "Add key" row should be shown.
169     */
170    public void updateShownOptions(boolean showModeSwitch, boolean showAddKeyOption) {
171
172        if (showModeSwitch != m_showModeSwitch) {
173            m_upperLeftComponent.removeAllComponents();
174            m_upperLeftComponent.addComponent(m_languageSwitch);
175            if (showModeSwitch) {
176                m_upperLeftComponent.addComponent(m_modeSwitch);
177            }
178            m_upperLeftComponent.addComponent(m_filePathLabel);
179            m_showModeSwitch = showModeSwitch;
180        }
181        if (showAddKeyOption != m_showAddKeyOption) {
182            if (showAddKeyOption) {
183                m_optionsComponent.addComponent(m_lowerLeftComponent, 0, 1);
184                m_optionsComponent.addComponent(m_lowerRightComponent, 1, 1);
185            } else {
186                m_optionsComponent.removeComponent(0, 1);
187                m_optionsComponent.removeComponent(1, 1);
188            }
189            m_showAddKeyOption = showAddKeyOption;
190        }
191    }
192
193    /**
194     * Handles adding a key. Calls the registered listener and wraps it's method in some GUI adjustments.
195     */
196    void handleAddKey() {
197
198        String key = m_addKeyInput.getValue();
199        if (m_listener.handleAddKey(key)) {
200            Notification.show(
201                key.isEmpty()
202                ? m_messages.key(Messages.GUI_NOTIFICATION_MESSAGEBUNDLEEDITOR_EMPTY_KEY_SUCCESSFULLY_ADDED_0)
203                : m_messages.key(Messages.GUI_NOTIFICATION_MESSAGEBUNDLEEDITOR_KEY_SUCCESSFULLY_ADDED_1, key));
204        } else {
205            CmsMessageBundleEditorTypes.showWarning(
206                m_messages.key(Messages.GUI_NOTIFICATION_MESSAGEBUNDLEEDITOR_KEY_ALREADEY_EXISTS_CAPTION_0),
207                m_messages.key(Messages.GUI_NOTIFICATION_MESSAGEBUNDLEEDITOR_KEY_ALREADEY_EXISTS_DESCRIPTION_1, key));
208
209        }
210        m_addKeyInput.focus();
211        m_addKeyInput.selectAll();
212    }
213
214    /**
215     * Sets the currently edited locale.
216     * @param locale the locale to set.
217     */
218    void setLanguage(final Locale locale) {
219
220        if (!m_languageSelect.getValue().equals(locale)) {
221            m_languageSelect.setValue(locale);
222        }
223    }
224
225    /**
226     * Creates the "Add key" button.
227     * @return the "Add key" button.
228     */
229    private Component createAddKeyButton() {
230
231        // the "+" button
232        Button addKeyButton = new Button();
233        addKeyButton.addStyleName("icon-only");
234        addKeyButton.addStyleName("borderless-colored");
235        addKeyButton.setDescription(m_messages.key(Messages.GUI_ADD_KEY_0));
236        addKeyButton.setIcon(FontOpenCms.CIRCLE_PLUS, m_messages.key(Messages.GUI_ADD_KEY_0));
237        addKeyButton.addClickListener(new ClickListener() {
238
239            private static final long serialVersionUID = 1L;
240
241            public void buttonClick(ClickEvent event) {
242
243                handleAddKey();
244
245            }
246        });
247        return addKeyButton;
248    }
249
250    /**
251     * Creates the upper right component of the options grid.
252     * Creation includes the initialization of {@link #m_filePathField}.
253     *
254     * @return the upper right component in the options grid.
255     */
256    private Component createUpperRightComponent() {
257
258        HorizontalLayout upperRight = new HorizontalLayout();
259        upperRight.setSizeFull();
260
261        FormLayout fileNameDisplay = new FormLayout();
262        fileNameDisplay.setDefaultComponentAlignment(Alignment.MIDDLE_LEFT);
263        fileNameDisplay.setSizeFull();
264
265        m_filePathField = new TextField();
266        m_filePathField.setWidth("100%");
267        m_filePathField.setEnabled(true);
268        m_filePathField.setReadOnly(true);
269
270        fileNameDisplay.addComponent(m_filePathField);
271        fileNameDisplay.setSpacing(true);
272
273        FormLayout filePathDisplay = new FormLayout();
274        filePathDisplay.setDefaultComponentAlignment(Alignment.MIDDLE_LEFT);
275        filePathDisplay.setSizeFull();
276        filePathDisplay.addComponent(m_filePathField);
277        filePathDisplay.setSpacing(true);
278
279        upperRight.addComponent(filePathDisplay);
280        upperRight.setExpandRatio(filePathDisplay, 2f);
281
282        HorizontalLayout placeHolder = new HorizontalLayout();
283        placeHolder.setWidth(CmsMessageBundleEditorTypes.OPTION_COLUMN_WIDTH_PX);
284        upperRight.addComponent(placeHolder);
285
286        return upperRight;
287    }
288
289    /**
290     * Initializes the input field for new keys {@link #m_addKeyInput}.
291     */
292    private void initAddKeyInput() {
293
294        //the input field for the key
295        m_addKeyInput = new TextField();
296        m_addKeyInput.setWidth("100%");
297        m_addKeyInput.setInputPrompt(m_messages.key(Messages.GUI_INPUT_PROMPT_ADD_KEY_0));
298        final ShortcutListener shortCutListener = new ShortcutListener("Add key via ENTER", KeyCode.ENTER, null) {
299
300            private static final long serialVersionUID = 1L;
301
302            @Override
303            public void handleAction(Object sender, Object target) {
304
305                handleAddKey();
306
307            }
308
309        };
310        m_addKeyInput.addFocusListener(new FocusListener() {
311
312            private static final long serialVersionUID = 1L;
313
314            public void focus(FocusEvent event) {
315
316                m_addKeyInput.addShortcutListener(shortCutListener);
317
318            }
319        });
320        m_addKeyInput.addBlurListener(new BlurListener() {
321
322            private static final long serialVersionUID = 1L;
323
324            public void blur(BlurEvent event) {
325
326                m_addKeyInput.removeShortcutListener(shortCutListener);
327
328            }
329        });
330
331    }
332
333    /**
334     * Initializes the label for the file path display {@link #m_filePathLabel}.
335     */
336    private void initFilePathLabel() {
337
338        m_filePathLabel = new TextField();
339        m_filePathLabel.setWidth("100%");
340        m_filePathLabel.setEnabled(true);
341        ((TextField)m_filePathLabel).setReadOnly(true);
342        m_filePathLabel = new Label(m_messages.key(Messages.GUI_FILENAME_LABEL_0));
343
344    }
345
346    /**
347     * Initializes the language switcher UI Component {@link #m_languageSwitch}, including {@link #m_languageSelect}.
348     * @param locales the locales that can be selected.
349     * @param current the currently selected locale.
350     */
351    private void initLanguageSwitch(Collection<Locale> locales, Locale current) {
352
353        FormLayout languages = new FormLayout();
354        languages.setHeight("100%");
355        languages.setDefaultComponentAlignment(Alignment.MIDDLE_LEFT);
356        ComboBox languageSelect = new ComboBox();
357        languageSelect.setCaption(m_messages.key(Messages.GUI_LANGUAGE_SWITCHER_LABEL_0));
358        languageSelect.setNullSelectionAllowed(false);
359
360        // set Locales
361        for (Locale locale : locales) {
362            languageSelect.addItem(locale);
363            String caption = locale.getDisplayName(UI.getCurrent().getLocale());
364            if (CmsLocaleManager.getDefaultLocale().equals(locale)) {
365                caption += " ("
366                    + Messages.get().getBundle(UI.getCurrent().getLocale()).key(Messages.GUI_DEFAULT_LOCALE_0)
367                    + ")";
368            }
369            languageSelect.setItemCaption(locale, caption);
370        }
371        languageSelect.setValue(current);
372        languageSelect.setNewItemsAllowed(false);
373        languageSelect.setTextInputAllowed(false);
374        languageSelect.addValueChangeListener(new ValueChangeListener() {
375
376            private static final long serialVersionUID = 1L;
377
378            public void valueChange(ValueChangeEvent event) {
379
380                m_listener.handleLanguageChange((Locale)event.getProperty().getValue());
381
382            }
383        });
384
385        if (locales.size() == 1) {
386            languageSelect.setEnabled(false);
387        }
388        languages.addComponent(languageSelect);
389        m_languageSwitch = languages;
390    }
391
392    /**
393     * Initializes the lower left component {@link #m_lowerLeftComponent} with the correctly placed "Add key"-label.
394     */
395    private void initLowerLeftComponent() {
396
397        HorizontalLayout placeHolderLowerLeft = new HorizontalLayout();
398        placeHolderLowerLeft.setWidth("100%");
399        Label newKeyLabel = new Label(m_messages.key(Messages.GUI_CAPTION_ADD_KEY_0));
400        newKeyLabel.setWidthUndefined();
401        HorizontalLayout lowerLeft = new HorizontalLayout(placeHolderLowerLeft, newKeyLabel);
402        lowerLeft.setWidth("100%");
403        lowerLeft.setExpandRatio(placeHolderLowerLeft, 1f);
404        m_lowerLeftComponent = lowerLeft;
405
406    }
407
408    /**
409     * Initializes the lower right component {@link #m_lowerRightComponent}, with all its components, i.e.,
410     * the "Add key" input field {@link #m_addKeyInput} and the "Add key" button.
411     */
412    private void initLowerRightComponent() {
413
414        initAddKeyInput();
415
416        Component addKeyButton = createAddKeyButton();
417        HorizontalLayout addKeyWrapper = new HorizontalLayout(addKeyButton);
418        addKeyWrapper.setComponentAlignment(addKeyButton, Alignment.MIDDLE_CENTER);
419        addKeyWrapper.setHeight("100%");
420        addKeyWrapper.setWidth(CmsMessageBundleEditorTypes.OPTION_COLUMN_WIDTH_PX);
421
422        FormLayout inputForm = new FormLayout(m_addKeyInput);
423        inputForm.setWidth("100%");
424        HorizontalLayout lowerRight = new HorizontalLayout();
425        lowerRight.setWidth("100%");
426        lowerRight.addComponent(inputForm);
427        lowerRight.addComponent(addKeyWrapper);
428        lowerRight.setExpandRatio(inputForm, 1f);
429        m_lowerRightComponent = lowerRight;
430
431    }
432
433    /**
434     * Initializes the mode switcher.
435     * @param current the current edit mode
436     */
437    private void initModeSwitch(final EditMode current) {
438
439        FormLayout modes = new FormLayout();
440        modes.setHeight("100%");
441        modes.setDefaultComponentAlignment(Alignment.MIDDLE_LEFT);
442
443        m_modeSelect = new ComboBox();
444        m_modeSelect.setCaption(m_messages.key(Messages.GUI_VIEW_SWITCHER_LABEL_0));
445
446        // add Modes
447        m_modeSelect.addItem(CmsMessageBundleEditorTypes.EditMode.DEFAULT);
448        m_modeSelect.setItemCaption(
449            CmsMessageBundleEditorTypes.EditMode.DEFAULT,
450            m_messages.key(Messages.GUI_VIEW_SWITCHER_EDITMODE_DEFAULT_0));
451        m_modeSelect.addItem(CmsMessageBundleEditorTypes.EditMode.MASTER);
452        m_modeSelect.setItemCaption(
453            CmsMessageBundleEditorTypes.EditMode.MASTER,
454            m_messages.key(Messages.GUI_VIEW_SWITCHER_EDITMODE_MASTER_0));
455
456        // set current mode as selected
457        m_modeSelect.setValue(current);
458
459        m_modeSelect.setNewItemsAllowed(false);
460        m_modeSelect.setTextInputAllowed(false);
461        m_modeSelect.setNullSelectionAllowed(false);
462
463        m_modeSelect.addValueChangeListener(new ValueChangeListener() {
464
465            private static final long serialVersionUID = 1L;
466
467            public void valueChange(ValueChangeEvent event) {
468
469                m_listener.handleModeChange((EditMode)event.getProperty().getValue());
470
471            }
472        });
473
474        modes.addComponent(m_modeSelect);
475        m_modeSwitch = modes;
476    }
477
478    /**
479     * Creates the complete options component.
480     * It's a grid with two rows and two columns, styled like:
481     *
482     *                                              ||
483     *   [language switch] [mode switch] File-Label || [file path display]
484     *   -------------------------------------------||----------------------------------
485     *                                New-Key-Label || [new key input] [add key button]
486     *                                              ||
487     *
488     *   NOTE: The second row is not filled with components on initialization, what means keys can not be added
489     *         Filling is done via {@link #updateShownOptions(boolean, boolean)}
490     */
491    private void initOptionsComponent() {
492
493        // create and layout the component
494        m_optionsComponent = new GridLayout(2, 2);
495        m_optionsComponent.setHideEmptyRowsAndColumns(true);
496        m_optionsComponent.setDefaultComponentAlignment(Alignment.MIDDLE_RIGHT);
497        m_optionsComponent.setWidth("100%");
498        m_optionsComponent.setColumnExpandRatio(1, 1f);
499        m_optionsComponent.setStyleName("v-options");
500
501        // add the components
502        m_optionsComponent.addComponent(m_upperLeftComponent, 0, 0);
503
504        Component upperRight = createUpperRightComponent();
505        m_optionsComponent.addComponent(upperRight, 1, 0);
506    }
507
508    /**
509     * Initializes the upper left component. Does not show the mode switch.
510     */
511    private void initUpperLeftComponent() {
512
513        m_upperLeftComponent = new HorizontalLayout();
514        m_upperLeftComponent.setHeight("100%");
515        m_upperLeftComponent.setSpacing(true);
516        m_upperLeftComponent.setDefaultComponentAlignment(Alignment.MIDDLE_RIGHT);
517        m_upperLeftComponent.addComponent(m_languageSwitch);
518        m_upperLeftComponent.addComponent(m_filePathLabel);
519
520    }
521
522}