Eclipseプラグインを作ってみよう!

第9回 画面の作成(4)

この記事を読むのに必要な時間:およそ 10 分

[上へ][下へ]ボタン

最後に[上へ]ボタン,⁠下へ]ボタンを実装します。フィールドの順序の入れ替えはTableViewerクラスのreplace()メソッドを使用して行います。最後に入れ替えたFieldオブジェクトを選択することで,連続して移動できるようにします。

[上へ]ボタン,⁠下へ]ボタンのイベント

private void createContents(Section section, FormToolkit toolkit) {
    ...
    Button upButton = toolkit.createButton(buttons, "上へ", SWT.PUSH);
    layoutData = new GridData();
    layoutData.horizontalAlignment = GridData.FILL;
    upButton.setLayoutData(layoutData);
    upButton.addSelectionListener(new SelectionListener() {
        public void widgetSelected(SelectionEvent e) {
            int index = viewer.getTable().getSelectionIndex();
            if (index == 0) {
                return;
            }

            Field tmpField = (Field) viewer.getElementAt(index - 1);
            viewer.replace(viewer.getElementAt(index), index - 1);
            viewer.replace(tmpField, index);

            viewer.setSelection(
                    new StructuredSelection((Field) viewer.getElementAt(index - 1)));
        }

        public void widgetDefaultSelected(SelectionEvent e) {
        }
    });
    
    Button downButton = toolkit.createButton(buttons, "下へ", SWT.PUSH);
    layoutData = new GridData();
    layoutData.horizontalAlignment = GridData.FILL;
    downButton.setLayoutData(layoutData);
    downButton.addSelectionListener(new SelectionListener() {
        public void widgetSelected(SelectionEvent e) {
            int index = viewer.getTable().getSelectionIndex();
            if (index + 1 == viewer.getTable().getItemCount()) {
                return;
            }

            Field tmpField = (Field) viewer.getElementAt(index + 1);
            viewer.replace(viewer.getElementAt(index), index + 1);
            viewer.replace(tmpField, index);

            viewer.setSelection(
                    new StructuredSelection((Field) viewer.getElementAt(index + 1)));
        }

        public void widgetDefaultSelected(SelectionEvent e) {
        }
    });
    ...
}

それでは実行してみましょう。Master(一覧)のテーブルから移動したいフィールドを選択し,⁠上へ]ボタン,⁠下へ]ボタンをクリックすると,フィールドが上下に移動します。

[上へ]ボタン,⁠下へ]ボタンに合わせてフィールドが上下に移動する

[上へ]ボタン,[下へ]ボタンに合わせてフィールドが上下に移動する

Details(詳細)の変更の反映

Master(一覧)からDetails(詳細)への変更通知はManagedFormオブジェクトを介して行いました。同じくDetails(詳細)からMaster(一覧)への変更通知もManagedFormオブジェクトを介して行うことにします。

ManagedFormクラスのfireSelectionChanged()メソッドの実装を見るとわかるのですが,addPart()メソッドで追加されたフォームパートのうちIPartSelectionListenerインタフェースを実装しているものを順番に呼び出しています。そこでMaster(一覧)でも変更通知イベントを受け取れるように,FieldsMasterSectionPartクラスでIPartSelectionListenerインタフェースを実装します。

IPartSelectionListenerインタフェースはselectionChanged()メソッドを定義しているので,これを実装する必要があります。このメソッドではTableViewerクラスのrefresh()メソッドを使って,テーブルのデータを更新します。これに伴い, TableViewerオブジェクトをfViewerとしてFieldsMasterSectionPartクラスのプロパティーに変更します。また,変更通知元が自分自身の場合には処理を行わないようにします。

FieldsMasterSectionPartクラス

public class FieldsMasterSectionPart extends SectionPart implements IPartSelectionListener {
    private TableViewer fViewer;
    ...
    private void createContents(Section section, FormToolkit toolkit) {
        ...
        fViewer = new TableViewer(table);
        ...
        fViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                FieldsMasterSectionPart.this.getManagedForm().fireSelectionChanged(
                                FieldsMasterSectionPart.this,  
                                event.getSelection());
            }
        });
        ...
        section.setClient(composite);

        getManagedForm().addPart(this);

        fViewer.setInput(createSample());
    }
    ...
    public void selectionChanged(IFormPart part, ISelection selection) {
        if (part == this) {
            return;
        }
        if (!(selection instanceof IStructuredSelection)) {
            return;
        }

        fViewer.refresh((Field) ((IStructuredSelection) selection).getFirstElement());
    }
}

次に変更を通知するDetails(詳細)の実装を行います。まず,どのタイミングで変更を通知するかですが,今回は各ウィジェットがフォーカスを失ったタイミングで通知することにします。またFieldsMasterSectionPartクラスの実装と同じく,通知元が自分自身の場合は処理を行わないようにします。

FieldDetailsPageクラス

public class FieldDetailsPage implements IDetailsPage {
    private Field fField;

    private SectionPart fSectionPart;
    ...
    public void createContents(Composite parent) {
        ...
        layoutData = new GridData();
        layoutData.grabExcessHorizontalSpace = true;
        layoutData.horizontalAlignment = GridData.FILL;
        composite.setLayoutData(layoutData);

        fSectionPart = new SectionPart(section);

        FocusListener focusListener = new FocusListener() {
            public void focusGained(FocusEvent e) {
            }

            public void focusLost(FocusEvent event) {
                if (fNameText == event.widget) {
                    fField.setName(fNameText.getText());
                } else if (fDescriptionText == event.widget) {
                    fField.setDescription(fDescriptionText.getText());
                } else if (fRequiredYesButton == event.widget
                           || fRequiredNoButton == event.widget) {
                    fField.setRequired(fRequiredYesButton.getSelection());
                } else if (fMessageText == event.widget) {
                    fField.setMessage(fMessageText.getText());
                }

                fForm.fireSelectionChanged(
                            fSectionPart, 
                            new StructuredSelection(fField));
            }
        };

        toolkit.createLabel(composite, "フィールド名");
        ...
        fNameText.addFocusListener(focusListener);

        toolkit.createLabel(composite, "概要");
        ...
        fDescriptionText.addFocusListener(focusListener);

        toolkit.createLabel(composite, "必須");
        ...
        fRequiredYesButton.addFocusListener(focusListener);
        fRequiredNoButton.addFocusListener(focusListener);
        
        toolkit.createLabel(composite, "メッセージ");
        ...
        fMessageText.addFocusListener(focusListener);

        section.setClient(composite);
    }
    ...
    public void selectionChanged(IFormPart part,
                                 ISelection selection) {
        if (fSectionPart == part) {
            return;
        }
        ...
        fField = field;
    }
}

それでは実行してみましょう。フィールド名や概要を変更して,フォーカスを移すと即座にテーブルに反映されるのがわかります。また別のフィールドを選択して,再度変更したフィールドを選択しても変更した内容が保持されています。これはTableViewerオブジェクトがデータをFieldオブジェクトとして管理しているためです。このようにテーブル,Master(一覧⁠⁠,Details(詳細)の間のデータのやりとりをFieldオブジェクトで行うことができるため,データとユーザインタフェースの間をやりとりするコードをほとんど実装する必要がありません。

Details(詳細)の変更がMaster(一覧)に反映されている

Details(詳細)の変更がMaster(一覧)に反映されている

おわりに

今回はテーブル,Master(一覧⁠⁠,Details(詳細)といったユーザーインタフェースとFieldオブジェクトの連携部分を実装しました。従来のUIプログラミングでは煩雑であったUI部品とオブジェクトとの連携が,TableViewerクラスやManagedFormクラスを介して行うことで,スムーズに行われていることをご理解いただけたと思います。

次回はバリデータ定義フォームの実装を説明します。

著者プロフィール

松藤秀治(まつふじひではる)

Piece Frameworkのプログラマー。担当はEclipseのプラグインとして開発されているPiece Frameworkの統合開発環境Piece_IDE。2007年5月に株式会社アイテマンを設立。