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.components;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsResource;
032import org.opencms.file.CmsResourceFilter;
033import org.opencms.main.CmsException;
034import org.opencms.main.CmsLog;
035import org.opencms.main.OpenCms;
036import org.opencms.site.CmsSite;
037import org.opencms.ui.A_CmsDialogContext;
038import org.opencms.ui.A_CmsUI;
039import org.opencms.ui.CmsVaadinUtils;
040import org.opencms.ui.FontOpenCms;
041import org.opencms.ui.I_CmsDialogContext;
042import org.opencms.ui.apps.CmsAppWorkplaceUi;
043import org.opencms.ui.apps.CmsDefaultAppButtonProvider;
044import org.opencms.ui.apps.CmsFileExplorer;
045import org.opencms.ui.apps.I_CmsAppUIContext;
046import org.opencms.ui.apps.I_CmsWorkplaceAppConfiguration;
047import org.opencms.ui.apps.Messages;
048import org.opencms.ui.components.CmsBasicDialog.DialogWidth;
049import org.opencms.ui.components.CmsUploadButton.I_UploadListener;
050import org.opencms.ui.contextmenu.CmsContextMenuTreeBuilder;
051import org.opencms.ui.contextmenu.I_CmsContextMenuItem;
052import org.opencms.ui.favorites.CmsExplorerFavoriteContext;
053import org.opencms.ui.favorites.CmsFavoriteDAO;
054import org.opencms.ui.favorites.CmsFavoriteDialog;
055import org.opencms.util.CmsStringUtil;
056import org.opencms.util.CmsTreeNode;
057import org.opencms.util.CmsUUID;
058import org.opencms.workplace.CmsWorkplace;
059
060import java.util.Collections;
061import java.util.List;
062import java.util.Locale;
063
064import org.apache.commons.logging.Log;
065
066import com.google.common.collect.Lists;
067import com.vaadin.server.ExternalResource;
068import com.vaadin.server.FontIcon;
069import com.vaadin.server.Page;
070import com.vaadin.server.Page.BrowserWindowResizeEvent;
071import com.vaadin.server.Page.BrowserWindowResizeListener;
072import com.vaadin.server.Resource;
073import com.vaadin.shared.ui.ContentMode;
074import com.vaadin.ui.AbstractOrderedLayout;
075import com.vaadin.ui.Alignment;
076import com.vaadin.ui.Button;
077import com.vaadin.ui.Component;
078import com.vaadin.ui.CssLayout;
079import com.vaadin.ui.HorizontalLayout;
080import com.vaadin.ui.Label;
081import com.vaadin.ui.MenuBar;
082import com.vaadin.ui.MenuBar.Command;
083import com.vaadin.ui.MenuBar.MenuItem;
084import com.vaadin.ui.PopupView;
085import com.vaadin.ui.UI;
086import com.vaadin.ui.VerticalLayout;
087import com.vaadin.ui.Window;
088import com.vaadin.ui.declarative.Design;
089import com.vaadin.ui.themes.ValoTheme;
090
091/**
092 * The workplace toolbar.<p>
093 */
094public class CmsToolBar extends CssLayout implements BrowserWindowResizeListener {
095
096    /** Toolbar dialog context. */
097    protected class ToolbarContext extends A_CmsDialogContext {
098
099        /**
100         * Constructor.<p>
101         *
102         * @param appId the app id
103         */
104        protected ToolbarContext(String appId) {
105
106            super(appId, ContextType.appToolbar, Collections.<CmsResource> emptyList());
107        }
108
109        /**
110         * @see org.opencms.ui.I_CmsDialogContext#focus(org.opencms.util.CmsUUID)
111         */
112        public void focus(CmsUUID structureId) {
113
114            // nothing to do
115        }
116
117        /**
118         * @see org.opencms.ui.I_CmsDialogContext#getAllStructureIdsInView()
119         */
120        public List<CmsUUID> getAllStructureIdsInView() {
121
122            return Lists.newArrayList();
123        }
124
125        /**
126         * @see org.opencms.ui.I_CmsDialogContext#updateUserInfo()
127         */
128        public void updateUserInfo() {
129
130            refreshUserInfoDropDown();
131        }
132    }
133
134    /** Logger instance for this class. */
135    private static final Log LOG = CmsLog.getLog(CmsToolBar.class);
136
137    /** The serial version id. */
138    private static final long serialVersionUID = -4551194983054069395L;
139
140    /** The app UI context. */
141    protected I_CmsAppUIContext m_appContext;
142
143    /** The app indicator. */
144    private Label m_appIndicator;
145
146    /** Flag indicating the toolbar buttons are folded into a sub menu. */
147    private boolean m_buttonsFolded;
148
149    /** The context menu component. */
150    private MenuBar m_contextMenu;
151
152    /** The dialog context. */
153    private I_CmsDialogContext m_dialogContext;
154
155    /** The favorite button. */
156    private Button m_favButton;
157
158    /** The sub menu displaying the folded buttons. */
159    private PopupView m_foldedButtonsMenu;
160
161    /** The browser window width that is required to display all toolbar buttons. */
162    private int m_foldingThreshhold;
163
164    /** Toolbar items left. */
165    private HorizontalLayout m_itemsLeft;
166
167    /** Toolbar items right. */
168    private HorizontalLayout m_itemsRight;
169
170    /** The contains the buttons from the left side, displayed in the folded buttons sub menu. */
171    private VerticalLayout m_leftButtons;
172
173    /** The quick launch drop down. */
174    private Component m_quickLaunchDropDown;
175
176    /** The contains the buttons from the right side, displayed in the folded buttons sub menu. */
177    private VerticalLayout m_rightButtons;
178
179    /** The user drop down. */
180    private Component m_userDropDown;
181
182    /**
183     * Constructor.<p>
184     */
185    public CmsToolBar() {
186
187        m_quickLaunchDropDown = createQuickLaunchDropDown();
188        m_userDropDown = createUserInfoDropDown();
189        m_favButton = CmsToolBar.createButton(
190            FontOpenCms.BOOKMARKS,
191            CmsVaadinUtils.getMessageText(org.opencms.ui.Messages.GUI_FAVORITES_BUTTON_0),
192            true);
193        m_leftButtons = new VerticalLayout();
194        m_rightButtons = new VerticalLayout();
195        VerticalLayout layout = new VerticalLayout();
196        layout.addComponent(m_leftButtons);
197        layout.addComponent(m_rightButtons);
198        m_foldedButtonsMenu = new PopupView(getDropDownButtonHtml(FontOpenCms.CONTEXT_MENU_DOTS), layout);
199        m_foldedButtonsMenu.addStyleName(OpenCmsTheme.NAVIGATOR_DROPDOWN);
200        m_foldedButtonsMenu.setHideOnMouseOut(false);
201        Design.read("CmsToolBar.html", this);
202
203        m_favButton.addClickListener(evt -> {
204            CmsFileExplorer explorer = (CmsFileExplorer)m_appContext.getAttribute(CmsFileExplorer.ATTR_KEY);
205            openFavoriteDialog(explorer);
206        });
207    }
208
209    /**
210     * Creates a properly styled toolbar button.<p>
211     *
212     * @param icon the button icon
213     * @param title the button title, will be used for the tooltip
214     *
215     * @return the button
216     */
217    public static Button createButton(Resource icon, String title) {
218
219        return createButton(icon, title, false);
220    }
221
222    /**
223     * Creates a properly styled toolbar button.<p>
224     *
225     * @param icon the button icon
226     * @param title the button title, will be used for the tooltip
227     * @param alwaysShow <code>true</code> to prevent the button to be folded into a sub menu for small screens
228     *
229     * @return the button
230     */
231    public static Button createButton(Resource icon, String title, boolean alwaysShow) {
232
233        Button button = new Button(icon);
234        button.setDescription(title);
235        button.addStyleName(ValoTheme.BUTTON_BORDERLESS);
236        button.addStyleName(OpenCmsTheme.TOOLBAR_BUTTON);
237        if (alwaysShow) {
238            button.addStyleName(OpenCmsTheme.REQUIRED_BUTTON);
239        }
240        return button;
241    }
242
243    /**
244     * Creates a drop down menu.<p>
245     *
246     * @param icon the button icon
247     * @param content the drop down content
248     * @param title the button title
249     *
250     * @return the component
251     */
252    public static Component createDropDown(ExternalResource icon, Component content, String title) {
253
254        return createDropDown(getDropDownButtonHtml(icon), content, title);
255    }
256
257    /**
258     * Creates a drop down menu.<p>
259     *
260     * @param icon the button icon
261     * @param content the drop down content
262     * @param title the drop down title
263     *
264     * @return the component
265     */
266    public static Component createDropDown(FontIcon icon, Component content, String title) {
267
268        return createDropDown(getDropDownButtonHtml(icon), content, title);
269    }
270
271    /**
272     * Creates a drop down menu.<p>
273     *
274     * @param buttonHtml the button HTML
275     * @param content the drop down content
276     * @param title the button title
277     *
278     * @return the component
279     */
280    public static Component createDropDown(String buttonHtml, Component content, String title) {
281
282        PopupView pv = new PopupView(buttonHtml, content);
283        pv.setDescription(title);
284        pv.addStyleName(OpenCmsTheme.NAVIGATOR_DROPDOWN);
285        pv.setHideOnMouseOut(false);
286        return pv;
287    }
288
289    /**
290     * Opens the favorite dialog.
291     *
292     * @param explorer the explorer instance (null if not currently in explorer)
293     */
294    public static void openFavoriteDialog(CmsFileExplorer explorer) {
295
296        try {
297            CmsExplorerFavoriteContext context = new CmsExplorerFavoriteContext(A_CmsUI.getCmsObject(), explorer);
298            CmsFavoriteDialog dialog = new CmsFavoriteDialog(context, new CmsFavoriteDAO(A_CmsUI.getCmsObject()));
299            Window window = CmsBasicDialog.prepareWindow(DialogWidth.max);
300            window.setContent(dialog);
301            window.setCaption(CmsVaadinUtils.getMessageText(org.opencms.ui.Messages.GUI_FAVORITES_DIALOG_TITLE_0));
302            A_CmsUI.get().addWindow(window);
303            window.center();
304        } catch (CmsException e) {
305            CmsErrorDialog.showErrorDialog(e);
306        }
307    }
308
309    /**
310     * Creates the button HTML for the given icon resource.<p>
311     *
312     * @param icon the icon
313     *
314     * @return the HTML
315     */
316    static String getDropDownButtonHtml(ExternalResource icon) {
317
318        return "<div tabindex=\"0\" role=\"button\" class=\"v-button v-widget borderless v-button-borderless "
319            + OpenCmsTheme.TOOLBAR_BUTTON
320            + " v-button-"
321            + OpenCmsTheme.TOOLBAR_BUTTON
322            + "\"><span class=\"v-button-wrap\"><img class=\"v-icon\" src=\""
323            + icon.getURL()
324            + "\" /></span></div>";
325    }
326
327    /**
328     * Creates the button HTML for the given icon resource.<p>
329     *
330     * @param icon the icon
331     *
332     * @return the HTML
333     */
334    static String getDropDownButtonHtml(FontIcon icon) {
335
336        return "<div tabindex=\"0\" role=\"button\" class=\"v-button v-widget borderless v-button-borderless "
337            + OpenCmsTheme.TOOLBAR_BUTTON
338            + " v-button-"
339            + OpenCmsTheme.TOOLBAR_BUTTON
340            + "\"><span class=\"v-button-wrap\">"
341            + icon.getHtml()
342            + "</span></div>";
343    }
344
345    /**
346     * Adds a button to left toolbar side.<p>
347     *
348     * @param button the button
349     */
350    public void addButtonLeft(Component button) {
351
352        if (m_buttonsFolded && !isAlwaysShow(button)) {
353            m_leftButtons.addComponent(button);
354        } else {
355            m_itemsLeft.addComponent(button);
356        }
357        updateFoldingThreshhold();
358    }
359
360    /**
361     * Adds a button to right toolbar side.<p>
362     *
363     * @param button the button
364     */
365    public void addButtonRight(Component button) {
366
367        if (m_buttonsFolded && !isAlwaysShow(button)) {
368            m_rightButtons.addComponent(button);
369        } else {
370            int dropDownIndex = m_itemsRight.getComponentIndex(m_userDropDown);
371            if (dropDownIndex >= 0) {
372                m_itemsRight.addComponent(button, dropDownIndex);
373            } else {
374                m_itemsRight.addComponent(button);
375            }
376        }
377        updateFoldingThreshhold();
378    }
379
380    /**
381     * @see com.vaadin.server.Page.BrowserWindowResizeListener#browserWindowResized(com.vaadin.server.Page.BrowserWindowResizeEvent)
382     */
383    public void browserWindowResized(BrowserWindowResizeEvent event) {
384
385        updateButtonVisibility(event.getWidth());
386    }
387
388    /**
389     * Clears the left toolbar buttons.<p>
390     */
391    public void clearButtonsLeft() {
392
393        m_itemsLeft.removeAllComponents();
394        m_leftButtons.removeAllComponents();
395        // in case the app title is set, make sure to keep the label in the button bar
396        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_appIndicator.getValue())) {
397            m_itemsLeft.addComponent(m_appIndicator);
398        }
399        updateFoldingThreshhold();
400    }
401
402    /**
403     * Clears the right toolbar buttons.<p>
404     */
405    public void clearButtonsRight() {
406
407        m_itemsRight.removeAllComponents();
408        m_rightButtons.removeAllComponents();
409        updateFoldingThreshhold();
410    }
411
412    /**
413     * Closes all visible popup views.<p>
414     */
415    public void closePopupViews() {
416
417        closePopupViews(m_itemsLeft);
418        closePopupViews(m_itemsRight);
419    }
420
421    /**
422     * Enables or removes the default toolbar buttons.<p>
423     * These are the context menu and the quick launch drop down.<p>
424     * The default is <code>enabled = true</code>.<p>
425     *
426     * @param enabled <code>true</code> to enable the buttons
427     */
428    public void enableDefaultButtons(boolean enabled) {
429
430        if (enabled) {
431            m_itemsRight.addComponent(m_contextMenu, 0);
432            m_itemsRight.addComponent(m_favButton, 1);
433            m_itemsRight.addComponent(m_quickLaunchDropDown, 2);
434        } else {
435            m_itemsRight.removeComponent(m_contextMenu);
436            m_itemsRight.removeComponent(m_favButton);
437            m_itemsRight.removeComponent(m_quickLaunchDropDown);
438        }
439        updateFoldingThreshhold();
440    }
441
442    /**
443     * Refreshes the user drop down.<p>
444     */
445    public void refreshUserInfoDropDown() {
446
447        Component oldVersion = m_userDropDown;
448        m_userDropDown = createUserInfoDropDown();
449        m_itemsRight.replaceComponent(oldVersion, m_userDropDown);
450    }
451
452    /**
453     * Removes the given button from the toolbar.<p>
454     *
455     * @param button the button to remove
456     */
457    public void removeButton(Component button) {
458
459        m_itemsLeft.removeComponent(button);
460        m_itemsRight.removeComponent(button);
461        m_leftButtons.removeComponent(button);
462        m_rightButtons.removeComponent(button);
463        updateFoldingThreshhold();
464    }
465
466    /**
467     * Sets the app context.
468     *
469     * @param context the app context
470     */
471    public void setAppContext(I_CmsAppUIContext context) {
472
473        m_appContext = context;
474    }
475
476    /**
477     * Sets the app title.<p>
478     *
479     * @param appTitle the app title
480     */
481    public void setAppTitle(String appTitle) {
482
483        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(appTitle)) {
484            m_appIndicator.setValue(appTitle);
485            updateAppIndicator();
486            m_appIndicator.setVisible(true);
487        } else {
488            m_appIndicator.setVisible(false);
489        }
490    }
491
492    /**
493     * Updates the app indicator site and project info.<p>
494     */
495    public void updateAppIndicator() {
496
497        if (CmsAppWorkplaceUi.isOnlineProject()) {
498            m_appIndicator.addStyleName(OpenCmsTheme.TOOLABER_APP_INDICATOR_ONLINE);
499
500        } else {
501            m_appIndicator.removeStyleName(OpenCmsTheme.TOOLABER_APP_INDICATOR_ONLINE);
502        }
503        CmsObject cms = A_CmsUI.getCmsObject();
504        String siteRoot = cms.getRequestContext().getSiteRoot();
505        CmsSite site = OpenCms.getSiteManager().getSiteForSiteRoot(siteRoot);
506        String siteName = null;
507        if (site != null) {
508            siteName = site.getTitle();
509        } else {
510            try {
511                CmsResource folder = cms.readResource("/", CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
512
513                siteName = OpenCms.getSiteManager().getSiteTitle(cms, folder);
514            } catch (CmsException e) {
515                LOG.warn("Error reading site title.", e);
516            }
517        }
518        if (CmsStringUtil.isEmptyOrWhitespaceOnly(siteName)) {
519            siteName = siteRoot;
520        } else {
521            siteName = CmsWorkplace.substituteSiteTitleStatic(siteName, UI.getCurrent().getLocale());
522        }
523        m_appIndicator.setDescription(
524            CmsVaadinUtils.getMessageText(
525                Messages.GUI_TOOLBAR_PROJECT_SITE_INFO_2,
526                A_CmsUI.getCmsObject().getRequestContext().getCurrentProject().getName(),
527                siteName),
528            ContentMode.HTML);
529    }
530
531    /**
532     * Initializes the toolbar.<p>
533     *
534     * @param appId the app id
535     * @param context the app UI context
536     */
537    protected void init(String appId, I_CmsAppUIContext context) {
538
539        m_dialogContext = new ToolbarContext(appId);
540        m_appContext = context;
541        initContextMenu();
542        m_itemsRight.addComponent(m_quickLaunchDropDown);
543        m_itemsRight.addComponent(m_userDropDown);
544        enableDefaultButtons(true);
545    }
546
547    /**
548     * Sets the dialog context.<p>
549     *
550     * @param context the dialog context
551     */
552    protected void setDialogContext(I_CmsDialogContext context) {
553
554        m_dialogContext = context;
555
556        // reinit context menu
557        initContextMenu();
558    }
559
560    /**
561     * Updates the button visibility according o the given widow width.<p>
562     *
563     * @param width the window width
564     */
565    protected void updateButtonVisibility(int width) {
566
567        if (!m_buttonsFolded && (m_foldingThreshhold > width)) {
568            foldButtons();
569        } else if (m_buttonsFolded && (width > m_foldingThreshhold)) {
570            unfoldButtons();
571        }
572    }
573
574    /**
575     * Recalculates the space required by the toolbar buttons.<p>
576     */
577    protected void updateFoldingThreshhold() {
578
579        int left = estimateRequiredWidth(m_itemsLeft) + estimateRequiredWidth(m_leftButtons);
580        int right = estimateRequiredWidth(m_itemsRight) + estimateRequiredWidth(m_rightButtons);
581        int requiredWidth = left > right ? left : right;
582        if (requiredWidth < 350) {
583            // folding not required at any width
584            m_foldingThreshhold = 0;
585        } else if (requiredWidth < 400) {
586            m_foldingThreshhold = 984;
587        } else if (requiredWidth <= 520) {
588            m_foldingThreshhold = 1240;
589        } else {
590            // always fold
591            m_foldingThreshhold = 10000;
592        }
593        updateButtonVisibility(Page.getCurrent().getBrowserWindowWidth());
594    }
595
596    /**
597     * Returns the dialog context.<p>
598     *
599     * @return the dialog context
600     */
601    I_CmsDialogContext getDialogContext() {
602
603        return m_dialogContext;
604    }
605
606    /**
607     * Handles the user image file upload.<p>
608     *
609     * @param uploadedFiles the uploaded file names
610     */
611    void handleUpload(List<String> uploadedFiles) {
612
613        CmsObject cms = A_CmsUI.getCmsObject();
614        boolean success = OpenCms.getWorkplaceAppManager().getUserIconHelper().handleImageUpload(cms, uploadedFiles);
615        if (success) {
616            refreshUserInfoDropDown();
617        }
618    }
619
620    /**
621     * Closes the visible popup view children of the given layout.<p>
622     *
623     * @param layout the layout
624     */
625    private void closePopupViews(AbstractOrderedLayout layout) {
626
627        for (Component item : layout) {
628            if (item instanceof PopupView) {
629                ((PopupView)item).setPopupVisible(false);
630            }
631        }
632    }
633
634    /**
635     * Creates the context menu entry and it's children.<p>
636     *
637     * @param parent the entry parent
638     * @param node the item tree node
639     * @param treeBuilder the tree builder
640     */
641    private void createMenuEntry(
642        MenuItem parent,
643        final CmsTreeNode<I_CmsContextMenuItem> node,
644        CmsContextMenuTreeBuilder treeBuilder) {
645
646        Command entryCommand = null;
647        if (node.getChildren().size() == 0) {
648            entryCommand = new Command() {
649
650                private static final long serialVersionUID = 1L;
651
652                public void menuSelected(MenuItem selectedItem) {
653
654                    node.getData().executeAction(getDialogContext());
655                }
656            };
657        }
658        MenuItem entry = parent.addItem((node.getData().getTitle(A_CmsUI.get().getLocale())), entryCommand);
659        for (CmsTreeNode<I_CmsContextMenuItem> child : node.getChildren()) {
660            createMenuEntry(entry, child, treeBuilder);
661        }
662        if (treeBuilder.getVisibility(node.getData()).isInActive()) {
663            entry.setEnabled(false);
664        }
665    }
666
667    /**
668     * Creates the app select drop down.<p>
669     *
670     * @return the drop down component
671     */
672    private Component createQuickLaunchDropDown() {
673
674        PopupView pv = new PopupView(new PopupView.Content() {
675
676            private static final long serialVersionUID = 1L;
677
678            public String getMinimizedValueAsHTML() {
679
680                return getDropDownButtonHtml(FontOpenCms.APPS);
681            }
682
683            public Component getPopupComponent() {
684
685                CmsObject cms = A_CmsUI.getCmsObject();
686                Locale locale = UI.getCurrent().getLocale();
687                HorizontalLayout layout = new HorizontalLayout();
688                layout.addStyleName(ValoTheme.LAYOUT_HORIZONTAL_WRAPPING);
689                layout.addStyleName(OpenCmsTheme.QUICK_LAUNCH);
690                layout.setSpacing(false);
691                layout.setMargin(true);
692                for (I_CmsWorkplaceAppConfiguration config : OpenCms.getWorkplaceAppManager().getQuickLaunchConfigurations(
693                    cms)) {
694                    layout.addComponent(CmsDefaultAppButtonProvider.createAppButton(cms, config, locale));
695                }
696                return layout;
697            }
698        });
699        pv.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_QUICK_LAUNCH_TITLE_0));
700        pv.addStyleName(OpenCmsTheme.NAVIGATOR_DROPDOWN);
701        pv.setHideOnMouseOut(false);
702
703        return pv;
704
705    }
706
707    /**
708     * Creates the user info drop down.<p>
709     *
710     * @return the drop down component
711     */
712    private Component createUserInfoDropDown() {
713
714        PopupView pv = new PopupView(new PopupView.Content() {
715
716            private static final long serialVersionUID = 1L;
717
718            public String getMinimizedValueAsHTML() {
719
720                CmsObject cms = A_CmsUI.getCmsObject();
721                return getDropDownButtonHtml(
722                    new ExternalResource(
723                        OpenCms.getWorkplaceAppManager().getUserIconHelper().getSmallIconPath(
724                            cms,
725                            cms.getRequestContext().getCurrentUser())));
726            }
727
728            public Component getPopupComponent() {
729
730                return new CmsUserInfo(new I_UploadListener() {
731
732                    public void onUploadFinished(List<String> uploadedFiles) {
733
734                        handleUpload(uploadedFiles);
735                    }
736                }, getDialogContext());
737            }
738        });
739        pv.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_USER_INFO_TITLE_0));
740        pv.addStyleName(OpenCmsTheme.NAVIGATOR_DROPDOWN);
741        pv.setHideOnMouseOut(false);
742        pv.addStyleName(OpenCmsTheme.USER_INFO);
743        return pv;
744    }
745
746    /**
747     * Calculates the width required by the layout components.<p>
748     *
749     * @param items the layout
750     *
751     * @return the width
752     */
753    private int estimateRequiredWidth(AbstractOrderedLayout items) {
754
755        int result = 0;
756        if (items != null) {
757            for (Component comp : items) {
758                if (comp == m_foldedButtonsMenu) {
759                    continue;
760                } else if ((comp instanceof Button) || (comp instanceof PopupView) || (comp instanceof MenuBar)) {
761                    // assume all buttons have a with of 50px
762                    result += 50;
763                } else if (comp == m_appIndicator) {
764                    // assume app indicator requires 150px
765                    result += 50;
766                } else {
767                    float compWidth = comp.getWidth();
768                    if ((compWidth > 0) && (comp.getWidthUnits() == Unit.PIXELS)) {
769                        // also add 10px margin
770                        result += compWidth + 10;
771                    } else {
772                        result += 200;
773                    }
774                }
775            }
776        }
777        return result;
778    }
779
780    /**
781     * Folds the toolbar buttons into a sub menu.<p>
782     */
783    private void foldButtons() {
784
785        VerticalLayout mainPV = (VerticalLayout)m_foldedButtonsMenu.getContent().getPopupComponent();
786        for (int i = m_itemsLeft.getComponentCount() - 1; i > -1; i--) {
787            Component comp = m_itemsLeft.getComponent(i);
788            if (!isAlwaysShow(comp)) {
789                m_itemsLeft.removeComponent(comp);
790                m_leftButtons.addComponent(comp, 0);
791                m_leftButtons.setComponentAlignment(comp, Alignment.MIDDLE_CENTER);
792            }
793        }
794        if (m_leftButtons.getComponentCount() == 0) {
795            mainPV.removeComponent(m_leftButtons);
796        } else {
797            mainPV.addComponent(m_leftButtons, 0);
798        }
799        for (int i = m_itemsRight.getComponentCount() - 1; i > -1; i--) {
800            Component comp = m_itemsRight.getComponent(i);
801            if (!isAlwaysShow(comp)) {
802                m_itemsRight.removeComponent(comp);
803                m_rightButtons.addComponent(comp, 0);
804                m_rightButtons.setComponentAlignment(comp, Alignment.MIDDLE_CENTER);
805            }
806        }
807        if (m_rightButtons.getComponentCount() == 0) {
808            mainPV.removeComponent(m_rightButtons);
809        } else {
810            mainPV.addComponent(m_rightButtons);
811        }
812        m_itemsRight.addComponent(m_foldedButtonsMenu, 0);
813        m_buttonsFolded = true;
814        markAsDirtyRecursive();
815    }
816
817    /**
818     * Initializes the context menu entries.<p>
819     */
820    private void initContextMenu() {
821
822        m_contextMenu.removeItems();
823        MenuItem main = m_contextMenu.addItem("", null);
824        main.setIcon(FontOpenCms.CONTEXT_MENU);
825        main.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_MENU_TITLE_0));
826        CmsContextMenuTreeBuilder treeBuilder = new CmsContextMenuTreeBuilder(getDialogContext());
827        CmsTreeNode<I_CmsContextMenuItem> tree = treeBuilder.buildAll(
828            OpenCms.getWorkplaceAppManager().getMenuItemProvider().getMenuItems());
829        for (CmsTreeNode<I_CmsContextMenuItem> node : tree.getChildren()) {
830            createMenuEntry(main, node, treeBuilder);
831        }
832    }
833
834    /**
835     * Checks whether the given component may be placed into the buttons sub menu.<p>
836     *
837     * @param comp the component to check
838     *
839     * @return <code>true</code> in case the component should always be displayed in the toolbar
840     */
841    private boolean isAlwaysShow(Component comp) {
842
843        return ((comp == m_appIndicator)
844            || (comp == m_contextMenu)
845            || (comp == m_userDropDown)
846            || (comp == m_quickLaunchDropDown)
847            || comp.getStyleName().contains(OpenCmsTheme.REQUIRED_BUTTON));
848    }
849
850    /**
851     * Places the buttons formerly moved to the sub menu back into the toolbar.<p>
852     */
853    private void unfoldButtons() {
854
855        m_itemsRight.removeComponent(m_foldedButtonsMenu);
856        while (m_leftButtons.getComponentCount() > 0) {
857            Component comp = m_leftButtons.getComponent(0);
858            if (!isAlwaysShow(comp)) {
859                m_leftButtons.removeComponent(comp);
860                m_itemsLeft.addComponent(comp);
861            }
862        }
863        int index = 0;
864        while (m_rightButtons.getComponentCount() > 0) {
865            Component comp = m_rightButtons.getComponent(0);
866            m_rightButtons.removeComponent(comp);
867            m_itemsRight.addComponent(comp, index);
868            index++;
869        }
870        m_buttonsFolded = false;
871        markAsDirtyRecursive();
872    }
873}