/**
* Copyright (c) 2014 - 2017 Frank Appel
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Frank Appel - initial API and implementation
*/
package com.codeaffine.eclipse.swt.widget.scrollable;
import static com.codeaffine.eclipse.swt.util.ControlReflectionUtil.$;
import static com.codeaffine.eclipse.swt.util.ControlReflectionUtil.CREATE_WIDGET;
import static com.codeaffine.eclipse.swt.util.ControlReflectionUtil.DISPLAY;
import static com.codeaffine.eclipse.swt.util.ControlReflectionUtil.PARENT;
import static com.codeaffine.eclipse.swt.util.ControlReflectionUtil.STYLE;
import static com.codeaffine.eclipse.swt.util.Platform.PlatformType.WIN32;
import static java.lang.Integer.valueOf;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;
import java.util.Collection;
import java.util.Optional;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Scrollable;
import com.codeaffine.eclipse.swt.util.ControlReflectionUtil;
import com.codeaffine.eclipse.swt.util.Platform;
import com.codeaffine.eclipse.swt.util.PlatformSupport;
public class ScrollableAdapterFactory {
static final String ADAPTED = ScrollableAdapterFactory.class.getName() + "#adapted";
static final Collection<Class<?>> SUPPORTED_TYPES = supportedTypes();
private final ControlReflectionUtil reflectionUtil;
private final PlatformSupport platformSupport;
public interface Adapter<S extends Scrollable> {
void adapt( S scrollable, PlatformSupport platformSupport );
S getScrollable();
}
public ScrollableAdapterFactory() {
reflectionUtil = new ControlReflectionUtil();
platformSupport = new PlatformSupport( WIN32 );
}
public <S extends Scrollable, A extends Scrollable & Adapter<S>> Optional<A> create( S scrollable, Class<A> type ) {
if( !usesScrollbars( scrollable ) ) {
return Optional.empty();
}
ensureThatTypeIsSupported( type );
return adapt( scrollable, type );
}
public <S extends Scrollable> void markAdapted( S scrollable ) {
scrollable.setData( ADAPTED, Boolean.TRUE );
}
public boolean isAdapted( Scrollable scrollable ) {
return Boolean.TRUE.equals( scrollable.getData( ADAPTED ) );
}
private static <S extends Scrollable> boolean usesScrollbars( S scrollable ) {
int scrollableStyle = scrollable.getStyle();
return ( scrollableStyle & SWT.H_SCROLL ) > 0 || ( scrollableStyle & SWT.V_SCROLL ) > 0;
}
private <A extends Scrollable & Adapter<S>, S extends Scrollable> Optional<A> adapt( S scrollable, Class<A> type ) {
Composite parent = scrollable.getParent();
int ordinalNumber = captureDrawingOrderOrdinalNumber( scrollable );
A result = createAdapter( scrollable, type );
if( platformSupport.isGranted() ) {
markAdapted( scrollable );
applyDrawingOrderOrdinalNumber( result, ordinalNumber );
}
result.adapt( scrollable, platformSupport );
if( platformSupport.isGranted() ) {
parent.layout();
result.setBackground( scrollable.getBackground() );
reflectionUtil.setField( scrollable, ControlReflectionUtil.PARENT, parent );
}
return Optional.of( result );
}
@SuppressWarnings("unchecked")
static <T extends Scrollable> LayoutFactory<T> createLayoutFactory(
Platform platform, LayoutMapping<T> ... mappings )
{
for( LayoutMapping<T> layoutMapping : mappings ) {
if( platform.matchesOneOf( layoutMapping.getPlatformTypes() ) ) {
return layoutMapping.getLayoutFactory();
}
}
return new NativeLayoutFactory<T>();
}
private static <A extends Adapter<? extends Scrollable>> void ensureThatTypeIsSupported( Class<A> type ) {
if( !SUPPORTED_TYPES.contains( type ) ) {
throw new IllegalArgumentException( format( "Scrollable type <%s> is not supported.", type ) );
}
}
private static int captureDrawingOrderOrdinalNumber( Control control ) {
for( int i = 0; i < control.getParent().getChildren().length; i++ ) {
if( control.getParent().getChildren()[ i ] == control ) {
return i;
}
}
throw new IllegalStateException( "Control is not contained in its parent's children list: " + control );
}
private <S extends Scrollable, A extends Scrollable & Adapter<S>> A createAdapter( S scrollable, Class<A> type ) {
int style = SWT.BORDER & scrollable.getStyle();
A result = reflectionUtil.newInstance( type );
if( platformSupport.isGranted() ) {
reflectionUtil.setField( result, DISPLAY, Display.getCurrent() );
reflectionUtil.setField( result, PARENT, scrollable.getParent() );
reflectionUtil.setField( result, STYLE, Integer.valueOf( style ) );
reflectionUtil.invoke( result, CREATE_WIDGET, $( valueOf( 0 ), int.class ) );
}
return result;
}
private static void applyDrawingOrderOrdinalNumber( Control control, int ordinalNumber ) {
control.moveAbove( control.getParent().getChildren()[ ordinalNumber ] );
}
private static Collection<Class<?>> supportedTypes() {
return unmodifiableList( asList(
TreeAdapter.class,
TableAdapter.class,
StyledTextAdapter.class,
ScrolledCompositeAdapter.class ) );
}
}