org.eclipse.egit.ui.internal.search.CommitSearchPage.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.egit.ui.internal.search.CommitSearchPage.java

Source

/*******************************************************************************
 *  Copyright (c) 2011 GitHub Inc.
 *  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:
 *    Kevin Sawicki (GitHub Inc.) - initial API and implementation
 *******************************************************************************/
package org.eclipse.egit.ui.internal.search;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.PatternSyntaxException;

import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIUtils;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.repository.RepositoriesViewContentProvider;
import org.eclipse.egit.ui.internal.repository.RepositoriesViewLabelProvider;
import org.eclipse.egit.ui.internal.repository.tree.RepositoryNode;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.fieldassist.ComboContentAdapter;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.resource.JFaceColors;
import org.eclipse.jface.text.FindReplaceDocumentAdapter;
import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.search.ui.ISearchPage;
import org.eclipse.search.ui.ISearchPageContainer;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
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.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;

/**
 * Commit search dialog page class.
 */
public class CommitSearchPage extends DialogPage implements ISearchPage {

    /**
     * ID
     */
    public static final String ID = "org.eclipse.egit.ui.commitSearchPage"; //$NON-NLS-1$

    private static class SearchComposite extends Composite {

        /**
         * @param parent
         * @param style
         */
        public SearchComposite(Composite parent, int style) {
            super(parent, style);
        }

        public void setLayoutData(Object layoutData) {
            // Prevent search dialog from overriding the locally set data
            if (getLayoutData() == null)
                super.setLayoutData(layoutData);
        }
    }

    private static final int HISTORY_SIZE = 12;

    // Dialog store id constants
    private static final String PAGE_NAME = "GitCommitSearchPage"; //$NON-NLS-1$

    private static final String STORE_HISTORY = "HISTORY"; //$NON-NLS-1$

    private static final String STORE_HISTORY_SIZE = "HISTORY_SIZE"; //$NON-NLS-1$

    private List<CommitSearchSettings> fPreviousSearchPatterns = new ArrayList<CommitSearchSettings>(HISTORY_SIZE);

    private boolean firstTime = true;

    private Combo patternCombo;

    private Button isCaseSensitiveButton;

    private Button isRegExButton;

    private Button searchTreeButton;

    private Button searchCommitButton;

    private Button searchParentsButton;

    private Button searchAuthorButton;

    private Button searchCommitterButton;

    private Button searchMessageButton;

    private Button searchAllBranchesButton;

    private CLabel statusLabel;

    private Group repositoryGroup;

    private CheckboxTableViewer repositoryViewer;

    private ISearchPageContainer container;

    private ContentAssistCommandAdapter patternFieldContentAssist;

    private ISearchQuery newQuery() {
        CommitSearchSettings settings = new CommitSearchSettings();
        settings.setTextPattern(patternCombo.getText());
        settings.setRegExSearch(isRegExButton.getSelection());
        settings.setCaseSensitive(isCaseSensitiveButton.getSelection());
        settings.setMatchAuthor(searchAuthorButton.getSelection());
        settings.setMatchCommitter(searchCommitterButton.getSelection());
        settings.setMatchMessage(searchMessageButton.getSelection());
        settings.setMatchCommit(searchCommitButton.getSelection());
        settings.setMatchTree(searchTreeButton.getSelection());
        settings.setMatchParents(searchParentsButton.getSelection());
        settings.setAllBranches(searchAllBranchesButton.getSelection());
        for (Object checked : repositoryViewer.getCheckedElements())
            settings.addRepository(((RepositoryNode) checked).getRepository().getDirectory().getAbsolutePath());
        fPreviousSearchPatterns.add(0, settings);
        return new CommitSearchQuery(settings);
    }

    /**
     * Perform search by running query in background
     *
     * @see org.eclipse.search.ui.ISearchPage#performAction()
     */
    public boolean performAction() {
        NewSearchUI.runQueryInBackground(newQuery());
        return true;
    }

    private String[] getPreviousSearchPatterns() {
        int size = fPreviousSearchPatterns.size();
        String[] patterns = new String[size];
        for (int i = 0; i < size; i++)
            patterns[i] = fPreviousSearchPatterns.get(i).getTextPattern();
        return patterns;
    }

    public void setVisible(boolean visible) {
        if (visible && patternCombo != null) {
            if (firstTime) {
                firstTime = false;
                // Set item and text here to prevent page from resizing
                patternCombo.setItems(getPreviousSearchPatterns());
                if (!initializePatternControl()) {
                    patternCombo.select(0);
                    handleWidgetSelected();
                }
            }
            patternCombo.setFocus();
        }
        updateOKStatus();
        super.setVisible(visible);
    }

    private void updateOKStatus() {
        boolean status = validateRegex();
        if (status)
            status = validateScope();
        if (status)
            status = validateRepositories();
        getContainer().setPerformActionEnabled(status);
    }

    private boolean validateRepositories() {
        return this.repositoryViewer.getCheckedElements().length > 0;
    }

    private boolean validateScope() {
        return this.searchAuthorButton.getSelection() || this.searchCommitterButton.getSelection()
                || this.searchMessageButton.getSelection() || this.searchCommitButton.getSelection()
                || this.searchParentsButton.getSelection() || this.searchTreeButton.getSelection();
    }

    public void createControl(Composite parent) {
        readConfiguration();

        initializeDialogUnits(parent);
        SearchComposite result = new SearchComposite(parent, SWT.NONE);
        result.setFont(parent.getFont());
        GridDataFactory.fillDefaults().grab(true, true).applyTo(result);
        GridLayoutFactory.swtDefaults().numColumns(2).equalWidth(false).applyTo(result);
        addTextPatternControls(result);
        addScopeControls(result);
        addRepositoryControl(result);
        setControl(result);
        Dialog.applyDialogFont(result);
    }

    private boolean validateRegex() {
        if (isRegExButton.getSelection()) {
            try {
                PatternUtils.createPattern(patternCombo.getText(), isCaseSensitiveButton.getSelection(), true);
            } catch (PatternSyntaxException e) {
                String locMessage = e.getLocalizedMessage();
                int i = 0;
                while (i < locMessage.length() && "\n\r".indexOf(locMessage.charAt(i)) == -1) //$NON-NLS-1$
                    i++;
                statusMessage(true, locMessage.substring(0, i));
                return false;
            }
            statusMessage(false, ""); //$NON-NLS-1$
        } else {
            statusMessage(false, UIText.CommitSearchPage_ContainingTextHint);
        }
        return true;
    }

    private void addScopeControls(Composite parent) {
        SelectionAdapter statusAdapter = new SelectionAdapter() {

            public void widgetSelected(SelectionEvent e) {
                updateOKStatus();
            }

        };

        Group scopeArea = new Group(parent, SWT.NONE);
        scopeArea.setText(UIText.CommitSearchPage_Scope);
        GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(scopeArea);
        GridLayoutFactory.swtDefaults().numColumns(3).applyTo(scopeArea);

        this.searchMessageButton = new Button(scopeArea, SWT.CHECK);
        this.searchMessageButton.setText(UIText.CommitSearchPage_Message);
        this.searchMessageButton.setSelection(true);
        this.searchMessageButton.addSelectionListener(statusAdapter);

        this.searchAuthorButton = new Button(scopeArea, SWT.CHECK);
        this.searchAuthorButton.setText(UIText.CommitSearchPage_Author);
        this.searchAuthorButton.setSelection(true);
        this.searchAuthorButton.addSelectionListener(statusAdapter);

        this.searchCommitterButton = new Button(scopeArea, SWT.CHECK);
        this.searchCommitterButton.setText(UIText.CommitSearchPage_Committer);
        this.searchCommitterButton.setSelection(true);
        this.searchCommitterButton.addSelectionListener(statusAdapter);

        this.searchCommitButton = new Button(scopeArea, SWT.CHECK);
        this.searchCommitButton.setText(UIText.CommitSearchPage_CommitId);
        this.searchCommitButton.setSelection(true);
        this.searchCommitButton.addSelectionListener(statusAdapter);

        this.searchTreeButton = new Button(scopeArea, SWT.CHECK);
        this.searchTreeButton.setText(UIText.CommitSearchPage_TreeId);
        this.searchTreeButton.setSelection(true);
        this.searchTreeButton.addSelectionListener(statusAdapter);

        this.searchParentsButton = new Button(scopeArea, SWT.CHECK);
        this.searchParentsButton.setText(UIText.CommitSearchPage_ParentIds);
        this.searchParentsButton.setSelection(true);
        this.searchParentsButton.addSelectionListener(statusAdapter);
    }

    private void addRepositoryControl(Composite parent) {
        repositoryGroup = new Group(parent, SWT.NONE);
        repositoryGroup.setBackgroundMode(SWT.INHERIT_DEFAULT);
        GridDataFactory.fillDefaults().grab(true, true).span(2, 1).applyTo(repositoryGroup);
        GridLayoutFactory.swtDefaults().numColumns(2).applyTo(repositoryGroup);

        this.repositoryViewer = CheckboxTableViewer.newCheckList(repositoryGroup,
                SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
        this.repositoryViewer
                .setLabelProvider(new DelegatingStyledCellLabelProvider(new RepositoriesViewLabelProvider()));
        this.repositoryViewer.setContentProvider(new RepositoriesViewContentProvider());
        this.repositoryViewer.setInput(ResourcesPlugin.getWorkspace().getRoot());
        this.repositoryViewer.addCheckStateListener(new ICheckStateListener() {

            public void checkStateChanged(CheckStateChangedEvent event) {
                updateOKStatus();
                repositoryGroup.setText(getRepositoryText());
            }
        });
        GridDataFactory.fillDefaults().grab(true, true).hint(SWT.DEFAULT, 40)
                .applyTo(this.repositoryViewer.getControl());

        ToolBar checkBar = new ToolBar(repositoryGroup, SWT.FLAT | SWT.VERTICAL);
        GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.TOP).grab(false, true).applyTo(checkBar);
        ToolItem checkItem = new ToolItem(checkBar, SWT.PUSH);
        checkItem.setToolTipText(UIText.CommitSearchPage_CheckAll);
        Image checkImage = UIIcons.CHECK_ALL.createImage();
        UIUtils.hookDisposal(checkItem, checkImage);
        checkItem.setImage(checkImage);
        checkItem.addSelectionListener(new SelectionAdapter() {

            public void widgetSelected(SelectionEvent e) {
                repositoryViewer.setAllChecked(true);
                repositoryGroup.setText(getRepositoryText());
                updateOKStatus();
            }

        });
        ToolItem uncheckItem = new ToolItem(checkBar, SWT.PUSH);
        uncheckItem.setToolTipText(UIText.CommitSearchPage_UncheckAll);
        Image uncheckImage = UIIcons.UNCHECK_ALL.createImage();
        UIUtils.hookDisposal(uncheckItem, uncheckImage);
        uncheckItem.setImage(uncheckImage);
        uncheckItem.addSelectionListener(new SelectionAdapter() {

            public void widgetSelected(SelectionEvent e) {
                repositoryViewer.setAllChecked(false);
                repositoryGroup.setText(getRepositoryText());
                updateOKStatus();
            }

        });

        this.searchAllBranchesButton = new Button(repositoryGroup, SWT.CHECK);
        this.searchAllBranchesButton.setText(UIText.CommitSearchPage_SearchAllBranches);
        GridDataFactory.swtDefaults().grab(true, false).span(2, 1).applyTo(this.searchAllBranchesButton);

        repositoryGroup.setText(getRepositoryText());
    }

    private String getRepositoryText() {
        return MessageFormat.format(UIText.CommitSearchPage_Repositories,
                Integer.valueOf(repositoryViewer.getCheckedElements().length),
                Integer.valueOf(repositoryViewer.getTable().getItemCount()));
    }

    private void addTextPatternControls(Composite group) {
        // Info text
        Label label = new Label(group, SWT.NONE);
        label.setText(UIText.CommitSearchPage_ContainingText);
        label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1));
        label.setFont(group.getFont());

        // Pattern combo
        patternCombo = new Combo(group, SWT.SINGLE | SWT.BORDER);
        patternCombo.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                handleWidgetSelected();
                updateOKStatus();
            }
        });
        // add some listeners for regex syntax checking
        patternCombo.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                updateOKStatus();
            }
        });
        patternCombo.setFont(group.getFont());
        GridData data = new GridData(GridData.FILL, GridData.FILL, true, false, 1, 1);
        data.widthHint = convertWidthInCharsToPixels(50);
        patternCombo.setLayoutData(data);

        ComboContentAdapter contentAdapter = new ComboContentAdapter();
        FindReplaceDocumentAdapterContentProposalProvider findProposer = new FindReplaceDocumentAdapterContentProposalProvider(
                true);
        patternFieldContentAssist = new ContentAssistCommandAdapter(patternCombo, contentAdapter, findProposer,
                ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS, new char[0], true);
        patternFieldContentAssist.setEnabled(false);

        isCaseSensitiveButton = new Button(group, SWT.CHECK);
        isCaseSensitiveButton.setText(UIText.CommitSearchPage_CaseSensitive);
        isCaseSensitiveButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
        isCaseSensitiveButton.setFont(group.getFont());

        // Text line which explains the special characters
        statusLabel = new CLabel(group, SWT.LEAD);
        statusLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
        statusLabel.setFont(group.getFont());
        statusLabel.setAlignment(SWT.LEFT);
        statusLabel.setText(UIText.CommitSearchPage_ContainingTextHint);

        // RegEx checkbox
        isRegExButton = new Button(group, SWT.CHECK);
        isRegExButton.setText(UIText.CommitSearchPage_RegularExpression);
        isRegExButton.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                updateOKStatus();

                patternFieldContentAssist.setEnabled(isRegExButton.getSelection());
            }
        });
        isRegExButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
        isRegExButton.setFont(group.getFont());
    }

    private void handleWidgetSelected() {
        int selectionIndex = patternCombo.getSelectionIndex();
        if (selectionIndex < 0 || selectionIndex >= fPreviousSearchPatterns.size()) {
            repositoryViewer.setAllChecked(true);
            repositoryGroup.setText(getRepositoryText());
            return;
        }

        CommitSearchSettings settings = fPreviousSearchPatterns.get(selectionIndex);
        if (!patternCombo.getText().equals(settings.getTextPattern()))
            return;

        isCaseSensitiveButton.setSelection(settings.isCaseSensitive());
        isRegExButton.setSelection(settings.isRegExSearch());
        patternCombo.setText(settings.getTextPattern());
        patternFieldContentAssist.setEnabled(settings.isRegExSearch());

        searchAuthorButton.setSelection(settings.isMatchAuthor());
        searchCommitButton.setSelection(settings.isMatchCommit());
        searchCommitterButton.setSelection(settings.isMatchCommitter());
        searchMessageButton.setSelection(settings.isMatchMessage());
        searchParentsButton.setSelection(settings.isMatchParents());
        searchTreeButton.setSelection(settings.isMatchTree());
        searchAllBranchesButton.setSelection(settings.isAllBranches());

        List<RepositoryNode> repositories = new LinkedList<RepositoryNode>();
        for (String path : settings.getRepositories()) {
            File file = new File(path);
            if (file.exists())
                try {
                    RepositoryNode node = new RepositoryNode(null, org.eclipse.egit.core.Activator.getDefault()
                            .getRepositoryCache().lookupRepository(file));
                    repositories.add(node);
                } catch (IOException ignore) {
                    // Ignore and don't check
                }
        }
        repositoryViewer.setCheckedElements(repositories.toArray());
        repositoryGroup.setText(getRepositoryText());
    }

    private boolean initializePatternControl() {
        ISelection selection = getSelection();
        if (selection instanceof ITextSelection && !selection.isEmpty()) {
            String text = ((ITextSelection) selection).getText();
            if (text != null) {
                if (isRegExButton.getSelection())
                    patternCombo.setText(FindReplaceDocumentAdapter.escapeForRegExPattern(text));
                else
                    patternCombo.setText(insertEscapeChars(text));

                return true;
            }
        }
        return false;
    }

    private String insertEscapeChars(String text) {
        if (text == null || text.length() == 0)
            return ""; //$NON-NLS-1$
        StringBuilder sbIn = new StringBuilder(text);
        BufferedReader reader = new BufferedReader(new StringReader(text));
        int lengthOfFirstLine = 0;
        try {
            String l = reader.readLine();
            if (l != null)
                lengthOfFirstLine = l.length();
        } catch (IOException ex) {
            return ""; //$NON-NLS-1$
        }
        StringBuilder sbOut = new StringBuilder(lengthOfFirstLine + 5);
        int i = 0;
        while (i < lengthOfFirstLine) {
            char ch = sbIn.charAt(i);
            if (ch == '*' || ch == '?' || ch == '\\')
                sbOut.append('\\');
            sbOut.append(ch);
            i++;
        }
        return sbOut.toString();
    }

    /**
     * Sets the search page's container.
     *
     * @param container
     *            the container to set
     */
    public void setContainer(ISearchPageContainer container) {
        this.container = container;
    }

    private ISearchPageContainer getContainer() {
        return this.container;
    }

    private ISelection getSelection() {
        return this.container.getSelection();
    }

    public void dispose() {
        writeConfiguration();
        super.dispose();
    }

    /**
     * Returns the page settings for this Text search page.
     *
     * @return the page settings to be used
     */
    private IDialogSettings getDialogSettings() {
        IDialogSettings dialogSettings = Activator.getDefault().getDialogSettings();
        IDialogSettings section = dialogSettings.getSection(PAGE_NAME);
        if (section == null)
            section = dialogSettings.addNewSection(PAGE_NAME);
        return section;
    }

    /**
     * Initializes itself from the stored page settings.
     */
    private void readConfiguration() {
        IDialogSettings settings = getDialogSettings();
        try {
            int historySize = settings.getInt(STORE_HISTORY_SIZE);
            for (int i = 0; i < historySize; i++) {
                IDialogSettings histSettings = settings.getSection(STORE_HISTORY + i);
                if (histSettings != null)
                    fPreviousSearchPatterns.add(CommitSearchSettings.create(histSettings));
            }
        } catch (NumberFormatException ignored) {
            // Ignored
        }
    }

    /**
     * Stores it current configuration in the dialog store.
     */
    private void writeConfiguration() {
        IDialogSettings s = getDialogSettings();
        int historySize = Math.min(fPreviousSearchPatterns.size(), HISTORY_SIZE);
        s.put(STORE_HISTORY_SIZE, historySize);
        for (int i = 0; i < historySize; i++)
            fPreviousSearchPatterns.get(i).store(s.addNewSection(STORE_HISTORY + i));
    }

    private void statusMessage(boolean error, String message) {
        statusLabel.setText(message);
        if (error)
            statusLabel.setForeground(JFaceColors.getErrorText(statusLabel.getDisplay()));
        else
            statusLabel.setForeground(null);
    }

}