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.CmsResource; 031import org.opencms.ui.A_CmsUI; 032import org.opencms.ui.CmsVaadinUtils; 033import org.opencms.ui.Messages; 034import org.opencms.ui.components.extensions.CmsMaxHeightExtension; 035import org.opencms.util.CmsStringUtil; 036 037import java.util.List; 038 039import org.jsoup.nodes.Attributes; 040import org.jsoup.nodes.Element; 041 042import com.google.common.collect.Lists; 043import com.vaadin.event.Action.Handler; 044import com.vaadin.server.Page; 045import com.vaadin.server.Page.BrowserWindowResizeEvent; 046import com.vaadin.server.Page.BrowserWindowResizeListener; 047import com.vaadin.shared.Registration; 048import com.vaadin.ui.Alignment; 049import com.vaadin.ui.Button; 050import com.vaadin.ui.Component; 051import com.vaadin.ui.HorizontalLayout; 052import com.vaadin.ui.Layout; 053import com.vaadin.ui.Panel; 054import com.vaadin.ui.VerticalLayout; 055import com.vaadin.ui.Window; 056import com.vaadin.ui.Window.CloseEvent; 057import com.vaadin.ui.Window.CloseListener; 058import com.vaadin.ui.declarative.DesignContext; 059 060/** 061 * Basic dialog class with a content panel and button bar.<p> 062 */ 063public class CmsBasicDialog extends VerticalLayout { 064 065 /** The available window widths. */ 066 public enum DialogWidth { 067 068 /** Depending on the content. */ 069 content, 070 071 /** The maximum width of 90% of the window width. */ 072 max, 073 074 /** The default width of 600px. */ 075 narrow, 076 077 /** The wide width of 800px. */ 078 wide 079 } 080 081 /** Serial version id. */ 082 private static final long serialVersionUID = 1L; 083 084 /** The window resize listener registration. */ 085 Registration m_resizeListenerRegistration; 086 087 /** The shortcut action handler. */ 088 private Handler m_actionHandler; 089 090 /** The left button panel. */ 091 private HorizontalLayout m_buttonPanelLeft; 092 093 /** The button bar. */ 094 private HorizontalLayout m_buttonPanelRight; 095 096 /** The content panel. */ 097 private Panel m_contentPanel; 098 099 /** The resource info component. */ 100 private Component m_infoComponent; 101 102 /** The resources for which the resource info boxes should be displayed. */ 103 private List<CmsResource> m_infoResources = Lists.newArrayList(); 104 105 /** The main panel. */ 106 private VerticalLayout m_mainPanel; 107 108 /** Extension used to regulate max height. */ 109 private CmsMaxHeightExtension m_maxHeightExtension; 110 111 /** Maximum recorded height. */ 112 private int m_maxRecordedHeight = Integer.MIN_VALUE; 113 114 /** The window resize listener. */ 115 private BrowserWindowResizeListener m_windowResizeListener; 116 117 /** 118 * Creates new instance.<p> 119 */ 120 public CmsBasicDialog() { 121 122 addStyleName(OpenCmsTheme.DIALOG); 123 setMargin(true); 124 setSpacing(true); 125 setWidth("100%"); 126 127 m_mainPanel = new VerticalLayout(); 128 m_mainPanel.addStyleName(OpenCmsTheme.DIALOG_CONTENT); 129 m_mainPanel.setSpacing(true); 130 m_mainPanel.setSizeFull(); 131 132 m_contentPanel = new Panel(); 133 m_contentPanel.setSizeFull(); 134 m_contentPanel.addStyleName("v-scrollable"); 135 m_contentPanel.addStyleName(OpenCmsTheme.DIALOG_CONTENT_PANEL); 136 137 m_mainPanel.addComponent(m_contentPanel); 138 m_mainPanel.setExpandRatio(m_contentPanel, 3); 139 140 Panel panel = new Panel(); 141 panel.setContent(m_mainPanel); 142 panel.setSizeFull(); 143 addComponent(panel); 144 setExpandRatio(panel, 1); 145 HorizontalLayout buttons = new HorizontalLayout(); 146 buttons.setWidth("100%"); 147 buttons.addStyleName(OpenCmsTheme.DIALOG_BUTTON_BAR); 148 addComponent(buttons); 149 m_buttonPanelLeft = new HorizontalLayout(); 150 m_buttonPanelLeft.setSpacing(true); 151 buttons.addComponent(m_buttonPanelLeft); 152 buttons.setComponentAlignment(m_buttonPanelLeft, Alignment.MIDDLE_LEFT); 153 m_buttonPanelLeft.setVisible(false); 154 m_buttonPanelRight = new HorizontalLayout(); 155 m_buttonPanelRight.setSpacing(true); 156 buttons.addComponent(m_buttonPanelRight); 157 buttons.setComponentAlignment(m_buttonPanelRight, Alignment.MIDDLE_RIGHT); 158 enableMaxHeight(); 159 } 160 161 /** 162 * Initializes the dialog window.<p> 163 * 164 * @return the window to be used by dialogs 165 */ 166 public static Window prepareWindow() { 167 168 return prepareWindow(DialogWidth.narrow); 169 } 170 171 /** 172 * Initializes the dialog window.<p> 173 * 174 * @param width the dialog width 175 * 176 * @return the window to be used by dialogs 177 */ 178 public static Window prepareWindow(DialogWidth width) { 179 180 Window window = new Window(); 181 window.setModal(true); 182 window.setClosable(true); 183 int pageWidth = Page.getCurrent().getBrowserWindowWidth(); 184 if (((width == DialogWidth.wide) && (pageWidth < 810)) 185 || ((width == DialogWidth.narrow) && (pageWidth < 610))) { 186 // in case the available page width does not allow the desired width, use max 187 width = DialogWidth.max; 188 } 189 if (width == DialogWidth.max) { 190 // in case max width would result in a width very close to wide or narrow, use their static width instead of relative width 191 if ((pageWidth >= 610) && (pageWidth < 670)) { 192 width = DialogWidth.narrow; 193 } else if ((pageWidth >= 810) && (pageWidth < 890)) { 194 width = DialogWidth.wide; 195 } 196 } 197 switch (width) { 198 case content: 199 // do nothing 200 break; 201 case wide: 202 window.setWidth("800px"); 203 break; 204 case max: 205 window.setWidth("90%"); 206 break; 207 case narrow: 208 default: 209 window.setWidth("600px"); 210 break; 211 } 212 window.center(); 213 return window; 214 } 215 216 /** 217 * Adds a button to the button bar.<p> 218 * 219 * @param button the button to add 220 */ 221 public void addButton(Component button) { 222 223 addButton(button, true); 224 } 225 226 /** 227 * Adds a button to the button bar.<p> 228 * 229 * @param button the button to add 230 * @param right to align the button right 231 */ 232 public void addButton(Component button, boolean right) { 233 234 if (right) { 235 m_buttonPanelRight.addComponent(button); 236 } else { 237 m_buttonPanelLeft.addComponent(button); 238 m_buttonPanelLeft.setVisible(true); 239 } 240 } 241 242 /** 243 * Creates an 'Cancel' button.<p> 244 * 245 * @return the button 246 */ 247 public Button createButtonCancel() { 248 249 return new Button(CmsVaadinUtils.getMessageText(org.opencms.workplace.Messages.GUI_DIALOG_BUTTON_CANCEL_0)); 250 } 251 252 /** 253 * Creates an 'Cancel' button.<p> 254 * 255 * @return the button 256 */ 257 public Button createButtonClose() { 258 259 return new Button(CmsVaadinUtils.getMessageText(org.opencms.workplace.Messages.GUI_DIALOG_BUTTON_CLOSE_0)); 260 } 261 262 /** 263 * Creates an 'OK' button.<p> 264 * 265 * @return the button 266 */ 267 public Button createButtonOK() { 268 269 return new Button(CmsVaadinUtils.getMessageText(org.opencms.workplace.Messages.GUI_DIALOG_BUTTON_OK_0)); 270 } 271 272 /** 273 * Displays the resource infos panel.<p> 274 * 275 * @param resources the resources 276 */ 277 public void displayResourceInfo(List<CmsResource> resources) { 278 279 displayResourceInfo(resources, Messages.GUI_SELECTED_0); 280 281 } 282 283 /** 284 * Display the resource indos panel with panel message.<p> 285 * 286 * @param resources to show info for 287 * @param messageKey of the panel 288 */ 289 public void displayResourceInfo(List<CmsResource> resources, String messageKey) { 290 291 m_infoResources = Lists.newArrayList(resources); 292 if (m_infoComponent != null) { 293 m_mainPanel.removeComponent(m_infoComponent); 294 m_infoComponent = null; 295 } 296 if ((resources != null) && !resources.isEmpty()) { 297 if (resources.size() == 1) { 298 m_infoComponent = new CmsResourceInfo(resources.get(0)); 299 m_mainPanel.addComponent(m_infoComponent, 0); 300 } else { 301 m_infoComponent = createResourceListPanel( 302 messageKey == null ? null : Messages.get().getBundle(A_CmsUI.get().getLocale()).key(messageKey), 303 resources); 304 m_mainPanel.addComponent(m_infoComponent, 0); 305 m_mainPanel.setExpandRatio(m_infoComponent, 1); 306 307 // reset expand ratio of the content panel 308 m_contentPanel.setSizeUndefined(); 309 m_contentPanel.setWidth("100%"); 310 m_mainPanel.setExpandRatio(m_contentPanel, 0); 311 } 312 313 } 314 } 315 316 /** 317 * Displays the resource info panel.<p> 318 * 319 * @param resourceInfos to display 320 */ 321 public void displayResourceInfoDirectly(List<CmsResourceInfo> resourceInfos) { 322 323 if (m_infoComponent != null) { 324 m_mainPanel.removeComponent(m_infoComponent); 325 m_infoComponent = null; 326 } 327 if ((resourceInfos != null) && !resourceInfos.isEmpty()) { 328 if (resourceInfos.size() == 1) { 329 m_infoComponent = resourceInfos.get(0); 330 m_mainPanel.addComponent(m_infoComponent, 0); 331 } else { 332 m_infoComponent = createResourceListPanelDirectly( 333 Messages.get().getBundle(A_CmsUI.get().getLocale()).key(Messages.GUI_SELECTED_0), 334 resourceInfos); 335 m_mainPanel.addComponent(m_infoComponent, 0); 336 m_mainPanel.setExpandRatio(m_infoComponent, 1); 337 338 // reset expand ratio of the content panel 339 m_contentPanel.setSizeUndefined(); 340 m_contentPanel.setWidth("100%"); 341 m_mainPanel.setExpandRatio(m_contentPanel, 0); 342 } 343 344 } 345 } 346 347 /** 348 * Gets the resources for which the resource info boxes should be displayed.<p> 349 * 350 * @return the resource info resources 351 */ 352 public List<CmsResource> getInfoResources() { 353 354 return m_infoResources; 355 } 356 357 /** 358 * Initializes action handler.<p> 359 * 360 * @param window the parent window 361 */ 362 public void initActionHandler(final Window window) { 363 364 if (m_actionHandler != null) { 365 window.addActionHandler(m_actionHandler); 366 window.addCloseListener(new CloseListener() { 367 368 private static final long serialVersionUID = 1L; 369 370 public void windowClose(CloseEvent e) { 371 372 clearActionHandler(window); 373 } 374 }); 375 } 376 } 377 378 /** 379 * @see com.vaadin.ui.AbstractOrderedLayout#readDesign(org.jsoup.nodes.Element, com.vaadin.ui.declarative.DesignContext) 380 */ 381 @Override 382 public void readDesign(Element design, DesignContext designContext) { 383 384 for (Element child : design.children()) { 385 boolean contentRead = false; 386 boolean buttonsRead = false; 387 boolean aboveRead = false; 388 boolean belowRead = false; 389 if ("content".equals(child.tagName()) && !contentRead) { 390 Component content = designContext.readDesign(child.child(0)); 391 setContent(content); 392 contentRead = true; 393 } else if ("buttons".equals(child.tagName()) && !buttonsRead) { 394 for (Element buttonElement : child.children()) { 395 Component button = designContext.readDesign(buttonElement); 396 Attributes attr = buttonElement.attributes(); 397 addButton(button, !attr.hasKey(":left")); 398 } 399 buttonsRead = true; 400 } else if ("above".equals(child.tagName()) && !aboveRead) { 401 Component aboveContent = designContext.readDesign(child.child(0)); 402 setAbove(aboveContent); 403 aboveRead = true; 404 } else if ("below".equals(child.tagName()) && !belowRead) { 405 Component belowContent = designContext.readDesign(child.child(0)); 406 setBelow(belowContent); 407 belowRead = true; 408 } 409 } 410 } 411 412 /** 413 * Sets the content to be displayed above the main content.<p> 414 * 415 * @param aboveContent the above content 416 */ 417 public void setAbove(Component aboveContent) { 418 419 if (m_mainPanel.getComponentIndex(m_contentPanel) == 0) { 420 m_mainPanel.addComponent(aboveContent, 0); 421 } else { 422 m_mainPanel.replaceComponent(m_mainPanel.getComponent(0), aboveContent); 423 } 424 } 425 426 /** 427 * Sets the shortcut action handler.<p> 428 * Set this before opening the window, so it will be initialized properly.<p> 429 * 430 * @param actionHandler the action handler 431 */ 432 public void setActionHandler(Handler actionHandler) { 433 434 m_actionHandler = actionHandler; 435 } 436 437 /** 438 * Sets the content to be displayed below the main content.<p> 439 * @param belowContent the below content 440 */ 441 public void setBelow(Component belowContent) { 442 443 int i = m_mainPanel.getComponentIndex(m_mainPanel); 444 Component oldBelow = m_mainPanel.getComponent(i + 1); 445 if (oldBelow == null) { 446 m_mainPanel.addComponent(belowContent); 447 } else { 448 m_mainPanel.replaceComponent(oldBelow, belowContent); 449 } 450 } 451 452 /** 453 * Sets the content.<p> 454 * 455 * @param content the content widget 456 */ 457 public void setContent(Component content) { 458 459 m_contentPanel.setContent(content); 460 if (content instanceof Layout.MarginHandler) { 461 ((Layout.MarginHandler)content).setMargin(true); 462 } 463 } 464 465 /** 466 * Sets the height of the content to a given min Height or 100%.<p> 467 * 468 * @param height minimal height. 469 */ 470 public void setContentMinHeight(int height) { 471 472 if ((0.9 * Page.getCurrent().getBrowserWindowHeight()) < height) { 473 m_contentPanel.getContent().setHeight(height + "px"); 474 } else { 475 m_contentPanel.getContent().setHeight("100%"); 476 } 477 } 478 479 /** 480 * Sets the visibility of the content panel.<o> 481 * 482 * @param visible visibility of the content. 483 */ 484 public void setContentVisibility(boolean visible) { 485 486 m_contentPanel.setVisible(visible); 487 } 488 489 /** 490 * Sets the window which contains this dialog to full height with a given minimal height in pixel.<p> 491 * 492 * @param minHeight minimal height in pixel 493 */ 494 public void setWindowMinFullHeight(int minHeight) { 495 496 Window window = CmsVaadinUtils.getWindow(this); 497 if (window == null) { 498 return; 499 } 500 window.setHeight("90%"); 501 setHeight("100%"); 502 setContentMinHeight(minHeight); 503 window.center(); 504 } 505 506 /** 507 * Creates a resource list panel.<p> 508 * 509 * @param caption the caption to use 510 * @param resources the resources 511 * 512 * @return the panel 513 */ 514 protected Panel createResourceListPanel(String caption, List<CmsResource> resources) { 515 516 Panel result = null; 517 if (CmsStringUtil.isEmptyOrWhitespaceOnly(caption)) { 518 result = new Panel(); 519 } else { 520 result = new Panel(caption); 521 } 522 result.addStyleName("v-scrollable"); 523 result.setSizeFull(); 524 VerticalLayout resourcePanel = new VerticalLayout(); 525 result.setContent(resourcePanel); 526 resourcePanel.addStyleName(OpenCmsTheme.REDUCED_MARGIN); 527 resourcePanel.addStyleName(OpenCmsTheme.REDUCED_SPACING); 528 resourcePanel.setSpacing(true); 529 resourcePanel.setMargin(true); 530 for (CmsResource resource : resources) { 531 resourcePanel.addComponent(new CmsResourceInfo(resource)); 532 } 533 return result; 534 } 535 536 /** 537 * Creates a resource list panel.<p> 538 * 539 * @param caption the caption to use 540 * @param resourceInfo the resource-infos 541 * @return the panel 542 */ 543 protected Panel createResourceListPanelDirectly(String caption, List<CmsResourceInfo> resourceInfo) { 544 545 Panel result = new Panel(caption); 546 result.addStyleName("v-scrollable"); 547 result.setSizeFull(); 548 VerticalLayout resourcePanel = new VerticalLayout(); 549 result.setContent(resourcePanel); 550 resourcePanel.addStyleName(OpenCmsTheme.REDUCED_MARGIN); 551 resourcePanel.addStyleName(OpenCmsTheme.REDUCED_SPACING); 552 resourcePanel.setSpacing(true); 553 resourcePanel.setMargin(true); 554 for (CmsResourceInfo resource : resourceInfo) { 555 resourcePanel.addComponent(resource); 556 } 557 return result; 558 } 559 560 /** 561 * Adds the max height extension to the dialog panel.<p> 562 */ 563 protected void enableMaxHeight() { 564 565 // use the window height minus an offset for the window header and some spacing 566 int maxHeight = calculateMaxHeight(A_CmsUI.get().getPage().getBrowserWindowHeight()); 567 m_maxHeightExtension = new CmsMaxHeightExtension(this, maxHeight); 568 // only center window for height changes that exceed the maximum height since the last window resize 569 // (window resize handler resets this) 570 m_maxHeightExtension.addHeightChangeHandler(new CmsMaxHeightExtension.I_HeightChangeHandler() { 571 572 @SuppressWarnings("synthetic-access") 573 public void onChangeHeight(int height) { 574 575 boolean center = height > m_maxRecordedHeight; 576 m_maxRecordedHeight = Math.max(m_maxRecordedHeight, height); 577 Window wnd = CmsVaadinUtils.getWindow(CmsBasicDialog.this); 578 if ((wnd != null) && center) { 579 wnd.center(); 580 } 581 } 582 }); 583 584 addDetachListener(new DetachListener() { 585 586 private static final long serialVersionUID = 1L; 587 588 public void detach(DetachEvent event) { 589 590 if (m_resizeListenerRegistration != null) { 591 m_resizeListenerRegistration.remove(); 592 m_resizeListenerRegistration = null; 593 } 594 } 595 }); 596 597 m_windowResizeListener = new BrowserWindowResizeListener() { 598 599 private static final long serialVersionUID = 1L; 600 601 @SuppressWarnings("synthetic-access") 602 public void browserWindowResized(BrowserWindowResizeEvent event) { 603 604 m_maxRecordedHeight = Integer.MIN_VALUE; 605 int newHeight = event.getHeight(); 606 m_maxHeightExtension.updateMaxHeight(calculateMaxHeight(newHeight)); 607 } 608 }; 609 m_resizeListenerRegistration = A_CmsUI.get().getPage().addBrowserWindowResizeListener(m_windowResizeListener); 610 611 } 612 613 /** 614 * Removes the action handler.<p> 615 * 616 * @param window the window the action handler is attached to 617 */ 618 void clearActionHandler(Window window) { 619 620 if (m_actionHandler != null) { 621 window.removeActionHandler(m_actionHandler); 622 } 623 } 624 625 /** 626 * Calculates max dialog height given the window height.<p> 627 * 628 * @param windowHeight the window height 629 * @return the maximal dialog height 630 */ 631 private int calculateMaxHeight(int windowHeight) { 632 633 return (int)((0.95 * windowHeight) - 40); 634 } 635}