/**
* @version $Id: SortedFilterList.java 1839 2014-04-16 02:33:51Z yukihiro-kinjyo $
*
* 2011/11/22 11:49:53
* @author kousuke-morishima
*
* Copyright 2011-2014 TIDAコンソーシアム All Rights Reserved.
*/
package com.tida_okinawa.corona.internal.ui.component;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import com.tida_okinawa.corona.internal.ui.util.DelayTimer;
/**
* @author kousuke-morishima
*/
public class SortedFilterList {
// TODO Jobを使って、表示を動的に更新するようにしたい。「参考:FilteredList」
private boolean checkStyle;
/**
* フィルタ用のテキストボックス
*/
StyledText filterText;
/**
* フィルタ用のテキストボックスに出すヒント文字列
*/
String hint;
/**
* リストに表示する文字列を提供する
*/
LabelProvider labelProvider;
/**
* フィルタ結果を表示するリスト
*/
TableViewer itemList;
/**
* デフォルトラベルプロバイダー({@link LabelProvider})を使用する
*
* @param parent
* @param style
* TableViewerに指定できるスタイル
*/
public SortedFilterList(Composite parent, int style) {
this(parent, style, new LabelProvider());
}
/**
* デフォルトラベルプロバイダーとデフォルトスタイルを使用する
*
* @param parent
*/
public SortedFilterList(Composite parent) {
this(parent, SWT.BORDER | SWT.SINGLE);
}
/**
* @param parent
* @param style
* TableViewerに指定できるスタイル
* @param labelProvider
* must not null
*/
public SortedFilterList(Composite parent, int style, LabelProvider labelProvider) {
checkStyle = (style & SWT.CHECK) != 0;
setHintText("値を絞り込みます");
this.labelProvider = labelProvider;
createContent(parent, style, labelProvider);
}
protected void createContent(Composite parent, int style, LabelProvider labelProvider) {
parent = CompositeUtil.defaultComposite(parent, 1);
createFilterText(parent);
createContentList(parent, style, new ArrayContentProvider(), labelProvider);
itemList.getTable().addSelectionListener(selectedItemCount);
}
/* ****************************************
* Filter
*/
protected void createFilterText(Composite parent) {
filterText = new StyledText(parent, SWT.BORDER | SWT.SINGLE);
filterText.setLayoutData(CompositeUtil.gridData(true, false, 1, 1));
filterText.setFont(parent.getFont());
filterText.setBackground(new Color(null, 255, 255, 255));
filterText.addListener(SWT.Modify, new Listener() {
final Display display = Display.getCurrent();
DelayTimer timer = new DelayTimer("FilterTimer", new Runnable() {
@Override
public void run() {
display.asyncExec(new Runnable() {
@Override
public void run() {
if (!filterText.isDisposed()) {
setFilter(filterText.getText());
}
}
});
}
});
@Override
public void handleEvent(Event e) {
timer.run(200);
}
});
filterText.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.keyCode == SWT.ARROW_DOWN) {
itemList.getTable().setFocus();
}
}
});
initHint();
}
/**
* 表示アイテムを指定のフィルタで絞る
*
* @param filter
*/
public void setFilter(ViewerFilter filter) {
itemList.setFilters(new ViewerFilter[] { filter });
restoreCheckState();
}
/**
* 表示アイテムを指定された文字で絞る
*
* @param filterText
*/
public void setFilter(String filterText) {
if (showHint) {
return;
}
setFilter(createFilter(filterText));
}
protected ViewerFilter createFilter(final String filterText) {
return new ViewerFilter() {
@Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
String text = labelProvider.getText(element).trim().toUpperCase();
if (text.length() == 0) {
/* 空文字は空文字にのみヒット */
return "".equals(filterText);
}
if (filterText.contains("*")) {
String[] words = filterText.toUpperCase().split("\\*");
if (!filterText.startsWith("*")) {
/* アスターで始まっていなければ、先頭一致 */
if (!text.startsWith(words[0])) {
return false;
}
}
int from = 0;
for (String word : words) {
if (word.equals("")) {
continue;
}
int index = text.indexOf(word, from);
if (index == -1) {
return false;
}
from = index;
}
} else {
/* アスターを含まなければ先頭一致 */
return text.startsWith(filterText.toUpperCase());
}
return true;
}
};
}
/* ********************
* ヒント
*/
private static final Color COLOR_HINT = new Color(null, 160, 180, 200);
private static final Color COLOR_USER = new Color(null, 0, 0, 0);
boolean showHint = true;
/**
* @return フィルタ文字列の入力欄に表示しているヒント文字列
*/
public String getHintText() {
return hint;
}
/**
* @param hint
* フィルタ文字列の入力欄に表示するヒント文字列<br />
* must not null
*/
public void setHintText(String hint) {
Assert.isNotNull(hint);
this.hint = hint;
if (filterText != null) {
updateHintText(hint);
}
}
private void initHint() {
if (getHintText().length() > 0) {
filterText.setText(getHintText());
filterText.setForeground(COLOR_HINT);
}
filterText.addFocusListener(new FocusListener() {
@Override
public void focusLost(FocusEvent e) {
showHint = (filterText.getText().length() == 0);
if (showHint) {
setText(getHintText(), COLOR_HINT);
}
}
@Override
public void focusGained(FocusEvent e) {
if (showHint) {
setText("", COLOR_USER);
showHint = false;
}
}
private void setText(String text, Color foreground) {
filterText.setText(text);
filterText.setForeground(foreground);
}
});
}
private void updateHintText(String newHint) {
if (showHint) {
filterText.setText(newHint);
}
}
/* ********************
* list
*/
protected void createContentList(Composite parent, int style, IStructuredContentProvider contentProvider, LabelProvider labelProvider) {
itemList = new TableViewer(parent, style);
itemList.setContentProvider(contentProvider);
itemList.setLabelProvider(labelProvider);
itemList.setUseHashlookup(true);
}
/* ****************************************
* Interface
*/
/**
* 並びを指定する
*
* @param sorter
*/
public void setSorter(ViewerSorter sorter) {
itemList.setSorter(sorter);
}
private void restoreCheckState() {
for (Entry<Object, Object> e : checkedItems.entrySet()) {
TableItem item = (TableItem) itemList.testFindItem(e.getKey());
if (item != null) {
checkedItems.put(e.getKey(), PRESENT);
item.setChecked(true);
}
}
}
/* ********************
* Other Interface
*/
/**
* 指定しなければ {@link ArrayContentProvider}
*
* @param provider
*/
public void setContentProvider(IStructuredContentProvider provider) {
itemList.setContentProvider(provider);
}
public void setLabelProvider(LabelProvider labelProvider) {
this.labelProvider = labelProvider;
itemList.setLabelProvider(labelProvider);
}
public void setInput(Object input) {
itemList.setInput(input);
}
/* ****************************************
* チェックの実装
*/
static final Object PRESENT = new Object();
private SelectionListener selectedItemCount = new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (e.detail == SWT.CHECK) {
TableItem item = (TableItem) e.item;
if (item.getChecked()) {
checkedItems.put(item.getData(), PRESENT);
} else {
checkedItems.remove(item.getData());
}
}
}
};
Map<Object, Object> checkedItems = new HashMap<Object, Object>();
/**
* styleに{@link SWT#CHECK}を指定していないときは常に長さ0の配列が返る
*
* @return チェックされているアイテムすべて。
*/
public Object[] getChecked() {
if (checkStyle) {
return checkedItems.keySet().toArray();
}
return new Object[0];
}
/**
* styleに{@link SWT#CHECK}を指定していないときは何もしない
*
* @param checked
* すべてのアイテムにチェックを入れたり外したり
*/
public void setCheckedAll(boolean checked) {
if (checkStyle) {
doSetCheckedAll(checked);
}
}
protected void doSetCheckedAll(boolean checked) {
Table t = itemList.getTable();
doSetChecked(t.getItems(), checked);
}
/**
* elementにチェックを入れる。
* styleに{@link SWT#CHECK}を指定していないときは何もしない
*
* @param element
* @param checked
*/
public void setChecked(Object element, boolean checked) {
if (checkStyle) {
doSetChecked(new Object[] { element }, checked);
}
}
/**
* elementsすべてにチェックを入れる。
* styleに{@link SWT#CHECK}を指定していないときは何もしない
*
* @param elements
* @param checked
*/
public void setChecked(Object[] elements, boolean checked) {
if (checkStyle) {
doSetChecked(elements, checked);
}
}
protected void doSetChecked(Object[] elements, boolean checked) {
for (Object element : elements) {
TableItem item = null;
if (element instanceof TableItem) {
item = (TableItem) element;
element = item.getData();
} else {
item = (TableItem) itemList.testFindItem(element);
if (item == null) {
continue;
}
}
item.setChecked(checked);
if (checked) {
checkedItems.put(element, PRESENT);
} else {
checkedItems.remove(element);
}
}
}
/**
* @return 選択しているアイテムすべて
*/
public Object[] getSelected() {
TableItem[] items = itemList.getTable().getSelection();
Object[] ret = new Object[items.length];
for (int i = 0; i < ret.length; i++) {
ret[i] = items[i].getData();
}
return ret;
}
public void setFocus() {
filterText.setFocus();
}
/* ****************************************
* Listeners
*/
/**
* {@link Table}にSelectionListenerを追加する
*
* @param listener
*/
public void addSelectionListener(SelectionListener listener) {
itemList.getTable().addSelectionListener(listener);
}
/**
* {@link Table}からSelectionListenerを削除する
*
* @param listener
*/
public void removeSelectionListener(SelectionListener listener) {
itemList.getTable().removeSelectionListener(listener);
}
/* ****************************************
* Layout
*/
/**
* {@link Table}にLayoutを指定する
*
* @param layout
*/
public void setLayout(Layout layout) {
itemList.getTable().setLayout(layout);
}
/**
* {@link Table}にLayoutDataを指定する
*
* @param layoutData
*/
public void setLayoutData(Object layoutData) {
itemList.getTable().setLayoutData(layoutData);
}
/**
* ラベルリスト関連情報クリア処理
* 1.フィルタ用テキストボックスをクリア
* 2.チェックON情報を持つマップをクリア
* 3.リストの表示を初期化
* 4.フィルタ用テキストボックスにヒントを設定
*/
public void sortedFilterListClear() {
filterText.setText("");
checkedItems.clear();
setFilter(filterText.getText());
showHint = (filterText.getText().length() == 0);
if (showHint) {
filterText.setText(getHintText());
filterText.setForeground(COLOR_HINT);
}
}
}