/* * RHQ Management Platform * Copyright (C) 2005-2011 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.coregui.client.components.carousel; import com.smartgwt.client.data.Criteria; import com.smartgwt.client.types.AnimationEffect; import com.smartgwt.client.types.VerticalAlignment; import com.smartgwt.client.widgets.AnimationCallback; import com.smartgwt.client.widgets.Canvas; import com.smartgwt.client.widgets.layout.VLayout; import org.rhq.coregui.client.BookmarkableView; import org.rhq.coregui.client.DetailsView; import org.rhq.coregui.client.ViewPath; import org.rhq.coregui.client.components.buttons.BackButton; import org.rhq.coregui.client.components.table.AbstractTableSection; import org.rhq.coregui.client.util.Log; import org.rhq.coregui.client.util.enhanced.EnhancedUtility; import org.rhq.coregui.client.util.enhanced.EnhancedVLayout; /** * Provides the typical carousel view with the additional ability of traversing to "details" views - a masters/detail * view in effect. This class is analogous to {@link AbstractTableSection}. * * @author Jay Shaughnessy */ public abstract class BookmarkableCarousel extends Carousel implements BookmarkableView { private boolean initialDisplay; private VLayout detailsHolder; private Canvas detailsView; private String basePath; public BookmarkableCarousel() { super(); } public BookmarkableCarousel(String titleString) { super(titleString); } public BookmarkableCarousel(String titleString, Criteria criteria) { super(titleString, criteria); } @Override protected void onInit() { super.onInit(); this.initialDisplay = true; detailsHolder = new EnhancedVLayout(); detailsHolder.setAlign(VerticalAlignment.TOP); detailsHolder.setMargin(4); detailsHolder.hide(); addMember(detailsHolder); // if the detailsView is already defined it means we want the details view to be rendered prior to // the master view, probably due to a direct navigation or refresh (like F5 when sitting on the details page) if (null != detailsView) { switchToDetailsView(); } } @Override public void renderView(ViewPath viewPath) { viewPath.next(); this.basePath = viewPath.getPathToCurrent(); if (!viewPath.isEnd()) { viewPath.next(); // get the id, which may be in string format String id = convertCurrentViewPathToID(viewPath.getCurrent().getPath()); detailsView = getDetailsView(id); switchToDetailsView(); } else { switchToCarouselView(); } } // the main CoreGUI class will assume anything with a digit as the first character in a path segment in the // URL is an ID. public static final String ID_PREFIX = "0id_"; // the prefix to be placed in front of the string IDs in URLs protected String convertCurrentViewPathToID(String path) { return path.startsWith(ID_PREFIX) ? path.substring(ID_PREFIX.length()) : path; } /** * Returns the details canvas with information on the item that has the given ID. * Note that an empty details view should be returned if the id passed in is 0 (as would * be the case if a new item is to be created using the details view). * * @param id the id of the details to be shown; will be "0" if an empty details view should be shown. */ public abstract Canvas getDetailsView(String id); /** * Switches to viewing the details canvas, hiding the table. This does not * do anything with reloading data or switching to the selected row in the table; * this only changes the visibility of canvases. */ protected void switchToDetailsView() { Canvas contents = getCarouselContents(); // If the Carousel has not yet been initialized then ignore if (contents != null) { // If the carousel view is visible then gracefully switch to the details view. if (contents.isVisible()) { contents.animateHide(AnimationEffect.WIPE, new AnimationCallback() { @Override public void execute(boolean b) { buildDetailsView(); } }); } else { // Even if the table view is not visible, it may not be hidden. Instead, it may be the // case that its parent (the encompassing Table/HLayout) may not be visible. This is unusual // because typically we switch between the table and detail view while under the subtab, but // if we navigate to the detail view from another subtab (for example, the drift tree context // menu) the Table may not be visible and the table view may not be hidden. To make a long // story short, ensure the table view is hidden when displaying the details view. contents.hide(); /* * if the programmer chooses to go directly from the detailView in create-mode to the * detailsView in edit-mode, the content canvas will already be hidden, which means the * animateHide would be a no-op (the event won't fire). this causes the detailsHolder * to keep a reference to the previous detailsView (the one in create-mode) instead of the * newly returned reference from getDetailsView(ID) that was called when the renderView * methods were called hierarchically down to render the new detailsView in edit-mode. * therefore, we need to explicitly destroy what's already there (presumably the detailsView * in create-mode), and then rebuild it (presumably the detailsView in edit-mode). */ EnhancedUtility.destroyMembers(detailsHolder); buildDetailsView(); } } } private void buildDetailsView() { detailsView.setWidth100(); detailsView.setHeight100(); boolean isEditable = (detailsView instanceof DetailsView && ((DetailsView) detailsView).isEditable()); if (!isEditable) { // Only add the "Back to List" button if the details are definitely not editable, because if they are // editable, a Cancel button should already be provided by the details view. BackButton backButton = new BackButton(MSG.view_tableSection_backButton(), basePath); detailsHolder.addMember(backButton); VLayout verticalSpacer = new EnhancedVLayout(); verticalSpacer.setHeight(8); detailsHolder.addMember(verticalSpacer); } detailsHolder.addMember(detailsView); detailsHolder.animateShow(AnimationEffect.WIPE); } /** * Switches to viewing the table, hiding the details canvas. */ protected void switchToCarouselView() { final Canvas contents = getCarouselContents(); if (contents != null) { // If this is not the initial display of the table, refresh the table's data. Otherwise, a refresh would be // redundant, since the data was just loaded when the table was drawn. if (this.initialDisplay) { this.initialDisplay = false; } else { Log.debug("Refreshing data for Table [" + getClass().getName() + "]..."); refresh(); } // if the detailsHolder is visible then gracefully switch views, otherwise just // clean up any lingering details holder and show the table view. if (detailsHolder != null && detailsHolder.isVisible()) { detailsHolder.animateHide(AnimationEffect.WIPE, new AnimationCallback() { @Override public void execute(boolean b) { EnhancedUtility.destroyMembers(detailsHolder); contents.animateShow(AnimationEffect.WIPE); } }); } else { if (detailsHolder != null) { EnhancedUtility.destroyMembers(detailsHolder); } contents.animateShow(AnimationEffect.WIPE); } } } }