Java tutorial
/* * Copyright (c) 2008-2016 Haulmont. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.haulmont.cuba.web; import com.google.common.collect.Lists; import com.haulmont.bali.datastruct.Pair; import com.haulmont.bali.util.ParamsMap; import com.haulmont.bali.util.Preconditions; import com.haulmont.cuba.client.ClientConfig; import com.haulmont.cuba.core.entity.Entity; import com.haulmont.cuba.core.global.ClientType; import com.haulmont.cuba.core.global.DevelopmentException; import com.haulmont.cuba.core.global.SilentException; import com.haulmont.cuba.core.global.UuidSource; import com.haulmont.cuba.gui.ComponentsHelper; import com.haulmont.cuba.gui.DialogParams; import com.haulmont.cuba.gui.ScreenHistorySupport; import com.haulmont.cuba.gui.WindowManager; import com.haulmont.cuba.gui.app.core.dev.LayoutAnalyzer; import com.haulmont.cuba.gui.app.core.dev.LayoutTip; import com.haulmont.cuba.gui.components.*; import com.haulmont.cuba.gui.components.Action.Status; import com.haulmont.cuba.gui.components.Component.BelongToFrame; import com.haulmont.cuba.gui.components.Component.Container; import com.haulmont.cuba.gui.components.DialogAction.Type; import com.haulmont.cuba.gui.components.Window; import com.haulmont.cuba.gui.components.Window.BeforeCloseWithCloseButtonEvent; import com.haulmont.cuba.gui.components.Window.BeforeCloseWithShortcutEvent; import com.haulmont.cuba.gui.components.actions.BaseAction; import com.haulmont.cuba.gui.components.mainwindow.AppWorkArea; import com.haulmont.cuba.gui.components.mainwindow.AppWorkArea.Mode; import com.haulmont.cuba.gui.components.mainwindow.FoldersPane; import com.haulmont.cuba.gui.components.mainwindow.TopLevelWindowAttachListener; import com.haulmont.cuba.gui.components.mainwindow.UserIndicator; import com.haulmont.cuba.gui.config.WindowConfig; import com.haulmont.cuba.gui.config.WindowInfo; import com.haulmont.cuba.gui.data.Datasource; import com.haulmont.cuba.gui.icons.CubaIcon; import com.haulmont.cuba.gui.icons.Icons; import com.haulmont.cuba.gui.theme.ThemeConstants; import com.haulmont.cuba.gui.xml.layout.LayoutLoaderConfig; import com.haulmont.cuba.security.app.UserSettingService; import com.haulmont.cuba.web.exception.ExceptionDialog; import com.haulmont.cuba.web.gui.WebWindow; import com.haulmont.cuba.web.gui.components.WebAbstractComponent; import com.haulmont.cuba.web.gui.components.WebButton; import com.haulmont.cuba.web.gui.components.WebComponentsHelper; import com.haulmont.cuba.web.gui.components.WebWrapperUtils; import com.haulmont.cuba.web.gui.components.mainwindow.WebAppWorkArea; import com.haulmont.cuba.web.sys.WindowBreadCrumbs; import com.haulmont.cuba.web.toolkit.ui.*; import com.vaadin.event.ShortcutAction; import com.vaadin.event.ShortcutListener; import com.vaadin.server.Page; import com.vaadin.shared.ui.BorderStyle; import com.vaadin.shared.ui.label.ContentMode; import com.vaadin.shared.ui.window.WindowMode; import com.vaadin.ui.*; import com.vaadin.ui.Button; import com.vaadin.ui.Component; import com.vaadin.ui.CssLayout; import com.vaadin.ui.Label; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import javax.annotation.Nullable; import javax.inject.Inject; import java.util.*; import static com.haulmont.cuba.gui.components.Component.AUTO_SIZE_PX; import static com.haulmont.cuba.gui.components.Frame.MessageType; import static com.haulmont.cuba.gui.components.Frame.NotificationType; import static com.haulmont.cuba.web.gui.components.WebComponentsHelper.convertNotificationType; import static com.vaadin.server.Sizeable.Unit; import static java.util.Collections.singletonMap; @org.springframework.stereotype.Component(WebWindowManager.NAME) @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class WebWindowManager extends WindowManager { public static final String NAME = "cuba_WebWindowManager"; public static final int HUMANIZED_NOTIFICATION_DELAY_MSEC = 3000; public static final int WARNING_NOTIFICATION_DELAY_MSEC = -1; private static final Logger log = LoggerFactory.getLogger(WebWindowManager.class); protected App app; protected AppUI ui; @Inject protected WebConfig webConfig; @Inject protected ClientConfig clientConfig; @Inject protected ScreenProfiler screenProfiler; @Inject protected Icons icons; @Inject protected UuidSource uuidSource; @Inject protected UserSettingService userSettingService; @Inject protected WindowConfig windowConfig; protected final Map<ComponentContainer, WindowBreadCrumbs> tabs = new HashMap<>(); protected final Map<WindowBreadCrumbs, Stack<Pair<Window, Integer>>> stacks = new HashMap<>(); protected final Map<Window, WindowOpenInfo> windowOpenMode = new LinkedHashMap<>(); protected final Map<Window, Integer> windows = new HashMap<>(); protected boolean disableSavingScreenHistory; protected ScreenHistorySupport screenHistorySupport; public WebWindowManager() { screenHistorySupport = new ScreenHistorySupport(); } public void setUi(AppUI ui) { this.ui = ui; this.app = ui.getApp(); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public App getApp() { return app; } @Override public Collection<Window> getOpenWindows() { return new ArrayList<>(windowOpenMode.keySet()); } @Override public Window openWindow(WindowInfo windowInfo, OpenType openType, Map<String, Object> params) { Window window = super.openWindow(windowInfo, openType, params); if (window != null) { screenProfiler.initProfilerMarkerForWindow(windowInfo.getId()); } return window; } @Override public Window.Lookup openLookup(WindowInfo windowInfo, Window.Lookup.Handler handler, OpenType openType, Map<String, Object> params) { Window.Lookup window = super.openLookup(windowInfo, handler, openType, params); if (window != null) { screenProfiler.initProfilerMarkerForWindow(windowInfo.getId()); } return window; } @Override public Window.Editor openEditor(WindowInfo windowInfo, Entity item, OpenType openType, Map<String, Object> params, Datasource parentDs) { Window.Editor window = super.openEditor(windowInfo, item, openType, params, parentDs); if (window != null) { screenProfiler.initProfilerMarkerForWindow(windowInfo.getId()); } return window; } @Override public void selectWindowTab(Window window) { WindowOpenInfo openInfo = windowOpenMode.get(window); if (openInfo != null) { OpenMode openMode = openInfo.getOpenMode(); if (openMode == OpenMode.NEW_TAB || openMode == OpenMode.NEW_WINDOW || openMode == OpenMode.THIS_TAB) { // show in tabsheet Layout layout = (Layout) openInfo.getData(); TabSheetBehaviour webTabsheet = getConfiguredWorkArea(createWorkAreaContext(window)) .getTabbedWindowContainer().getTabSheetBehaviour(); webTabsheet.setSelectedTab(layout); } } } @Override public void setWindowCaption(Window window, String caption, String description) { Window webWindow = window; if (window instanceof Window.Wrapper) { webWindow = ((Window.Wrapper) window).getWrappedWindow(); } window.setCaption(caption); window.setDebugId(description); WindowOpenInfo openInfo = windowOpenMode.get(webWindow); String formattedCaption; if (openInfo != null && (openInfo.getOpenMode() == OpenMode.NEW_TAB || openInfo.getOpenMode() == OpenMode.NEW_WINDOW || openInfo.getOpenMode() == OpenMode.THIS_TAB)) { formattedCaption = formatTabCaption(caption, description); } else { formattedCaption = formatTabDescription(caption, description); } if (openInfo != null) { if (openInfo.getOpenMode() == OpenMode.DIALOG) { com.vaadin.ui.Window dialog = (com.vaadin.ui.Window) openInfo.getData(); dialog.setCaption(formattedCaption); } else { if (getConfiguredWorkArea(createWorkAreaContext(window)).getMode() == Mode.SINGLE) { return; } Component tabContent = (Component) openInfo.getData(); if (tabContent == null) { return; } TabSheetBehaviour tabSheet = getConfiguredWorkArea(createWorkAreaContext(window)) .getTabbedWindowContainer().getTabSheetBehaviour(); String tabId = tabSheet.getTab(tabContent); if (tabId == null) { return; } tabSheet.setTabCaption(tabId, formattedCaption); String formattedDescription = formatTabDescription(caption, description); if (!Objects.equals(formattedDescription, formattedCaption)) { tabSheet.setTabDescription(tabId, formattedDescription); } else { tabSheet.setTabDescription(tabId, null); } } } } protected static class WindowOpenInfo { protected Window window; protected OpenMode openMode; protected Object data; public WindowOpenInfo(Window window, OpenMode openMode) { this.window = window; this.openMode = openMode; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public Window getWindow() { return window; } public OpenMode getOpenMode() { return openMode; } } protected ComponentContainer findTab(Integer hashCode) { Set<Map.Entry<ComponentContainer, WindowBreadCrumbs>> set = tabs.entrySet(); for (Map.Entry<ComponentContainer, WindowBreadCrumbs> entry : set) { Window currentWindow = entry.getValue().getCurrentWindow(); if (hashCode.equals(getWindowHashCode(currentWindow))) { return entry.getKey(); } } return null; } protected Stack<Pair<Window, Integer>> getStack(WindowBreadCrumbs breadCrumbs) { return stacks.get(breadCrumbs); } protected boolean hasModalWindow() { for (Map.Entry<Window, WindowOpenInfo> entry : windowOpenMode.entrySet()) { if (OpenMode.DIALOG == entry.getValue().getOpenMode() && BooleanUtils.isTrue(entry.getKey().getDialogOptions().getModal())) { return true; } } return false; } @Override protected void showWindow(final Window window, final String caption, OpenType type, boolean multipleOpen) { showWindow(window, caption, null, type, multipleOpen); } @Override protected void showWindow(final Window window, final String caption, final String description, OpenType type, final boolean multipleOpen) { OpenType targetOpenType = type.copy(); // for backward compatibility only copyDialogParamsToOpenType(targetOpenType); overrideOpenTypeParams(targetOpenType, window.getDialogOptions()); boolean forciblyDialog = false; if (targetOpenType.getOpenMode() != OpenMode.DIALOG && hasModalWindow()) { targetOpenType.setOpenMode(OpenMode.DIALOG); forciblyDialog = true; } if (targetOpenType.getOpenMode() == OpenMode.THIS_TAB && tabs.size() == 0) { targetOpenType.setOpenMode(OpenMode.NEW_TAB); } final WindowOpenInfo openInfo = new WindowOpenInfo(window, targetOpenType.getOpenMode()); Component component; window.setCaption(caption); window.setDescription(description); switch (targetOpenType.getOpenMode()) { case NEW_TAB: case NEW_WINDOW: final WebAppWorkArea workArea = getConfiguredWorkArea(createWorkAreaContext(window)); workArea.switchTo(AppWorkArea.State.WINDOW_CONTAINER); if (workArea.getMode() == Mode.SINGLE) { VerticalLayout mainLayout = workArea.getSingleWindowContainer(); if (mainLayout.iterator().hasNext()) { ComponentContainer oldLayout = (ComponentContainer) mainLayout.iterator().next(); WindowBreadCrumbs oldBreadCrumbs = tabs.get(oldLayout); if (oldBreadCrumbs != null) { Window oldWindow = oldBreadCrumbs.getCurrentWindow(); oldWindow.closeAndRun(MAIN_MENU_ACTION_ID, () -> showWindow(window, caption, description, OpenType.NEW_TAB, false)); return; } } } else { final Integer hashCode = getWindowHashCode(window); ComponentContainer tab = null; if (hashCode != null && !multipleOpen) { tab = findTab(hashCode); } ComponentContainer oldLayout = tab; final WindowBreadCrumbs oldBreadCrumbs = tabs.get(oldLayout); if (oldBreadCrumbs != null && windowOpenMode.containsKey(oldBreadCrumbs.getCurrentWindow().getFrame()) && !multipleOpen) { Window oldWindow = oldBreadCrumbs.getCurrentWindow(); selectWindowTab(((Window.Wrapper) oldBreadCrumbs.getCurrentWindow()).getWrappedWindow()); int tabPosition = -1; final TabSheetBehaviour tabSheet = workArea.getTabbedWindowContainer().getTabSheetBehaviour(); String tabId = tabSheet.getTab(tab); if (tabId != null) { tabPosition = tabSheet.getTabPosition(tabId); } final int finalTabPosition = tabPosition; oldWindow.closeAndRun(MAIN_MENU_ACTION_ID, () -> { showWindow(window, caption, description, OpenType.NEW_TAB, false); Window wrappedWindow = window; if (window instanceof Window.Wrapper) { wrappedWindow = ((Window.Wrapper) window).getWrappedWindow(); } if (finalTabPosition >= 0 && finalTabPosition < tabSheet.getComponentCount() - 1) { moveWindowTab(workArea, wrappedWindow, finalTabPosition); } }); return; } } component = showWindowNewTab(window, multipleOpen); break; case THIS_TAB: getConfiguredWorkArea(createWorkAreaContext(window)).switchTo(AppWorkArea.State.WINDOW_CONTAINER); component = showWindowThisTab(window, caption, description); break; case DIALOG: component = showWindowDialog(window, targetOpenType, forciblyDialog); break; default: throw new UnsupportedOperationException(); } openInfo.setData(component); if (window instanceof Window.Wrapper) { Window wrappedWindow = ((Window.Wrapper) window).getWrappedWindow(); windowOpenMode.put(wrappedWindow, openInfo); } else { windowOpenMode.put(window, openInfo); } afterShowWindow(window); } @Override public boolean isOpenAsNewTab(OpenType openType) { return super.isOpenAsNewTab(openType) || openType == OpenType.NEW_WINDOW; } /** * @param workArea Work area * @param window Window implementation (WebWindow) * @param position new tab position */ protected void moveWindowTab(WebAppWorkArea workArea, Window window, int position) { // move tab to TabSheetBehaviour tabSheet = workArea.getTabbedWindowContainer().getTabSheetBehaviour(); if (position >= 0 && position < tabSheet.getComponentCount()) { WindowOpenInfo openInfo = windowOpenMode.get(window); if (openInfo != null) { OpenMode openMode = openInfo.getOpenMode(); if (openMode == OpenMode.NEW_TAB || openMode == OpenMode.NEW_WINDOW || openMode == OpenMode.THIS_TAB) { // show in tabsheet Layout layout = (Layout) openInfo.getData(); tabSheet.moveTab(layout, position); tabSheet.setSelectedTab(layout); } } } } protected void moveFocus(TabSheetBehaviour tabSheet, String tabId) { //noinspection SuspiciousMethodCalls Window window = tabs.get(tabSheet.getTabComponent(tabId)).getCurrentWindow(); if (window != null) { boolean focused = false; String focusComponentId = window.getFocusComponent(); if (focusComponentId != null) { com.haulmont.cuba.gui.components.Component focusComponent = window.getComponent(focusComponentId); if (focusComponent != null && focusComponent.isEnabled() && focusComponent.isVisible()) { focusComponent.requestFocus(); focused = true; } } if (!focused && window instanceof Window.Wrapper) { Window.Wrapper wrapper = (Window.Wrapper) window; focused = ((WebWindow) wrapper.getWrappedWindow()).findAndFocusChildComponent(); if (!focused) { tabSheet.focus(); } } } } protected Component showWindowNewTab(final Window window, final boolean multipleOpen) { getDialogParams().reset(); final WindowBreadCrumbs breadCrumbs = createWindowBreadCrumbs(window); breadCrumbs.addListener(new WindowBreadCrumbs.Listener() { @Override public void windowClick(final Window window) { Runnable op = new Runnable() { @Override public void run() { Window currentWindow = breadCrumbs.getCurrentWindow(); if (currentWindow != null && window != currentWindow) { if (!isCloseWithCloseButtonPrevented(currentWindow)) { currentWindow.closeAndRun(Window.CLOSE_ACTION_ID, this); } } } }; op.run(); } }); breadCrumbs.addWindow(window); //noinspection UnnecessaryLocalVariable final Layout layout = createNewTabLayout(window, multipleOpen, breadCrumbs); return layout; } protected Layout createNewTabLayout(final Window window, final boolean multipleOpen, WindowBreadCrumbs breadCrumbs, Component... additionalComponents) { Layout layout = new CssLayout(); layout.setPrimaryStyleName("c-app-window-wrap"); layout.setSizeFull(); layout.addComponent(breadCrumbs); if (additionalComponents != null) { for (final Component c : additionalComponents) { layout.addComponent(c); } } final Component component = WebComponentsHelper.getComposition(window); component.setSizeFull(); layout.addComponent(component); WebAppWorkArea workArea = getConfiguredWorkArea(createWorkAreaContext(window)); if (workArea.getMode() == Mode.TABBED) { layout.addStyleName("c-app-tabbed-window"); TabSheetBehaviour tabSheet = workArea.getTabbedWindowContainer().getTabSheetBehaviour(); String tabId; Integer hashCode = getWindowHashCode(window); ComponentContainer tab = null; if (hashCode != null) { tab = findTab(hashCode); } if (tab != null && !multipleOpen) { tabSheet.replaceComponent(tab, layout); tabSheet.removeComponent(tab); tabs.put(layout, breadCrumbs); tabId = tabSheet.getTab(layout); } else { tabs.put(layout, breadCrumbs); tabId = "tab_" + uuidSource.createUuid(); tabSheet.addTab(layout, tabId); if (ui.isTestMode()) { String id = "tab_" + window.getId(); tabSheet.setTabTestId(tabId, ui.getTestIdManager().getTestId(id)); tabSheet.setTabCubaId(tabId, id); } } String windowContentSwitchMode = window.getContentSwitchMode().name(); ContentSwitchMode contentSwitchMode = ContentSwitchMode.valueOf(windowContentSwitchMode); tabSheet.setContentSwitchMode(tabId, contentSwitchMode); String formattedCaption = formatTabCaption(window.getCaption(), window.getDescription()); tabSheet.setTabCaption(tabId, formattedCaption); String formattedDescription = formatTabDescription(window.getCaption(), window.getDescription()); if (!Objects.equals(formattedCaption, formattedDescription)) { tabSheet.setTabDescription(tabId, formattedDescription); } else { tabSheet.setTabDescription(tabId, null); } tabSheet.setTabIcon(tabId, WebComponentsHelper.getIcon(window.getIcon())); tabSheet.setTabClosable(tabId, true); tabSheet.setTabCloseHandler(layout, (targetTabSheet, tabContent) -> { //noinspection SuspiciousMethodCalls WindowBreadCrumbs breadCrumbs1 = tabs.get(tabContent); if (!canWindowBeClosed(breadCrumbs1.getCurrentWindow())) { return; } Runnable closeTask = new TabCloseTask(breadCrumbs1); closeTask.run(); // it is needed to force redraw tabsheet if it has a lot of tabs and part of them are hidden targetTabSheet.markAsDirty(); }); tabSheet.setSelectedTab(layout); } else { tabs.put(layout, breadCrumbs); layout.addStyleName("c-app-single-window"); VerticalLayout mainLayout = workArea.getSingleWindowContainer(); mainLayout.removeAllComponents(); mainLayout.addComponent(layout); } return layout; } public class TabCloseTask implements Runnable { private final WindowBreadCrumbs breadCrumbs; public TabCloseTask(WindowBreadCrumbs breadCrumbs) { this.breadCrumbs = breadCrumbs; } @Override public void run() { Window windowToClose = breadCrumbs.getCurrentWindow(); if (windowToClose != null) { if (!isCloseWithCloseButtonPrevented(windowToClose)) { windowToClose.closeAndRun(Window.CLOSE_ACTION_ID, new TabCloseTask(breadCrumbs)); } } } } protected String formatTabCaption(final String caption, final String description) { String s = formatTabDescription(caption, description); int maxLength = webConfig.getMainTabCaptionLength(); if (s.length() > maxLength) { return s.substring(0, maxLength) + "..."; } else { return s; } } protected String formatTabDescription(final String caption, final String description) { if (!StringUtils.isEmpty(description)) { return String.format("%s: %s", caption, description); } else { return caption; } } protected Component showWindowThisTab(final Window window, final String caption, final String description) { getDialogParams().reset(); WebAppWorkArea workArea = getConfiguredWorkArea(createWorkAreaContext(window)); Layout layout; if (workArea.getMode() == Mode.TABBED) { TabSheetBehaviour tabSheet = workArea.getTabbedWindowContainer().getTabSheetBehaviour(); layout = (Layout) tabSheet.getSelectedTab(); } else { layout = (Layout) workArea.getSingleWindowContainer().getComponent(0); } final WindowBreadCrumbs breadCrumbs = tabs.get(layout); if (breadCrumbs == null) { throw new IllegalStateException("BreadCrumbs not found"); } final Window currentWindow = breadCrumbs.getCurrentWindow(); Set<Map.Entry<Window, Integer>> set = windows.entrySet(); boolean pushed = false; for (Map.Entry<Window, Integer> entry : set) { if (entry.getKey().equals(currentWindow)) { windows.remove(currentWindow); getStack(breadCrumbs).push(new Pair<>(entry.getKey(), entry.getValue())); pushed = true; break; } } if (!pushed) { getStack(breadCrumbs).push(new Pair<>(currentWindow, null)); } removeFromWindowMap(currentWindow); layout.removeComponent(WebComponentsHelper.getComposition(currentWindow)); final Component component = WebComponentsHelper.getComposition(window); component.setSizeFull(); layout.addComponent(component); breadCrumbs.addWindow(window); if (workArea.getMode() == Mode.TABBED) { TabSheetBehaviour tabSheet = workArea.getTabbedWindowContainer().getTabSheetBehaviour(); String tabId = tabSheet.getTab(layout); String formattedCaption = formatTabCaption(caption, description); tabSheet.setTabCaption(tabId, formattedCaption); String formattedDescription = formatTabDescription(caption, description); if (!Objects.equals(formattedCaption, formattedDescription)) { tabSheet.setTabDescription(tabId, formattedDescription); } else { tabSheet.setTabDescription(tabId, null); } tabSheet.setTabIcon(tabId, WebComponentsHelper.getIcon(window.getIcon())); ContentSwitchMode contentSwitchMode = ContentSwitchMode.valueOf(window.getContentSwitchMode().name()); tabSheet.setContentSwitchMode(tabId, contentSwitchMode); } else { layout.markAsDirtyRecursive(); } return layout; } protected WebAppWorkArea getConfiguredWorkArea(@Nullable WorkAreaContext workAreaContext) { Window.TopLevelWindow topLevelWindow = ui.getTopLevelWindow(); if (topLevelWindow instanceof Window.MainWindow) { AppWorkArea workArea = ((Window.MainWindow) topLevelWindow).getWorkArea(); if (workArea != null) { return (WebAppWorkArea) workArea; } } throw new IllegalStateException("Application does not have any configured work area"); } protected Component showWindowDialog(Window window, OpenType openType, boolean forciblyDialog) { final CubaWindow vWindow = createDialogWindow(window); vWindow.setStyleName("c-app-dialog-window"); if (ui.isTestMode()) { vWindow.setCubaId("dialog_" + window.getId()); vWindow.setId(ui.getTestIdManager().getTestId("dialog_" + window.getId())); } Layout layout = (Layout) WebComponentsHelper.getComposition(window); vWindow.setContent(layout); vWindow.addPreCloseListener(event -> { event.setPreventClose(true); if (!isCloseWithCloseButtonPrevented(window)) { // user has clicked on X window.close(Window.CLOSE_ACTION_ID); } }); String closeShortcut = clientConfig.getCloseShortcut(); KeyCombination closeCombination = KeyCombination.create(closeShortcut); ShortcutAction exitAction = new ShortcutAction("closeShortcutAction", closeCombination.getKey().getCode(), KeyCombination.Modifier.codes(closeCombination.getModifiers())); Map<com.vaadin.event.Action, Runnable> actions = singletonMap(exitAction, () -> { if (openType.getOpenMode() != OpenMode.DIALOG || BooleanUtils.isNotFalse(window.getDialogOptions().getCloseable())) { if (isCloseWithShortcutPrevented(window)) { return; } window.close(Window.CLOSE_ACTION_ID); } }); WebComponentsHelper.setActions(vWindow, actions); boolean dialogParamsSizeUndefined = openType.getHeight() == null && openType.getWidth() == null; ThemeConstants theme = app.getThemeConstants(); if (forciblyDialog && dialogParamsSizeUndefined) { layout.setHeight(100, Unit.PERCENTAGE); vWindow.setWidth(theme.getInt("cuba.web.WebWindowManager.forciblyDialog.width"), Unit.PIXELS); vWindow.setHeight(theme.getInt("cuba.web.WebWindowManager.forciblyDialog.height"), Unit.PIXELS); // resizable by default, but may be overridden in dialog params vWindow.setResizable(BooleanUtils.isNotFalse(openType.getResizable())); window.setHeightFull(); } else { if (openType.getWidth() == null) { vWindow.setWidth(theme.getInt("cuba.web.WebWindowManager.dialog.width"), Unit.PIXELS); } else if (openType.getWidth() == AUTO_SIZE_PX) { vWindow.setWidthUndefined(); layout.setWidthUndefined(); window.setWidthAuto(); } else { vWindow.setWidth(openType.getWidth(), openType.getWidthUnit() != null ? WebWrapperUtils.toVaadinUnit(openType.getWidthUnit()) : Unit.PIXELS); } if (openType.getHeight() != null && openType.getHeight() != AUTO_SIZE_PX) { vWindow.setHeight(openType.getHeight(), openType.getHeightUnit() != null ? WebWrapperUtils.toVaadinUnit(openType.getHeightUnit()) : Unit.PIXELS); layout.setHeight("100%"); window.setHeightFull(); } else { window.setHeightAuto(); } // non resizable by default vWindow.setResizable(BooleanUtils.isTrue(openType.getResizable())); } if (openType.getCloseable() != null) { vWindow.setClosable(openType.getCloseable()); } boolean modal = true; if (!hasModalWindow() && openType.getModal() != null) { modal = openType.getModal(); } vWindow.setModal(modal); if (vWindow.isModal()) { boolean informationDialog = false; if (openType.getCloseOnClickOutside() != null) { informationDialog = openType.getCloseOnClickOutside(); } vWindow.setCloseOnClickOutside(informationDialog); } if (openType.getMaximized() != null) { if (openType.getMaximized()) { vWindow.setWindowMode(WindowMode.MAXIMIZED); } else { vWindow.setWindowMode(WindowMode.NORMAL); } } if (openType.getPositionX() == null && openType.getPositionY() == null) { vWindow.center(); } else { if (openType.getPositionX() != null) { vWindow.setPositionX(openType.getPositionX()); } if (openType.getPositionY() != null) { vWindow.setPositionY(openType.getPositionY()); } } getDialogParams().reset(); ui.addWindow(vWindow); return vWindow; } protected WindowBreadCrumbs createWindowBreadCrumbs(Window window) { WebAppWorkArea appWorkArea = getConfiguredWorkArea(createWorkAreaContext(window)); WindowBreadCrumbs windowBreadCrumbs = new WindowBreadCrumbs(appWorkArea); boolean showBreadCrumbs = webConfig.getShowBreadCrumbs() || Mode.SINGLE == appWorkArea.getMode(); windowBreadCrumbs.setVisible(showBreadCrumbs); stacks.put(windowBreadCrumbs, new Stack<>()); return windowBreadCrumbs; } protected CubaWindow createDialogWindow(Window window) { CubaWindow dialogWindow = new CubaWindow(window.getCaption()); if (window.getIcon() != null) { dialogWindow.setIcon(WebComponentsHelper.getIcon(window.getIcon())); } dialogWindow.setErrorHandler(ui); dialogWindow.addContextActionHandler(new DialogWindowActionHandler(window)); return dialogWindow; } @Override public void close(Window window) { if (window instanceof Window.Wrapper) { window = ((Window.Wrapper) window).getWrappedWindow(); } WindowOpenInfo openInfo = windowOpenMode.get(window); if (openInfo == null) { log.warn("Problem closing window {}: WindowOpenMode not found", window); return; } disableSavingScreenHistory = false; closeWindow(window, openInfo); windowOpenMode.remove(window); removeFromWindowMap(openInfo.getWindow()); } /** * Check modifications and close all screens in all main windows. * * @param runIfOk a closure to run after all screens are closed */ public void checkModificationsAndCloseAll(Runnable runIfOk) { checkModificationsAndCloseAll(runIfOk, null); } /** * Check modifications and close all screens in all main windows. * * @param runIfOk a closure to run after all screens are closed * @param runIfCancel a closure to run if there were modifications and a user canceled the operation */ public void checkModificationsAndCloseAll(final Runnable runIfOk, final @Nullable Runnable runIfCancel) { boolean modified = false; for (Window window : getOpenWindows()) { if (!disableSavingScreenHistory) { screenHistorySupport.saveScreenHistory(window, windowOpenMode.get(window).getOpenMode()); } if (window instanceof WrappedWindow && ((WrappedWindow) window).getWrapper() != null) { ((WrappedWindow) window).getWrapper().saveSettings(); } else { window.saveSettings(); } if (window.getDsContext() != null && window.getDsContext().isModified()) { modified = true; } } disableSavingScreenHistory = true; if (modified) { showOptionDialog(messages.getMainMessage("closeUnsaved.caption"), messages.getMainMessage("discardChangesOnClose"), MessageType.WARNING, new Action[] { new BaseAction("closeApplication") .withCaption(messages.getMainMessage("closeApplication")) .withIcon(icons.get(CubaIcon.DIALOG_OK)).withHandler(event -> { closeAllWindows(); runIfOk.run(); }), new DialogAction(Type.CANCEL, Status.PRIMARY).withHandler(event -> { if (runIfCancel != null) { runIfCancel.run(); } }) }); } else { closeAllWindows(); runIfOk.run(); } } public void closeAllTabbedWindows() { closeAllTabbedWindowsExcept(null); } public void closeAllTabbedWindowsExcept(@Nullable ComponentContainer keepOpened) { boolean modified = false; List<WebWindow> windowsToClose = new ArrayList<>(); WindowBreadCrumbs keepOpenedCrumbs = tabs.get(keepOpened); Frame keepOpenedFrame = keepOpenedCrumbs != null ? keepOpenedCrumbs.getCurrentWindow().getFrame() : null; for (Window window : getOpenWindows()) { if (!canWindowBeClosed(window)) { continue; } OpenMode openMode = windowOpenMode.get(window).getOpenMode(); WindowBreadCrumbs windowBreadCrumbs = tabs.get(windowOpenMode.get(window).getData()); Window currentWindow = (windowBreadCrumbs != null && window != windowBreadCrumbs.getCurrentWindow()) ? windowBreadCrumbs.getCurrentWindow() : window; if (window.getFrame() == keepOpenedFrame || openMode == OpenMode.DIALOG || keepOpenedCrumbs == windowBreadCrumbs || isCloseWithCloseButtonPrevented(currentWindow)) continue; if (window.getDsContext() != null && window.getDsContext().isModified()) { modified = true; } windowsToClose.add((WebWindow) window); } disableSavingScreenHistory = true; if (modified) { showOptionDialog(messages.getMainMessage("closeUnsaved.caption"), messages.getMainMessage("discardChangesInTabs"), MessageType.WARNING, new Action[] { new AbstractAction(messages.getMainMessage("closeTabs")) { { icon = icons.get(CubaIcon.DIALOG_OK); } @Override public void actionPerform(com.haulmont.cuba.gui.components.Component component) { closeTabsForce(windowsToClose); } }, new DialogAction(Type.CANCEL, Status.PRIMARY) }); } else { closeTabsForce(windowsToClose); } } protected void closeTabsForce(List<WebWindow> windowsToClose) { windowsToClose = Lists.reverse(windowsToClose); for (WebWindow window : windowsToClose) { window.close(Window.CLOSE_ACTION_ID, true); } } /** * Close all screens in all main windows (browser tabs). */ public void closeAllWindows() { app.cleanupBackgroundTasks(); app.closeAllWindows(); } /** * Close all screens in the main window (browser tab) this WindowManager belongs to. */ public void closeAll() { List<Map.Entry<Window, WindowOpenInfo>> entries = new ArrayList<>(windowOpenMode.entrySet()); for (int i = entries.size() - 1; i >= 0; i--) { WebWindow window = (WebWindow) entries.get(i).getKey(); if (window instanceof WebWindow.Editor) { ((WebWindow.Editor) window).releaseLock(); } closeWindow(window, entries.get(i).getValue()); } disableSavingScreenHistory = false; windowOpenMode.clear(); windows.clear(); } protected void closeWindow(Window window, WindowOpenInfo openInfo) { if (!disableSavingScreenHistory) { screenHistorySupport.saveScreenHistory(window, openInfo.getOpenMode()); } WebWindow webWindow = (WebWindow) window; webWindow.stopTimers(); switch (openInfo.getOpenMode()) { case DIALOG: { final CubaWindow cubaDialogWindow = (CubaWindow) openInfo.getData(); cubaDialogWindow.forceClose(); fireListeners(window, tabs.size() != 0); break; } case NEW_WINDOW: case NEW_TAB: { final Layout layout = (Layout) openInfo.getData(); layout.removeComponent(WebComponentsHelper.getComposition(window)); WebAppWorkArea workArea = getConfiguredWorkArea(createWorkAreaContext(window)); if (workArea.getMode() == Mode.TABBED) { TabSheetBehaviour tabSheet = workArea.getTabbedWindowContainer().getTabSheetBehaviour(); tabSheet.silentCloseTabAndSelectPrevious(layout); tabSheet.removeComponent(layout); } else { VerticalLayout singleLayout = workArea.getSingleWindowContainer(); singleLayout.removeComponent(layout); } WindowBreadCrumbs windowBreadCrumbs = tabs.get(layout); if (windowBreadCrumbs != null) { windowBreadCrumbs.clearListeners(); windowBreadCrumbs.removeWindow(); } tabs.remove(layout); stacks.remove(windowBreadCrumbs); fireListeners(window, !tabs.isEmpty()); if (tabs.isEmpty() && app.getConnection().isConnected()) { workArea.switchTo(AppWorkArea.State.INITIAL_LAYOUT); } break; } case THIS_TAB: { final Layout layout = (Layout) openInfo.getData(); final WindowBreadCrumbs breadCrumbs = tabs.get(layout); if (breadCrumbs == null) { throw new IllegalStateException("Unable to close screen: breadCrumbs not found"); } breadCrumbs.removeWindow(); Window currentWindow = breadCrumbs.getCurrentWindow(); if (!getStack(breadCrumbs).empty()) { Pair<Window, Integer> entry = getStack(breadCrumbs).pop(); putToWindowMap(entry.getFirst(), entry.getSecond()); } Component component = WebComponentsHelper.getComposition(currentWindow); component.setSizeFull(); WebAppWorkArea workArea = getConfiguredWorkArea(createWorkAreaContext(window)); layout.removeComponent(WebComponentsHelper.getComposition(window)); if (app.getConnection().isConnected()) { layout.addComponent(component); if (workArea.getMode() == Mode.TABBED) { TabSheetBehaviour tabSheet = workArea.getTabbedWindowContainer().getTabSheetBehaviour(); String tabId = tabSheet.getTab(layout); String formattedCaption = formatTabCaption(currentWindow.getCaption(), currentWindow.getDescription()); tabSheet.setTabCaption(tabId, formattedCaption); String formattedDescription = formatTabDescription(currentWindow.getCaption(), currentWindow.getDescription()); if (!Objects.equals(formattedCaption, formattedDescription)) { tabSheet.setTabDescription(tabId, formattedDescription); } else { tabSheet.setTabDescription(tabId, null); } tabSheet.setTabIcon(tabId, WebComponentsHelper.getIcon(currentWindow.getIcon())); ContentSwitchMode contentSwitchMode = ContentSwitchMode .valueOf(currentWindow.getContentSwitchMode().name()); tabSheet.setContentSwitchMode(tabId, contentSwitchMode); } } fireListeners(window, !tabs.isEmpty()); if (tabs.isEmpty() && app.getConnection().isConnected()) { workArea.switchTo(AppWorkArea.State.INITIAL_LAYOUT); } break; } default: { throw new UnsupportedOperationException(); } } } @Override public void showFrame(com.haulmont.cuba.gui.components.Component parent, Frame frame) { if (parent instanceof Container) { Container container = (Container) parent; for (com.haulmont.cuba.gui.components.Component c : container.getComponents()) { if (c instanceof com.haulmont.cuba.gui.components.Component.Disposable) { com.haulmont.cuba.gui.components.Component.Disposable disposable = (com.haulmont.cuba.gui.components.Component.Disposable) c; if (!disposable.isDisposed()) { disposable.dispose(); } } container.remove(c); } container.add(frame); } else { throw new IllegalStateException( "Parent component must be com.haulmont.cuba.gui.components.Component.Container"); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public void showNotification(String caption) { showNotification(caption, null, NotificationType.HUMANIZED); } @Override public void showNotification(String caption, NotificationType type) { showNotification(caption, null, type); } @Override public void showNotification(String caption, String description, NotificationType type) { backgroundWorker.checkUIAccess(); Notification notification = new Notification(caption, description, convertNotificationType(type)); notification.setHtmlContentAllowed(NotificationType.isHTML(type)); setNotificationDelayMsec(notification, type); notification.show(Page.getCurrent()); } protected void setNotificationDelayMsec(Notification notification, NotificationType type) { switch (type) { case HUMANIZED: case HUMANIZED_HTML: notification.setDelayMsec(HUMANIZED_NOTIFICATION_DELAY_MSEC); break; case WARNING: case WARNING_HTML: notification.setDelayMsec(WARNING_NOTIFICATION_DELAY_MSEC); break; } } @Override public void showMessageDialog(String title, String message, MessageType messageType) { backgroundWorker.checkUIAccess(); final com.vaadin.ui.Window vWindow = new CubaWindow(title); if (ui.isTestMode()) { vWindow.setCubaId("messageDialog"); vWindow.setId(ui.getTestIdManager().getTestId("messageDialog")); } String closeShortcut = clientConfig.getCloseShortcut(); KeyCombination closeCombination = KeyCombination.create(closeShortcut); vWindow.addAction(new ShortcutListener("Esc", closeCombination.getKey().getCode(), KeyCombination.Modifier.codes(closeCombination.getModifiers())) { @Override public void handleAction(Object sender, Object target) { vWindow.close(); } }); vWindow.addAction(new ShortcutListener("Enter", ShortcutAction.KeyCode.ENTER, null) { @Override public void handleAction(Object sender, Object target) { vWindow.close(); } }); VerticalLayout layout = new VerticalLayout(); layout.setStyleName("c-app-message-dialog"); layout.setSpacing(true); if (messageType.getWidth() != null && messageType.getWidth() == AUTO_SIZE_PX) { layout.setWidthUndefined(); } vWindow.setContent(layout); Label messageLab = new CubaLabel(); messageLab.setValue(message); if (MessageType.isHTML(messageType)) { messageLab.setContentMode(ContentMode.HTML); } else { messageLab.setContentMode(ContentMode.TEXT); } if (messageType.getWidth() != null && messageType.getWidth() == AUTO_SIZE_PX) { messageLab.setWidthUndefined(); } layout.addComponent(messageLab); DialogAction action = new DialogAction(Type.OK); Button button = WebComponentsHelper.createButton(); button.setCaption(action.getCaption()); button.setIcon(WebComponentsHelper.getIcon(action.getIcon())); button.addStyleName(WebButton.ICON_STYLE); button.addClickListener(event -> vWindow.close()); button.focus(); layout.addComponent(button); layout.setComponentAlignment(button, Alignment.BOTTOM_RIGHT); float width; SizeUnit unit; DialogParams dialogParams = getDialogParams(); if (messageType.getWidth() != null) { width = messageType.getWidth(); unit = messageType.getWidthUnit(); } else if (dialogParams.getWidth() != null) { width = dialogParams.getWidth(); unit = dialogParams.getWidthUnit(); } else { SizeWithUnit size = SizeWithUnit .parseStringSize(app.getThemeConstants().get("cuba.web.WebWindowManager.messageDialog.width")); width = size.getSize(); unit = size.getUnit(); } vWindow.setWidth(width, unit != null ? WebWrapperUtils.toVaadinUnit(unit) : Unit.PIXELS); vWindow.setResizable(false); boolean modal = true; if (!hasModalWindow()) { if (messageType.getModal() != null) { modal = messageType.getModal(); } else if (dialogParams.getModal() != null) { modal = dialogParams.getModal(); } } vWindow.setModal(modal); boolean closeOnClickOutside = false; if (vWindow.isModal()) { if (messageType.getCloseOnClickOutside() != null) { closeOnClickOutside = messageType.getCloseOnClickOutside(); } } ((CubaWindow) vWindow).setCloseOnClickOutside(closeOnClickOutside); if (messageType.getMaximized() != null) { if (messageType.getMaximized()) { vWindow.setWindowMode(WindowMode.MAXIMIZED); } else { vWindow.setWindowMode(WindowMode.NORMAL); } } dialogParams.reset(); ui.addWindow(vWindow); vWindow.center(); vWindow.focus(); } @Override public void showOptionDialog(String title, String message, MessageType messageType, Action[] actions) { backgroundWorker.checkUIAccess(); final com.vaadin.ui.Window window = new CubaWindow(title); if (ui.isTestMode()) { window.setCubaId("optionDialog"); window.setId(ui.getTestIdManager().getTestId("optionDialog")); } window.setClosable(false); Label messageLab = new CubaLabel(); messageLab.setValue(message); if (MessageType.isHTML(messageType)) { messageLab.setContentMode(ContentMode.HTML); } else { messageLab.setContentMode(ContentMode.TEXT); } if (messageType.getWidth() != null && messageType.getWidth() == AUTO_SIZE_PX) { messageLab.setWidthUndefined(); } float width; SizeUnit unit; if (messageType.getWidth() != null) { width = messageType.getWidth(); unit = messageType.getWidthUnit(); } else if (getDialogParams().getWidth() != null) { width = getDialogParams().getWidth(); unit = getDialogParams().getWidthUnit(); } else { SizeWithUnit size = SizeWithUnit .parseStringSize(app.getThemeConstants().get("cuba.web.WebWindowManager.optionDialog.width")); width = size.getSize(); unit = size.getUnit(); } if (messageType.getModal() != null) { log.warn("MessageType.modal is not supported for showOptionDialog"); } getDialogParams().reset(); window.setWidth(width, unit != null ? WebWrapperUtils.toVaadinUnit(unit) : Unit.PIXELS); window.setResizable(false); window.setModal(true); boolean closeOnClickOutside = false; if (window.isModal()) { if (messageType.getCloseOnClickOutside() != null) { closeOnClickOutside = messageType.getCloseOnClickOutside(); } } ((CubaWindow) window).setCloseOnClickOutside(closeOnClickOutside); if (messageType.getMaximized() != null) { if (messageType.getMaximized()) { window.setWindowMode(WindowMode.MAXIMIZED); } else { window.setWindowMode(WindowMode.NORMAL); } } VerticalLayout layout = new VerticalLayout(); layout.setStyleName("c-app-option-dialog"); layout.setSpacing(true); if (messageType.getWidth() != null && messageType.getWidth() == AUTO_SIZE_PX) { layout.setWidthUndefined(); } window.setContent(layout); HorizontalLayout buttonsContainer = new HorizontalLayout(); buttonsContainer.setSpacing(true); boolean hasPrimaryAction = false; Map<Action, Button> buttonMap = new HashMap<>(); for (Action action : actions) { Button button = WebComponentsHelper.createButton(); button.setCaption(action.getCaption()); button.addClickListener(event -> { try { action.actionPerform(null); } finally { ui.removeWindow(window); } }); if (StringUtils.isNotEmpty(action.getIcon())) { button.setIcon(WebComponentsHelper.getIcon(action.getIcon())); button.addStyleName(WebButton.ICON_STYLE); } if (action instanceof AbstractAction && ((AbstractAction) action).isPrimary()) { button.addStyleName("c-primary-action"); button.focus(); hasPrimaryAction = true; } if (ui.isTestMode()) { button.setCubaId("optionDialog_" + action.getId()); button.setId(ui.getTestIdManager().getTestId("optionDialog_" + action.getId())); } buttonsContainer.addComponent(button); buttonMap.put(action, button); } assignDialogShortcuts(buttonMap); if (!hasPrimaryAction && actions.length > 0) { ((Button) buttonsContainer.getComponent(0)).focus(); } layout.addComponent(messageLab); layout.addComponent(buttonsContainer); layout.setExpandRatio(messageLab, 1); layout.setComponentAlignment(buttonsContainer, Alignment.BOTTOM_RIGHT); ui.addWindow(window); window.center(); } @Override public void showExceptionDialog(Throwable throwable) { showExceptionDialog(throwable, null, null); } @Override public void showExceptionDialog(Throwable throwable, @Nullable String caption, @Nullable String message) { Preconditions.checkNotNullArgument(throwable); Throwable rootCause = ExceptionUtils.getRootCause(throwable); if (rootCause == null) { rootCause = throwable; } ExceptionDialog dialog = new ExceptionDialog(rootCause, caption, message); for (com.vaadin.ui.Window window : ui.getWindows()) { if (window.isModal()) { dialog.setModal(true); break; } } ui.addWindow(dialog); dialog.focus(); } public WindowBreadCrumbs getBreadCrumbs(ComponentContainer container) { return tabs.get(container); } protected void assignDialogShortcuts(Map<Action, Button> buttonMap) { List<DialogAction> dialogActions = new ArrayList<>(); for (Action action : buttonMap.keySet()) { if (action instanceof DialogAction) { dialogActions.add((DialogAction) action); } } // find action for commit shortcut Action firstOkAction = dialogActions.stream().filter(action -> action.getType() == Type.OK).findFirst() .orElse(null); if (firstOkAction == null) { firstOkAction = dialogActions.stream().filter(action -> action.getType() == Type.YES).findFirst() .orElse(null); } if (firstOkAction != null) { WebComponentsHelper.setClickShortcut(buttonMap.get(firstOkAction), clientConfig.getCommitShortcut()); } // find action for close shortcut Action firstCancelAction = dialogActions.stream().filter(action -> action.getType() == Type.CANCEL) .findFirst().orElse(null); if (firstCancelAction == null) { firstCancelAction = dialogActions.stream().filter(action -> action.getType() == Type.CLOSE).findFirst() .orElse(null); if (firstCancelAction == null) { firstCancelAction = dialogActions.stream().filter(action -> action.getType() == Type.NO).findFirst() .orElse(null); } } if (firstCancelAction != null) { WebComponentsHelper.setClickShortcut(buttonMap.get(firstCancelAction), clientConfig.getCloseShortcut()); } } @Override public void showWebPage(String url, @Nullable Map<String, Object> params) { String target = null; Integer width = null; Integer height = null; String border = "DEFAULT"; Boolean tryToOpenAsPopup = null; if (params != null) { target = (String) params.get("target"); width = (Integer) params.get("width"); height = (Integer) params.get("height"); border = (String) params.get("border"); tryToOpenAsPopup = (Boolean) params.get("tryToOpenAsPopup"); } if (target == null) { target = "_blank"; } if (width != null && height != null && border != null) { ui.getPage().open(url, target, width, height, BorderStyle.valueOf(border)); } else if (tryToOpenAsPopup != null) { ui.getPage().open(url, target, tryToOpenAsPopup); } else { ui.getPage().open(url, target, false); } } @Override public void initDebugIds(final Frame frame) { if (ui.isTestMode()) { ComponentsHelper.walkComponents(frame, (component, name) -> { if (component.getDebugId() == null) { Frame componentFrame = null; if (component instanceof BelongToFrame) { componentFrame = ((BelongToFrame) component).getFrame(); } if (componentFrame == null) { log.warn("Frame for component {} is not assigned", component.getClass()); } else { if (component instanceof WebAbstractComponent) { WebAbstractComponent webComponent = (WebAbstractComponent) component; webComponent.assignAutoDebugId(); } } } }); } } @Override protected void putToWindowMap(Window window, Integer hashCode) { if (window != null) { windows.put(window, hashCode); } } protected void removeFromWindowMap(Window window) { windows.remove(window); } protected Integer getWindowHashCode(Window window) { return windows.get(window); } @Override protected Window getWindow(Integer hashCode) { AppWorkArea workArea = getConfiguredWorkArea(null); if (workArea == null || workArea.getMode() == Mode.SINGLE) { return null; } for (Map.Entry<Window, Integer> entry : windows.entrySet()) { if (hashCode.equals(entry.getValue())) { return entry.getKey(); } } return null; } @Override protected void checkCanOpenWindow(WindowInfo windowInfo, OpenType openType, Map<String, Object> params) { if (openType.getOpenMode() != OpenMode.NEW_TAB && openType.getOpenMode() != OpenMode.NEW_WINDOW) { return; } if (!windowInfo.getMultipleOpen() && getWindow(getHash(windowInfo, params)) != null) { //window is already open return; } int maxCount = webConfig.getMaxTabCount(); if (maxCount > 0 && maxCount <= tabs.size()) { Notification notification = new Notification( messages.formatMainMessage("tooManyOpenTabs.message", maxCount), Notification.Type.WARNING_MESSAGE); notification.setDelayMsec(WARNING_NOTIFICATION_DELAY_MSEC); notification.show(ui.getPage()); throw new SilentException(); } } public void createTopLevelWindow(WindowInfo windowInfo) { ui.beforeTopLevelWindowInit(); String template = windowInfo.getTemplate(); Window.TopLevelWindow topLevelWindow; Map<String, Object> params = Collections.emptyMap(); if (template != null) { //noinspection unchecked topLevelWindow = (Window.TopLevelWindow) createWindow(windowInfo, OpenType.NEW_TAB, params, LayoutLoaderConfig.getWindowLoaders(), true); } else { Class screenClass = windowInfo.getScreenClass(); if (screenClass != null) { //noinspection unchecked topLevelWindow = (Window.TopLevelWindow) createWindowByScreenClass(windowInfo, params); } else { throw new DevelopmentException("Unable to load top level window"); } } // detect work area Window windowImpl = ((Window.Wrapper) topLevelWindow).getWrappedWindow(); if (topLevelWindow instanceof AbstractMainWindow) { AbstractMainWindow mainWindow = (AbstractMainWindow) topLevelWindow; // bind system UI components to AbstractMainWindow ComponentsHelper.walkComponents(windowImpl, component -> { if (component instanceof AppWorkArea) { mainWindow.setWorkArea((AppWorkArea) component); } else if (component instanceof UserIndicator) { mainWindow.setUserIndicator((UserIndicator) component); } else if (component instanceof FoldersPane) { mainWindow.setFoldersPane((FoldersPane) component); } return false; }); } ui.setTopLevelWindow(topLevelWindow); // load menu ComponentsHelper.walkComponents(windowImpl, component -> { if (component instanceof TopLevelWindowAttachListener) { ((TopLevelWindowAttachListener) component).topLevelWindowAttached(topLevelWindow); } return false; }); if (topLevelWindow instanceof Window.HasWorkArea) { AppWorkArea workArea = ((Window.HasWorkArea) topLevelWindow).getWorkArea(); if (workArea != null) { workArea.addStateChangeListener(new AppWorkArea.StateChangeListener() { @Override public void stateChanged(AppWorkArea.State newState) { if (newState == AppWorkArea.State.WINDOW_CONTAINER) { initTabShortcuts(); // listener used only once getConfiguredWorkArea(createWorkAreaContext(topLevelWindow)) .removeStateChangeListener(this); } } }); } } afterShowWindow(topLevelWindow); } protected void initTabShortcuts() { Window.TopLevelWindow topLevelWindow = ui.getTopLevelWindow(); CubaOrderedActionsLayout actionsLayout = topLevelWindow.unwrap(CubaOrderedActionsLayout.class); if (getConfiguredWorkArea(createWorkAreaContext(topLevelWindow)).getMode() == Mode.TABBED) { actionsLayout.addShortcutListener(createNextWindowTabShortcut(topLevelWindow)); actionsLayout.addShortcutListener(createPreviousWindowTabShortcut(topLevelWindow)); } actionsLayout.addShortcutListener(createCloseShortcut(topLevelWindow)); } protected boolean hasDialogWindows() { return !ui.getWindows().isEmpty(); } public ShortcutListener createCloseShortcut(Window.TopLevelWindow topLevelWindow) { String closeShortcut = clientConfig.getCloseShortcut(); KeyCombination combination = KeyCombination.create(closeShortcut); return new ShortcutListener("onClose", combination.getKey().getCode(), KeyCombination.Modifier.codes(combination.getModifiers())) { @Override public void handleAction(Object sender, Object target) { WebAppWorkArea workArea = getConfiguredWorkArea(createWorkAreaContext(topLevelWindow)); if (workArea.getState() != AppWorkArea.State.WINDOW_CONTAINER) { return; } if (workArea.getMode() == Mode.TABBED) { TabSheetBehaviour tabSheet = workArea.getTabbedWindowContainer().getTabSheetBehaviour(); if (tabSheet != null) { Layout layout = (Layout) tabSheet.getSelectedTab(); if (layout != null) { tabSheet.focus(); WindowBreadCrumbs breadCrumbs = tabs.get(layout); if (!canWindowBeClosed(breadCrumbs.getCurrentWindow())) { return; } if (isCloseWithShortcutPrevented(breadCrumbs.getCurrentWindow())) { return; } if (stacks.get(breadCrumbs).empty()) { final Component previousTab = tabSheet.getPreviousTab(layout); if (previousTab != null) { breadCrumbs.getCurrentWindow().closeAndRun(Window.CLOSE_ACTION_ID, () -> tabSheet.setSelectedTab(previousTab)); } else { breadCrumbs.getCurrentWindow().close(Window.CLOSE_ACTION_ID); } } else { breadCrumbs.getCurrentWindow().close(Window.CLOSE_ACTION_ID); } } } } else { Iterator<WindowBreadCrumbs> it = tabs.values().iterator(); if (it.hasNext()) { Window currentWindow = it.next().getCurrentWindow(); if (!isCloseWithShortcutPrevented(currentWindow)) { ui.focus(); currentWindow.close(Window.CLOSE_ACTION_ID); } } } } }; } protected boolean canWindowBeClosed(Window window) { if (webConfig.getDefaultScreenCanBeClosed()) { return true; } String defaultScreenId = webConfig.getDefaultScreenId(); if (webConfig.getUserCanChooseDefaultScreen()) { String userDefaultScreen = userSettingService.loadSetting(ClientType.WEB, "userDefaultScreen"); defaultScreenId = StringUtils.isEmpty(userDefaultScreen) ? defaultScreenId : userDefaultScreen; } return !window.getId().equals(defaultScreenId); } @Override public void openDefaultScreen() { String defaultScreenId = webConfig.getDefaultScreenId(); if (webConfig.getUserCanChooseDefaultScreen()) { String userDefaultScreen = userSettingService.loadSetting(ClientType.WEB, "userDefaultScreen"); defaultScreenId = StringUtils.isEmpty(userDefaultScreen) ? defaultScreenId : userDefaultScreen; } if (StringUtils.isEmpty(defaultScreenId)) { return; } if (!windowConfig.hasWindow(defaultScreenId)) { log.info("Can't find default screen: {}", defaultScreenId); return; } Window window = openWindow(windowConfig.getWindowInfo(defaultScreenId), OpenType.NEW_TAB); // in case of window is created by Runnable instance if (window == null) { return; } if (!webConfig.getDefaultScreenCanBeClosed()) { WebAppWorkArea workArea = getConfiguredWorkArea(createWorkAreaContext(window)); if (workArea.getMode() == Mode.TABBED) { TabSheetBehaviour tabSheetBehaviour = workArea.getTabbedWindowContainer().getTabSheetBehaviour(); HasComponents tabLayout = WebComponentsHelper.getComposition(window).getParent(); String tabId = tabSheetBehaviour.getTab(tabLayout); tabSheetBehaviour.setTabClosable(tabId, false); } } } protected boolean isCloseWithShortcutPrevented(Window currentWindow) { WebWindow webWindow = (WebWindow) ComponentsHelper.getWindowImplementation(currentWindow); if (webWindow != null) { BeforeCloseWithShortcutEvent event = new BeforeCloseWithShortcutEvent(webWindow); webWindow.fireBeforeCloseWithShortcut(event); return event.isClosePrevented(); } return false; } protected boolean isCloseWithCloseButtonPrevented(Window currentWindow) { WebWindow webWindow = (WebWindow) ComponentsHelper.getWindowImplementation(currentWindow); if (webWindow != null) { BeforeCloseWithCloseButtonEvent event = new BeforeCloseWithCloseButtonEvent(webWindow); webWindow.fireBeforeCloseWithCloseButton(event); return event.isClosePrevented(); } return false; } public ShortcutListener createNextWindowTabShortcut(Window.TopLevelWindow topLevelWindow) { String nextTabShortcut = clientConfig.getNextTabShortcut(); KeyCombination combination = KeyCombination.create(nextTabShortcut); return new ShortcutListener("onNextTab", combination.getKey().getCode(), KeyCombination.Modifier.codes(combination.getModifiers())) { @Override public void handleAction(Object sender, Object target) { TabSheetBehaviour tabSheet = getConfiguredWorkArea(createWorkAreaContext(topLevelWindow)) .getTabbedWindowContainer().getTabSheetBehaviour(); if (tabSheet != null && !hasDialogWindows() && tabSheet.getComponentCount() > 1) { Component selectedTabComponent = tabSheet.getSelectedTab(); String tabId = tabSheet.getTab(selectedTabComponent); int tabPosition = tabSheet.getTabPosition(tabId); int newTabPosition = (tabPosition + 1) % tabSheet.getComponentCount(); String newTabId = tabSheet.getTab(newTabPosition); tabSheet.setSelectedTab(newTabId); moveFocus(tabSheet, newTabId); } } }; } public ShortcutListener createPreviousWindowTabShortcut(Window.TopLevelWindow topLevelWindow) { String previousTabShortcut = clientConfig.getPreviousTabShortcut(); KeyCombination combination = KeyCombination.create(previousTabShortcut); return new ShortcutListener("onPreviousTab", combination.getKey().getCode(), KeyCombination.Modifier.codes(combination.getModifiers())) { @Override public void handleAction(Object sender, Object target) { TabSheetBehaviour tabSheet = getConfiguredWorkArea(createWorkAreaContext(topLevelWindow)) .getTabbedWindowContainer().getTabSheetBehaviour(); if (tabSheet != null && !hasDialogWindows() && tabSheet.getComponentCount() > 1) { Component selectedTabComponent = tabSheet.getSelectedTab(); String selectedTabId = tabSheet.getTab(selectedTabComponent); int tabPosition = tabSheet.getTabPosition(selectedTabId); int newTabPosition = (tabSheet.getComponentCount() + tabPosition - 1) % tabSheet.getComponentCount(); String newTabId = tabSheet.getTab(newTabPosition); tabSheet.setSelectedTab(newTabId); moveFocus(tabSheet, newTabId); } } }; } protected class DialogWindowActionHandler implements com.vaadin.event.Action.Handler { protected Window window; protected com.vaadin.event.Action saveSettingsAction; protected com.vaadin.event.Action restoreToDefaultsAction; protected com.vaadin.event.Action analyzeAction; protected boolean initialized = false; public DialogWindowActionHandler(Window window) { this.window = window; } @Override public com.vaadin.event.Action[] getActions(Object target, Object sender) { if (!initialized) { saveSettingsAction = new com.vaadin.event.Action(messages.getMainMessage("actions.saveSettings")); restoreToDefaultsAction = new com.vaadin.event.Action( messages.getMainMessage("actions.restoreToDefaults")); analyzeAction = new com.vaadin.event.Action(messages.getMainMessage("actions.analyzeLayout")); initialized = true; } List<com.vaadin.event.Action> actions = new ArrayList<>(3); if (clientConfig.getManualScreenSettingsSaving()) { actions.add(saveSettingsAction); actions.add(restoreToDefaultsAction); } if (clientConfig.getLayoutAnalyzerEnabled()) { actions.add(analyzeAction); } return actions.toArray(new com.vaadin.event.Action[actions.size()]); } @Override public void handleAction(com.vaadin.event.Action action, Object sender, Object target) { if (initialized) { if (saveSettingsAction == action) { window.saveSettings(); } else if (restoreToDefaultsAction == action) { window.deleteSettings(); } else if (analyzeAction == action) { LayoutAnalyzer analyzer = new LayoutAnalyzer(); List<LayoutTip> tipsList = analyzer.analyze(window); if (tipsList.isEmpty()) { window.showNotification("No layout problems found", NotificationType.HUMANIZED); } else { window.openWindow("layoutAnalyzer", OpenType.DIALOG, ParamsMap.of("tipsList", tipsList)); } } } } } @Nullable protected WorkAreaContext createWorkAreaContext(WindowInfo windowInfo) { return null; } @Nullable protected WorkAreaContext createWorkAreaContext(Window window) { return null; } public interface WorkAreaContext { } }