--- /dev/null
+/*
+ * Common
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Jihoon Song <jihoon80.song@samsung.com>
+ *
+ * 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.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ */
+
+package org.tizen.common.ui.dialog;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.window.IShellProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.tizen.common.core.command.InputValidator;
+import org.tizen.common.core.command.PrompterConstants;
+import org.tizen.common.core.command.UserField;
+import org.tizen.common.util.Assert;
+import org.tizen.common.util.CollectionUtil;
+import org.tizen.common.util.SWTUtil;
+
+
+/**
+ * Dialog for creating control dynamically using UserField models.
+ * Types of "String, char[], boolean" are currently supported.
+ *
+ * @author JIhoon Song {@literal<jihoon80.song@samsung.com>}
+ */
+public class DynamicDialog extends Dialog {
+
+ protected final Logger logger = LoggerFactory.getLogger( getClass() );
+
+ protected String title = "Dynamic Dialog"; // default. If you want to change, use setter.
+ protected int width = 500; // default. This is fixed currently.
+
+ // inputted fields
+ protected Collection<UserField> fields;
+
+ // ID based controls for searching
+ protected Map<String, Control> controlMap = new HashMap<String, Control>();
+
+
+ /////////////////////////////////////////////////////////////////
+ // Constructor
+
+ /**
+ * Default constructor for dynamic generation
+ * @param parent parent shell
+ * @param userFields {@link UserField}
+ */
+ public DynamicDialog(Shell parent, Collection<UserField> fields) {
+ super( parent );
+ Assert.notNull( fields );
+
+ this.fields = fields;
+ }
+
+ public DynamicDialog(IShellProvider parentShell, Collection<UserField> fields) {
+ super( parentShell );
+ Assert.notNull( fields );
+
+ this.fields = fields;
+ }
+
+ /**
+ * Constructor for dynamic generation using current active shell
+ * @param userFields {@link UserField}
+ */
+ public DynamicDialog(Collection<UserField> fields) {
+ this( SWTUtil.getActiveShell(), fields );
+ }
+
+
+ /////////////////////////////////////////////////////////////////
+ // Dialog logic
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ @Override
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+
+ newShell.setText( getTitle() );
+ }
+
+ @Override
+ protected Point getInitialSize() {
+ Point initialSize = super.getInitialSize();
+
+ initialSize.x = this.width;
+
+ return initialSize;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#createContents(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createContents(Composite parent) {
+ // create super dialog control
+ Composite superContents = (Composite) super.createContents( parent );
+
+ Control dialogArea = this.getDialogArea();
+ boolean firstControl = true;
+ for ( UserField field : this.fields ) {
+ // create dynamic dialog control
+ Control control = createDynamicControl( (Composite) dialogArea, field );
+
+ // set focus to first control
+ if ( firstControl && control != null ) {
+ firstControl = false;
+ control.setFocus();
+ if ( control instanceof Text ) {
+ ((Text) control).selectAll();
+ }
+ }
+
+ // initialize child's status
+ if ( control instanceof Button ) {
+ enableChildControls( field.getId(), getBoolean( field.getValue() ) );
+ }
+ }
+
+ return superContents;
+ }
+
+ /**
+ * Dynamically create control with field information
+ * @param parent control
+ * @return created control
+ */
+ protected Control createDynamicControl(Composite parent, final UserField field) {
+ Control control = null;
+
+ // filtering unsupported field
+ if ( !isSupportField( field ) ) {
+ return null;
+ }
+
+ // create control
+ Class<?> type = field.getType();
+ if ( String.class.equals( type ) ) {
+ control = createStringTypeControl( parent, field );
+ } else if ( char[].class.equals( type ) || Character[].class.equals( type ) ) {
+ control = createCharacterTypeControl( parent, field );
+ } else if ( boolean.class.equals( type ) || Boolean.class.equals( type ) ) {
+ control = createBooleanTypeControl( parent, field );
+ } else {
+ throw new IllegalArgumentException( "Unsupported user field type." );
+ }
+
+ // create child controls
+ Collection<UserField> children = field.getChildren();
+ if ( children != null ) {
+ Composite composite = createComposite( parent, new GridLayout(), new GridData( GridData.FILL_BOTH ) );
+
+ for ( UserField childField : children ) {
+ createDynamicControl( composite, childField );
+ }
+ }
+
+ // return created control
+ return control;
+ }
+
+ /**
+ * If support list is nothing or the "eclipse" exist, allow control creation.
+ * @param field {@link UserField} with support list
+ * @return If can support, return true. otherwise, return false
+ */
+ protected boolean isSupportField(final UserField field) {
+ Collection<Object> supports = field.getSupports();
+ if ( null != supports && !supports.contains( PrompterConstants.SUPPORT_IDE ) ) {
+ logger.warn( "{} is not supported in {}", field, this );
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /////////////////////////////////////////////////////////////////
+ // style getter
+
+ /**
+ * If you want, override this.
+ * @return Composite's default style
+ */
+ public int getCompositeStyle() {
+ return SWT.NONE;
+ }
+
+ /**
+ * If you want, override this.
+ * @return Text's default style
+ */
+ public int getTextStyle() {
+ return SWT.SINGLE;
+ }
+
+
+ /**
+ * If you want, override this.
+ * @return Text's default style for password
+ */
+ public int getPasswordTextStyle() {
+ return getTextStyle() | SWT.PASSWORD;
+ }
+
+
+ /**
+ * If you want, override this.
+ * @return Label's default style
+ */
+ public int getLabelStyle() {
+ return SWT.WRAP;
+ }
+
+
+ /**
+ * If you want, override this.
+ * @return Button's default style for Check
+ */
+ public int getCheckButtonStyle() {
+ return SWT.CHECK | SWT.CENTER;
+ }
+
+
+ /////////////////////////////////////////////////////////////////
+ // create controls for type
+
+ protected Text createStringTypeControl(Composite parent, final UserField field) {
+ Composite composite = createComposite( parent, new GridLayout( 2, false ), new GridData( GridData.FILL_BOTH ) );
+
+ createLabel( composite, new GridData( GridData.HORIZONTAL_ALIGN_END ), field.getMessage(), field.canModify() );
+ Text text = createText( composite, new GridData( GridData.FILL_BOTH ), field.getValue(), field.canModify(), field.getValidator() );
+ text.addModifyListener( new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ Text modifiedText = ((Text) e.widget);
+ field.setValue( modifiedText.getText() ); // synchronize model
+ }
+ } );
+
+ this.controlMap.put( field.getId(), text );
+
+ return text;
+ }
+
+ protected Text createCharacterTypeControl(Composite parent, final UserField field) {
+ Composite composite = createComposite( parent, new GridLayout( 2, false ), new GridData( GridData.FILL_BOTH ) );
+
+ createLabel( composite, new GridData( GridData.HORIZONTAL_ALIGN_END ), field.getMessage(), field.canModify() );
+ Text text = createPasswordText( composite, new GridData( GridData.FILL_BOTH ), field.getValue(), field.canModify(), field.getValidator() );
+ text.addModifyListener( new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ Text modifiedText = ((Text) e.widget);
+ field.setValue( modifiedText.getText().toCharArray() ); // synchronize model
+ }
+ } );
+
+ this.controlMap.put( field.getId(), text );
+
+ return text;
+ }
+
+ protected Button createBooleanTypeControl(Composite parent, final UserField field) {
+ Button button = createCheckButton( parent, new GridData( GridData.CENTER ), field.getId(), field.getMessage(), field.getValue(), field.canModify() );
+ button.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ Button modifiedButton = ((Button) e.widget);
+ field.setValue( modifiedButton.getSelection() ); // synchronize model
+ }
+ } );
+
+ this.controlMap.put( field.getId(), button );
+
+ return button;
+ }
+
+
+ /////////////////////////////////////////////////////////////////
+ // control creation utility
+
+ private Composite createComposite(Composite parent, Layout layout, Object layoutData) {
+ Composite composite = new Composite( parent, getCompositeStyle() ) {
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+
+ for ( Control children : getChildren() ) {
+ if ( controlMap.containsValue( children ) ) {
+ String controlId = getKeyByValue( controlMap, children );
+ UserField userField = getModel( fields, controlId );
+
+ if ( !userField.canModify() ) {
+ continue;
+ }
+ }
+ children.setEnabled( enabled );
+ }
+ }
+ };
+ composite.setLayout( layout );
+ composite.setLayoutData( layoutData );
+
+ return composite;
+ }
+
+ private Label createLabel(Composite parent, Object layoutData, String msg, boolean enabled) {
+ Label label = new Label( parent, getLabelStyle() );
+ label.setLayoutData( layoutData );
+ label.setText( msg );
+ label.setEnabled( enabled );
+
+ return label;
+ }
+
+ private Text createText(Composite parent, Object layoutData, Object defaultText, boolean enabled, final InputValidator validator) {
+ Text text = new Text( parent, getTextStyle() );
+ text.setLayoutData( layoutData );
+ if ( defaultText instanceof String ) {
+ text.setText( (String) defaultText );
+ }
+ text.setEnabled( enabled );
+ // TODO : add validation logic
+
+ return text;
+ }
+
+ private Text createPasswordText(Composite parent, Object layoutData, Object defaultText, boolean enabled, final InputValidator validator) {
+ Text text = new Text( parent, getPasswordTextStyle() );
+ text.setLayoutData( layoutData );
+ if ( defaultText instanceof char[] ) {
+ text.setText( new String( (char[]) defaultText ) );
+ } else if ( defaultText instanceof Character[] ) {
+ text.setText( ((Character[]) defaultText).toString() );
+ }
+ text.setEnabled( enabled );
+ // TODO : add validation logic
+
+ return text;
+ }
+
+ private Button createCheckButton(Composite parent, Object layoutData, final String id, String msg, Object value, boolean enabled) {
+ Button button = new Button( parent, getCheckButtonStyle() );
+ button.setLayoutData( layoutData );
+ button.setText( msg );
+ button.setEnabled( enabled );
+ button.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ enableChildControls( id, ((Button) e.widget).getSelection() );
+ }
+ });
+ button.setSelection( getBoolean( value ) );
+
+ return button;
+ }
+
+
+ /////////////////////////////////////////////////////////////////
+ // model utility
+
+ /**
+ * Get key by value in the map
+ * @param map
+ * @param value
+ * @return
+ */
+ private <T, E> T getKeyByValue(Map<T, E> map, E value) {
+ Assert.notNull( map );
+ Assert.notNull( value );
+
+ for ( Entry<T, E> entry : map.entrySet() ) {
+ if ( value.equals( entry.getValue() ) ) {
+ return entry.getKey();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get boolean by object value
+ * @param obj
+ * @return If obj is null, return true. if obj is not boolean type, return false. otherwise, return parsed boolean value.
+ */
+ private boolean getBoolean(Object obj) {
+ return ( obj == null ) ? true : Boolean.parseBoolean( String.valueOf( obj ) );
+ }
+
+ private void enableChildControls(final String id, boolean enabled) {
+ Collection<UserField> childIds = null;
+ for ( UserField field : this.fields ) {
+ childIds = getChildModelById( field, id );
+ if ( !CollectionUtil.isEmpty( childIds ) ) {
+ break;
+ }
+ }
+
+ if ( !CollectionUtil.isEmpty( childIds ) ) {
+ for ( UserField childId : childIds ) {
+ if ( !childId.canModify() && enabled == true ) {
+ continue;
+ }
+
+ Control control = this.controlMap.get( childId.getId() );
+ if ( ! ( control instanceof Composite) ) {
+ control = control.getParent();
+ }
+ control.setEnabled( enabled );
+ }
+ }
+ }
+
+ private UserField getModel(Collection<UserField> fields, String id) {
+ for ( UserField field : fields ) {
+ if ( field.getId().equals( id ) ) {
+ return field;
+ }
+
+ if ( !CollectionUtil.isEmpty( field.getChildren() ) ) {
+ UserField result = getModel( field.getChildren(), id);
+ if ( result != null ) {
+ return result;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private Collection<UserField> getChildModelById(final UserField field, final String id) {
+ Assert.notNull( id );
+
+ Collection<UserField> children = field.getChildren();
+ if ( CollectionUtil.isEmpty( field.getChildren() )) {
+ return null;
+ }
+
+ if ( id.equals( field.getId() ) ) {
+ return children;
+ }
+
+ for ( UserField child : children ) {
+ Collection<UserField> childResult = getChildModelById( child, child.getId() );
+ if ( !CollectionUtil.isEmpty( childResult ) ) {
+ return childResult;
+ }
+ }
+
+ return null;
+ }
+}