Eclipse Dev: Custom Search Page

This is for the archive and due to lack of useful tutorials on writing custom search pages in your own Eclipse plugin. It turned out that the default text search is good enough for my purposes, so I’ll stick it here as a future reference.

To start, your Eclipse plugin needs to add org.eclipse.search as a dependency, and, if you don’t have it already, org.eclipse.jface.text, too. With that, the Require-Bundle part of your MANIFEST.MF should look something like this:

Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
org.eclipse.ui.views,
org.eclipse.core.resources,
org.eclipse.ui.ide,
org.eclipse.ui.workbench.texteditor,
org.eclipse.text,
org.eclipse.ui.editors,
org.eclipse.search,
org.eclipse.jface.text

Next, we’ll specify the extension for our custom search page, using the plugin.xml editor or just pasting and modifying this snippet:

<extension point="org.eclipse.search.searchPages">
  <page
    class="com.acm.your.product.CustomSearchPage"
    id="yourplugin.page1"
    label="Your Custom Search, rename this">
  </page>
</extension>

And the actual implementation, providing a textfield as input, initialized with whatever text selection is currently active (thats where the org.eclipse.jface.text dependency comes from), and doing a text search across the full workspace:

package com.acm.your.product;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.search.ui.ISearchPage;
import org.eclipse.search.ui.ISearchPageContainer;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.search.ui.text.TextSearchQueryProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

public class CustomSearchPage extends DialogPage implements ISearchPage {

	private String selected;
	private Text idText;
	
	public IdSearchPage() {
		super();
	}
	
	public IdSearchPage(String title) {
		super(title);
	}

	public boolean performAction() {
		if (idText.getText().length() == 0)
			return false;
		try {
			NewSearchUI.runQueryInBackground(TextSearchQueryProvider.getPreferred().createQuery(idText.getText()));
		} catch (IllegalArgumentException e) {
			throw new RuntimeException(e);
		} catch (CoreException e) {
			throw new RuntimeException(e);
		}
        return true;
	}

	public void setContainer(ISearchPageContainer container) {
		if (container.getSelection() instanceof TextSelection) {
			selected = ((TextSelection) container.getSelection()).getText();
		}
	}

	public void createControl(Composite parent) {
		GridLayout layout = new GridLayout(1, false);
		layout.horizontalSpacing = 5;
		layout.verticalSpacing = 5;
		parent.setLayout(layout);
		new Label(parent, 0).setText("Containing text:");
		idText = new Text(parent, SWT.BORDER);
		idText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		if (selected != null) {
			idText.setText(selected);
			idText.setSelection(0, selected.length());
		}
		setControl(parent);
	}
	
	@Override
	public void setVisible(boolean visible) {
		super.setVisible(visible);
		idText.setFocus();
	}
	
}

You can also restrict the files to search by creating a list first, and pass that to the search provider:

package com.acm.your.product;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.search.ui.ISearchPage;
import org.eclipse.search.ui.ISearchPageContainer;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.search.ui.text.TextSearchQueryProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

public class CustomSearchPage2 extends DialogPage implements ISearchPage {

	private String selected;
	private Text idText;
	
	public IdSearchPage() {
		super();
	}
	
	public IdSearchPage(String title) {
		super(title);
	}

	public boolean performAction() {
		if (idText.getText().length() == 0)
			return false;
		try {
			final List files = new ArrayList();
			ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceProxyVisitor() {
				public boolean visit(IResourceProxy proxy) throws CoreException {
					if (proxy.getType() == IResource.FILE) {
						IFile file = (IFile) proxy.requestResource();
			            if (file.getLocation().getFileExtension() != null && file.getLocation().getFileExtension().matches("xml|html|js|css")) {
			            	files.add(file);
			            }
			         }
			         return true;
				}
			}, IResource.DEPTH_INFINITE);
			NewSearchUI.runQueryInBackground(TextSearchQueryProvider.getPreferred().createQuery(idText.getText(), files.toArray(new IResource[0])));
		} catch (IllegalArgumentException e) {
			throw new RuntimeException(e);
		} catch (CoreException e) {
			throw new RuntimeException(e);
		}
        return true;
	}

	public void setContainer(ISearchPageContainer container) {
		if (container.getSelection() instanceof TextSelection) {
			selected = ((TextSelection) container.getSelection()).getText();
		}
	}

	public void createControl(Composite parent) {
		GridLayout layout = new GridLayout(1, false);
		layout.horizontalSpacing = 5;
		layout.verticalSpacing = 5;
		parent.setLayout(layout);
		new Label(parent, 0).setText("Containing text:");
		idText = new Text(parent, SWT.BORDER);
		idText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		if (selected != null) {
			idText.setText(selected);
			idText.setSelection(0, selected.length());
		}
		setControl(parent);
	}
	
	@Override
	public void setVisible(boolean visible) {
		super.setVisible(visible);
		idText.setFocus();
	}
	
}

This in itself doesn’t yet provide a lot of value, but if you actually need to build a custom search, its a good start.

Roughly based on this sort-of tutorial on the Eclipse wiki.

Doener Grenade

Crawling some old backups, I found this:

doener

Can you guess what it is? Well, along other files, there’s also a readme:

Author:    [BIA]_Enchos (TNT)
Email:  Enchos@gmx.net

HE DOENER
———————-
Built to replace the HE grenade in Counter-Strike Halflife.
———————-

This pack Include:

New Hud Sprites
All new models, v_, p_, and w_
Completly new skins
———————-
Installation: Just extract the files to your cstrike-folder.
Be sure that the given pathes are used!
(MDL-files in the models-folder and SPR-files in sprites-folder)
———————-
This is a replacement for the HE Grenade, but you can also you
it as a flashbang. Make a copy of the 3 mdl´s and rename them to:
p_flashbang.mdl
v_flashbang.mdl
w_flashbang.mdl

If you don´t want to use the new explosion-sprites, just delete
eexplo.spr, fexplo1.spr and fexplo.spr! :-(
———————-
Note:
The “DOENER” is a kind of burger, but in combination with
“TOMATO KETCHUP” it makes “BOOM”. Hurt your enemies
around the corner and they will hate you :-). Shoot through the
corner and they will kill you .-|


Still to do:
I also wanted to make new sounds, but this is not as easy as I thought.
If anybody knows how to do it, please mail me (Enchos@gmx.net).
<->
Have a LOT of fun!!!
<->
Suggestions, ideas, anything at all is welcome. Just e-mail me.

———————————————————————-

Be careful with hostages, they are allergic against doener´s.
What? Your enemies too?!?

The highlighted section “explains” the purpose of the tomato ketchup… so yeah, what was I thinking when creating that? I need to figure out how to show you the custom animation; the ketchup was actually poured over the undefined thing in the left hand! Also look at the awesome custom hand-texture…

Looking at the custom grenade models of a random Counterstrike site – this one is still way cooler then those!

Getting started with Git and GitHub and importing from Subversion

If you’re working an open-source projects, these days you can barely get around Git and GitHub. Actually getting started, especially on Windows, isn’t trivial. In my case I had some projects I need to transfer from a Google Code subversion repository to a new repository at GitHub. Actually getting started with GitHub needs to wait for another post, but here are some commands for importing from Subversion:

git svn clone http://[yourproject].googlecode.com/svn/trunk/path/to/code/
git remote add origin git@github.com:[youraccount]/[yourrepository].git
git push origin master

This will create a new folder, based on the svn url you provide (the last part will provide the folder name) and get the files from that path, including the history(!). The second command tells the local Git repository where the next command should go to. You have to replace that URL with “Your clone URL” from your GitHub project.
The last command actually pushes the code to the repository. For that to work, the GitHub repository should be brand-new and empty. If you tried GitHub’s SVN import and got nothing, the repository may be filled with something that GitHub doesn’t show. In that case, create a new repository and import into that. You can remove/delete later.

The following code should do mostly the same, but instead of creating a folder, will initialize an empty repository in the current folder, then do the checkout from svn into that folder (need to test that again):

git init
git svn clone http://[yourproject].googlecode.com/svn/trunk/path/to/code/ .
git remote add origin git@github.com:[youraccount]/[yourrepository].git
git push origin master

Note the dot after the svn clone command.

Thanks to Kelvin Luck for the ideas and commands!

Demon Hunter

A few weeks ago I mentioned a current music favorite, The Dillinger Escape Plan. Today I’ve got another one I’d like to share: Demon Hunter.

While a bit more straight-foward in terms of instruments, vocals are again a great mix between melodic-catchy and slightly over-the-top. See for yourself, from the Storm The Gates Of Hell album:


4. Fading Away

Probably not an official video, but a great song anyway:

8. Incision

Update: Trying a different plugin to get the video into the RSS feed