/*
* Copyright 2012 Red Hat, Inc. and/or its affiliates.
*
* 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 org.kie.workbench.common.widgets.decoratedgrid.client.widget;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.ScrollPanel;
import org.kie.workbench.common.widgets.decoratedgrid.client.widget.events.AppendRowEvent;
import org.kie.workbench.common.widgets.decoratedgrid.client.widget.events.DeleteRowEvent;
import org.kie.workbench.common.widgets.decoratedgrid.client.widget.events.InsertRowEvent;
import org.kie.workbench.common.widgets.decoratedgrid.client.widget.events.SelectedCellChangeEvent;
import org.kie.workbench.common.widgets.decoratedgrid.client.widget.events.SetModelEvent;
import org.kie.workbench.common.widgets.decoratedgrid.client.widget.events.ColumnResizeEvent;
/**
* Abstract grid, decorated with DecoratedGridHeaderWidget and
* DecoratedGridSidebarWidget encapsulating basic operation: keyboard navigation
* and column resizing.
* @param <M> The domain model represented by the Grid
* @param <T> The type of domain columns represented by the Grid
* @param <C> The type of domain cell represented by the Grid
*/
public abstract class AbstractDecoratedGridWidget<M, T, C> extends Composite
implements
ColumnResizeEvent.Handler,
SelectedCellChangeEvent.Handler,
DeleteRowEvent.Handler,
InsertRowEvent.Handler,
AppendRowEvent.Handler,
SetModelEvent.Handler<M> {
// Widgets for UI
protected Panel mainPanel;
protected Panel bodyPanel;
protected ScrollPanel scrollPanel;
protected AbstractMergableGridWidget<M, T> gridWidget;
protected AbstractDecoratedGridHeaderWidget<M, T> headerWidget;
protected AbstractDecoratedGridSidebarWidget<M, T> sidebarWidget;
protected int height;
protected int width;
// Resources
protected final ResourcesProvider<T> resources;
//Event Bus
protected final EventBus eventBus;
/**
* Construct at empty DecoratedGridWidget, without DecoratedGridHeaderWidget
* or DecoratedGridSidebarWidget These should be set before the grid is
* displayed using setHeaderWidget and setSidebarWidget respectively.
*/
public AbstractDecoratedGridWidget( ResourcesProvider<T> resources,
EventBus eventBus,
Panel mainPanel,
Panel bodyPanel,
AbstractMergableGridWidget<M, T> gridWidget,
AbstractDecoratedGridHeaderWidget<M, T> headerWidget,
AbstractDecoratedGridSidebarWidget<M, T> sidebarWidget ) {
if ( resources == null ) {
throw new IllegalArgumentException( "resources cannot be null" );
}
if ( eventBus == null ) {
throw new IllegalArgumentException( "eventBus cannot be null" );
}
if ( mainPanel == null ) {
throw new IllegalArgumentException( "mainPanel cannot be null" );
}
if ( bodyPanel == null ) {
throw new IllegalArgumentException( "bodyPanel cannot be null" );
}
if ( gridWidget == null ) {
throw new IllegalArgumentException( "gridWidget cannot be null" );
}
if ( headerWidget == null ) {
throw new IllegalArgumentException( "headerWidget cannot be null" );
}
if ( sidebarWidget == null ) {
throw new IllegalArgumentException( "sidebarWidget cannot be null" );
}
this.resources = resources;
this.eventBus = eventBus;
this.mainPanel = mainPanel;
this.bodyPanel = bodyPanel;
this.gridWidget = gridWidget;
this.headerWidget = headerWidget;
this.sidebarWidget = sidebarWidget;
//The height of the sidebar controls the height of the resize divider
this.headerWidget.setSidebar( this.sidebarWidget );
scrollPanel = new ScrollPanel();
scrollPanel.add( gridWidget );
scrollPanel.addScrollHandler( getScrollHandler() );
initialiseHeaderWidget();
initialiseSidebarWidget();
initWidget( mainPanel );
//Wire-up event handlers
eventBus.addHandler( DeleteRowEvent.TYPE,
this );
eventBus.addHandler( InsertRowEvent.TYPE,
this );
eventBus.addHandler( AppendRowEvent.TYPE,
this );
eventBus.addHandler( SelectedCellChangeEvent.TYPE,
this );
}
/**
* Resize the DecoratedGridHeaderWidget and DecoratedGridSidebarWidget when
* DecoratedGridWidget shows scrollbars
*/
protected void assertDimensions() {
headerWidget.setWidth( scrollPanel.getElement().getClientWidth()
+ "px" );
sidebarWidget.setHeight( scrollPanel.getElement().getClientHeight()
+ "px" );
}
/**
* Return the ScrollPanel in which the DecoratedGridWidget "grid" is nested.
* This allows ScrollEvents to be hooked up to other defendant controls
* (e.g. the Header).
* @return
*/
protected abstract ScrollHandler getScrollHandler();
//Initialise the Header Widget and attach resize handlers to GridWidget to support
//column resizing and to resize GridWidget's ScrollPanel when header resizes.
private void initialiseHeaderWidget() {
eventBus.addHandler( ColumnResizeEvent.TYPE,
this );
this.headerWidget.addResizeHandler( new ResizeHandler() {
public void onResize( ResizeEvent event ) {
final int scrollPanelHeight = height - event.getHeight();
if ( scrollPanelHeight > 0 ) {
scrollPanel.setHeight( scrollPanelHeight + "px" );
assertDimensions();
}
}
} );
bodyPanel.add( headerWidget );
bodyPanel.add( scrollPanel );
}
//Set the SidebarWidget and attach a ResizeEvent handler to the Sidebar for when the header changes
//size and the Sidebar needs to be redrawn to align correctly. Also attach a RowGroupingChangeEvent
//handler to the MergableGridWidget so the Sidebar can redraw itself when rows are merged, grouped,
//ungrouped or unmerged.
private void initialiseSidebarWidget() {
this.headerWidget.addResizeHandler( new ResizeHandler() {
public void onResize( ResizeEvent event ) {
sidebarWidget.resizeSidebar( event.getHeight() );
}
} );
this.mainPanel.add( sidebarWidget );
this.mainPanel.add( bodyPanel );
}
/**
* This should be used instead of setHeight(String) and setWidth(String) as
* various child Widgets of the DecisionTable need to have their sizes set
* relative to the outermost Widget (i.e. this).
*/
@Override
public void setPixelSize( int width,
int height ) {
if ( width < 0 ) {
throw new IllegalArgumentException( "width cannot be less than zero" );
}
if ( height < 0 ) {
throw new IllegalArgumentException( "height cannot be less than zero" );
}
super.setPixelSize( width,
height );
this.height = height;
setHeight( height );
setWidth( width );
}
//Ensure the selected cell is visible
private void cellSelected( AbstractMergableGridWidget.CellSelectionDetail ce ) {
//No selection
if ( ce == null ) {
return;
}
//Left extent
if ( ce.getOffsetX() < scrollPanel.getHorizontalScrollPosition() ) {
scrollPanel.setHorizontalScrollPosition( ce.getOffsetX() );
}
//Right extent
int scrollWidth = scrollPanel.getElement().getClientWidth();
if ( ce.getOffsetX() + ce.getWidth() > scrollWidth + scrollPanel.getHorizontalScrollPosition() ) {
int delta = ce.getOffsetX() + ce.getWidth() - scrollPanel.getHorizontalScrollPosition() - scrollWidth;
scrollPanel.setHorizontalScrollPosition( scrollPanel.getHorizontalScrollPosition() + delta );
}
//Top extent
if ( ce.getOffsetY() < scrollPanel.getVerticalScrollPosition() ) {
scrollPanel.setVerticalScrollPosition( ce.getOffsetY() );
}
//Bottom extent
int scrollHeight = scrollPanel.getElement().getClientHeight();
if ( ce.getOffsetY() + ce.getHeight() > scrollHeight + scrollPanel.getVerticalScrollPosition() ) {
int delta = ce.getOffsetY() + ce.getHeight() - scrollPanel.getVerticalScrollPosition() - scrollHeight;
scrollPanel.setVerticalScrollPosition( scrollPanel.getVerticalScrollPosition() + delta );
}
}
// Set height of outer most Widget and related children
private void setHeight( final int height ) {
mainPanel.setHeight( height
+ "px" );
// The Sidebar and Header sizes are derived from the ScrollPanel
Scheduler.get().scheduleFinally( new ScheduledCommand() {
public void execute() {
assertDimensions();
}
} );
}
// Set width of outer most Widget and related children
private void setWidth( int width ) {
mainPanel.setWidth( width
+ "px" );
scrollPanel.setWidth( ( width - resources.sidebarWidth() )
+ "px" );
// The Sidebar and Header sizes are derived from the ScrollPanel
Scheduler.get().scheduleFinally( new ScheduledCommand() {
public void execute() {
assertDimensions();
}
} );
}
public void onColumnResize( final ColumnResizeEvent event ) {
Scheduler.get().scheduleDeferred( new Command() {
public void execute() {
assertDimensions();
}
} );
}
public void onSelectedCellChange( SelectedCellChangeEvent event ) {
cellSelected( event.getCellSelectionDetail() );
}
public void onDeleteRow( DeleteRowEvent event ) {
Scheduler.get().scheduleDeferred( new Command() {
public void execute() {
assertDimensions();
}
} );
}
public void onInsertRow( InsertRowEvent event ) {
Scheduler.get().scheduleDeferred( new Command() {
public void execute() {
assertDimensions();
}
} );
}
public void onAppendRow( AppendRowEvent event ) {
Scheduler.get().scheduleDeferred( new Command() {
public void execute() {
assertDimensions();
}
} );
}
}