레이블이 SWT인 게시물을 표시합니다. 모든 게시물 표시
레이블이 SWT인 게시물을 표시합니다. 모든 게시물 표시

2010년 2월 25일 목요일

ElementTreeSelectionDialog 사용하기

org.eclipse.ui.dialogs 에는 이클립스에서 제공하는 유용한 다이얼로그들이 많다.
ElementTreeSelectionDialog는 트리 형태의 데이터를 출력한 후 사용자가 선택을 반영할 수 있는 다이얼로그다.
ContentProvider와 LabelProvider를 생성해 주어야 한다.
아래와 같이 간단하게 TreeSelection 다이얼로그를 사용할 수 있다.




SqlMapConfigParser parser = new SqlMapConfigParser();

SqlMapConfig sqlMapConfig =
parser.parse(ProjectHelper.getSqlMapConfigFile(javaProject.getProject()).getContents());


ElementTreeSelectionDialog dialog =
new ElementTreeSelectionDialog(getShell(), new SqlMapLabelProvider(),
new SqlMapContentProvider());

dialog.setTitle("dialog title");
dialog.setMessage("dialog message");

dialog.setInput(sqlMapConfig);
if(dialog.open() == Window.OK) {
for(Object selected: dialog.getResult()) {
if(selected instanceof SqlMap.IbatisQuery) {
ibatisStatements.add((SqlMap.IbatisQuery)selected);
}
}
tableViewer.refresh();
}





ElementTreeSelectionDialog를 사용할 때 트리의 최하단 노드만 선택할 수 있게 하거나, 선택된 노드들의 validation을 선택하게 만들면 사용하기 편할때가 있다. 이럴 경우에는 ISelectionStatusValidator 를 이용하면 된다. 아래의 코드를 참조하기 바란다.



dialog.setValidator(new ISelectionStatusValidator() {

@Override
public IStatus validate(Object[] selection) {
for(Object obj : selection) {
if (!(obj instanceof MethodDeclaration)) {
return new Status(IStatus.ERROR, IRuntimeConstants.PI_RUNTIME, IStatus.ERROR, "Only methods can be selected.", null);
}
}
return Status.OK_STATUS;
}
});




Dialog의 창 크기를 명시적으로 지정하고자 한다면 org.eclipse.pde.internal.ui.util.SWTUtil 를 이용하면 된다. SWTUtil 이름으로 된 클래스는 한 두개가 아니라서 fullyQualifiedName으로 알아두지 않으면 종종 귀찮은 일이 생긴다. SWTUtil.setDialogSize() 를 호출하기 전에 반드시 dialog.create() 먼저 해 줘야 NullPointerException이 발생하지 않는 다는 것도 알아두기 바란다.



dialog.create();

SWTUtil.setDialogSize(dialog, 600, 400);






Resources:
http://blog.cypal-solutions.com/2008/07/selection-dialogs-in-eclipse.html

SWT Table Cell Editor 사용

SWT Table의 기본 셀 에디터는 TextCellEditor 다
셀이 선택되었을 때 Text 입력 모드로 변경이 되고, 엔터키가 입력되거나 포커스를 잃게 되었을때 Text안의 문자열로 값이 설정된다.
리스트에서 선택하는 경우에는 ComboCellEditor를 사용하면 된다.

나는 메소드의 파라미터에 대한 타입을 지정하는 셀에디터가 필요했다.
Primitive 타입은 Combo를 이용하여 선택할 수 있도록 하고,
클래스는 찾기 버튼을 클릭할때 JDT의 JavaUI.createTypeDialog() 메소드를 이용하여 별도의 다이얼로그에서 선택할 수 있도록 해야했다.
또한 배열타입을 고려하여 사용자가 직접 값을 입력할 필요도 있었다. (이부분은 Combo 의 스타일만 적용하면 된다.)

이와 같은 요구사항을 종합해 보면 메소드 파라미터 목록은 테이블의 각 아이템으로 설정이 가능하며,
파라미터 타입은 별도의 CellEditor를 구현해야만 하고, 파라미터명은 TextCellEditor를 이용하여 구현하면 된다.
타입을 설정하기 위한 CellEditor는 Combo와 버튼이 있어야 하고 Combo에는 Primitive 타입을 선택할 수 있도록 하고 버튼이 클릭되면 타입(Class, Interface, Enum 등)을 선택할 수 있는 다이얼로그 창이 열린 후 선택된 값으로 설정되도록 해야 한다.


1. 테이블의 각 셀에 셀 에디터 설정하기


protected void setupViewer(TableViewer tableViewer) {
Table table = tableViewer.getTable();

table.addListener(SWT.MeasureItem, measureListener = new Listener() {

@Override
public void handleEvent(Event event) {
event.height = event.gc.getFontMetrics().getHeight() * 2;
}
});
TableViewerColumn column = null;

column = new TableViewerColumn(tableViewer, SWT.NONE);
column.getColumn().setText("parameter type");
column.getColumn().setWidth(200);

EditingSupport editingSupport = new ParameterEditingSupport(viewer, MethodParameterColumn.PARAMETER_TYPE);
column.setLabelProvider(new ParameterLabelProvider(MethodParameterColumn.PARAMETER_TYPE));
column.setEditingSupport(editingSupport);


column = new TableViewerColumn(tableViewer, SWT.NONE);
column.getColumn().setText("name");
column.getColumn().setWidth(200);
column.setLabelProvider(new ParameterLabelProvider(MethodParameterColumn.PARAMETER_NAME));
column.setEditingSupport(new ParameterEditingSupport(viewer, MethodParameterColumn.PARAMETER_NAME));

table.setLinesVisible(true);
table.setHeaderVisible(true);
}

2. EditingSupport 클래스 구현하기

class ParameterEditingSupport extends EditingSupport {
private final MethodParameterColumn column;
private boolean editable;

public ParameterEditingSupport(ColumnViewer viewer, MethodParameterColumn column) {
super(viewer);
this.column = column;
init();
}

@Override
protected boolean canEdit(Object element) {
return this.editable;
}

@Override
protected CellEditor getCellEditor(Object element) {

if (element instanceof MethodParameter) {
final MethodParameter parameter = (MethodParameter) element;

switch(column) {
case PARAMETER_TYPE:
final CellEditor cellEditor = new TypeSelectionDialogCellEditor(viewer.getTable(), javaProject);
cellEditor.addListener(new ICellEditorListener() {

@Override
public void applyEditorValue() {
System.out.println("applyEditorValue()");
System.out.println("value: " + cellEditor.getValue());
parameter.setType((String)cellEditor.getValue());
manager.refresh();
}

@Override
public void cancelEditor() {
System.out.println("cancelEditor()");
}

@Override
public void editorValueChanged(boolean oldValidState,
boolean newValidState) {
System.out.println("editorValueChanged: " + oldValidState + ", " + newValidState);
}

});

return cellEditor;
case PARAMETER_NAME:
this.editable = true;
return new TextCellEditor(viewer.getTable());
}

}
return new TextCellEditor(viewer.getTable());
}

@Override
protected Object getValue(Object element) {
if (element instanceof MethodParameter) {
MethodParameter cast = (MethodParameter) element;
return MethodParameterManagerHelper.getValue(cast, column);
}
return "NULL";
}

@Override
protected void setValue(Object element, Object value) {
if (element instanceof MethodParameter) {
MethodParameter cast = (MethodParameter) element;

MethodParameterManagerHelper.setValue(value, cast, column);
getViewer().update(element, MethodParameterColumn.getColumnProperties());
getViewer().refresh(element);
return;
}
throw new IllegalArgumentException("invalid element: " + element);
}

private void init() {
switch(column) {
case PARAMETER_TYPE:
this.editable = true;
break;
case PARAMETER_NAME:
this.editable = true;
break;
}
}
}


3. CellEditor 구현하기
각 셀에 대한 에디터는 getCellEditor() 메소드에 의해서 결정된다. parameterType에 대한 CellEditor는 위의 코드에서 TypeSelectionDialogCellEditor 이다.
클래스에 대한 소스 코드 양이 많지 않으니 모두 포함하도록 한다.


package com.archnal.amf.ui.wizards;


import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.ui.IJavaElementSearchConstants;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.viewers.DialogCellEditor;
import org.eclipse.jface.window.Window;
import org.eclipse.pde.internal.ui.PDEPlugin;
import org.eclipse.pde.internal.ui.PDEUIMessages;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.SelectionDialog;

public class TypeSelectionDialogCellEditor extends DialogCellEditor {
private static final String[] PRIMITIVE_PARAMETER_TYPES = {
"boolean",
"byte",
"char",
"double",
"float",
"int",
"long",
"short"
};
private IJavaProject javaProject;
private Combo combo;
private SelectionListener selectionListener;


public TypeSelectionDialogCellEditor(Composite parent, IJavaProject javaProject) {
super(parent);
this.javaProject = javaProject;
}

@Override
public void dispose() {
combo.removeSelectionListener(selectionListener);

super.dispose();
}
@Override
protected Control createContents(Composite cell) {
// return super.createContents(cell);

Composite composite = new Composite(cell, getStyle());
composite.setBackground(cell.getBackground());

GridLayout layout = new GridLayout(1, false);
layout.marginWidth = 0;
layout.marginHeight = 0;
composite.setLayout(layout);

GridData gd = new GridData(GridData.FILL_HORIZONTAL);

combo = new Combo(composite, SWT.NONE);
combo.setItems(PRIMITIVE_PARAMETER_TYPES);
combo.setLayoutData(gd);

combo.addSelectionListener(selectionListener = new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {

markDirty();

String value = combo.getText();
doSetValue(value);

fireApplyEditorValue();
}
});
return composite;
}

@Override
protected Object openDialogBox(Control cellEditorWindow) {
// TODO Auto-generated method stub
try {
final int scopeType = IJavaElementSearchConstants.CONSIDER_ALL_TYPES;
// String filter = getReturnType();
String filter = getValue() == null ? "" : getValue().toString();

// filter = filter.substring(filter.lastIndexOf(".") + 1); //$NON-NLS-1$
// SelectionDialog dialog = JavaUI.createTypeDialog(PDEPlugin.getActiveWorkbenchShell(), PlatformUI.getWorkbench().getProgressService(), SearchEngine.createWorkspaceScope(), scopeType, false, filter);
SelectionDialog dialog = JavaUI.createTypeDialog(PDEPlugin.getActiveWorkbenchShell(), PlatformUI.getWorkbench().getProgressService(), SearchEngine.createJavaSearchScope(new IJavaElement[] { javaProject }), scopeType, false, filter);
dialog.setTitle(PDEUIMessages.GeneralInfoSection_selectionTitle);
if (dialog.open() == Window.OK) {
IType type = (IType) dialog.getResult()[0];
return type.getFullyQualifiedName('$');
// returnTypeCombo.setText(type.getFullyQualifiedName('$'));
// entry.setValue(type.getFullyQualifiedName('$'));
// entry.commit();
}
} catch (CoreException e) {
e.printStackTrace();
}
return null;
}

}


위의 소스 코드에서 다음과 같은 사항은 주의해서 볼 필요가 있다.
TypeSelectionDialogCellEditor.openDialogBox()에서
doSetValue() 메소드 호출 후 fireApplyEditorValue() 메소드를 호출하여
셀의 값이 변경되었음을 누군가에게 통지하고 있다.
어딘가에서는 그 값을 변경하여 모델이 되는 파라미터 타입 값을 변경해 주어야 한다.
ParameterEditingSupport 클래스의 getCellEditor() 메소드에 파라미터 타입에 대한 셀 에디터 생성 시 ICellEditorListener를 등록하여 applyEditorValue()메소드에서 값이 변경될 때마다 파라미터 타입에 변경해주고 있음을 알 수 있다.

SWT Table cell 높이, 넓이 변경

SWT Table을 이용하여 프로그램을 하다보면 일반적으로 셀 높이를 변경할 필요가 없지만,
셀에디터를 사용하여 셀을 편집해야 할 경우에 콤보박스가 일부 잘려 보이기도 한다.
이런 경우에는 Table의 cell 높이를 늘려 주어야 UI가 보기 좋게 된다.

아래와 같이 코드를 작성하면 테이블의 셀 크기를 변경할 수 있다.

Table table = tableViewer.getTable();

table.addListener(SWT.MeasureItem, measureListener = new Listener() {

@Override
public void handleEvent(Event event) {
event.height = event.gc.getFontMetrics().getHeight() * 2;
}
});
물론 폭도 설정가능하다.


UI 프로그래밍에서 쓸데없이 메모리를 사용하지 않도록 등록된 리스너는 반드시 제거시키는 것도 잊지 말아야 한다.

viewer.getTable().removeListener(SWT.MeasureItem, measureListener);


Resources:
http://lekkimworld.com/2008/03/27/setting_table_row_height_in_swt.html

http://www.eclipse.org/articles/article.php?file=Article-CustomDrawingTableAndTreeItems/index.html