In an Eclipse plugin, how can I make a DirectoryFieldEditor start with a particular path? - eclipse

I am making an Eclipse plugin which on right clicking a project produces a UI.
In this UI I have used DirectoryFieldEditor. This produces directory dialog starting at "MyComputer" as root. What i want is it to show paths starting at the project which i right clicked. how can this be achieved?
I am trying to mimic when you right click a project and say "new package" - the source folder browse give a directory dialog with only those folders which are open projects.... I want a similar directory dialog.
Can somebody help and give me some code snippets or suggestions?

Well, considering the "new package" is actually the class:
org.eclipse.jdt.internal.ui.wizards.NewPackageCreationWizard
which uses NewPackageWizardPage (source code), you will see:
public void init(IStructuredSelection selection) {
IJavaElement jelem = getInitialJavaElement(selection);
initContainerPage(jelem);
String pName = ""; //$NON-NLS-1$
if (jelem != null) {
IPackageFragment pf = (IPackageFragment) jelem
.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
if (pf != null && !pf.isDefaultPackage())
pName = pf.getElementName();
}
setPackageText(pName, true);
updateStatus(new IStatus[] { fContainerStatus, fPackageStatus });
}
With the getInitialJavaElement() being part of superclass NewContainerWizardPage:
/**
* Utility method to inspect a selection to find a Java element.
*
* #param selection the selection to be inspected
* #return a Java element to be used as the initial selection, or <code>null</code>,
* if no Java element exists in the given selection
*/
protected IJavaElement getInitialJavaElement(
IStructuredSelection selection) {
IJavaElement jelem = null;
if (selection != null && !selection.isEmpty()) {
Object selectedElement = selection.getFirstElement();
if (selectedElement instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) selectedElement;
jelem = (IJavaElement) adaptable
.getAdapter(IJavaElement.class);
if (jelem == null) {
IResource resource = (IResource) adaptable
.getAdapter(IResource.class);
if (resource != null
&& resource.getType() != IResource.ROOT) {
while (jelem == null
&& resource.getType() != IResource.PROJECT) {
resource = resource.getParent();
jelem = (IJavaElement) resource
.getAdapter(IJavaElement.class);
}
if (jelem == null) {
jelem = JavaCore.create(resource); // java project
}
}
}
}
}
if (jelem == null) {
IWorkbenchPart part = JavaPlugin.getActivePage()
.getActivePart();
if (part instanceof ContentOutline) {
part = JavaPlugin.getActivePage().getActiveEditor();
}
if (part instanceof IViewPartInputProvider) {
Object elem = ((IViewPartInputProvider) part)
.getViewPartInput();
if (elem instanceof IJavaElement) {
jelem = (IJavaElement) elem;
}
}
}
if (jelem == null
|| jelem.getElementType() == IJavaElement.JAVA_MODEL) {
try {
IJavaProject[] projects = JavaCore.create(
getWorkspaceRoot()).getJavaProjects();
if (projects.length == 1) {
jelem = projects[0];
}
} catch (JavaModelException e) {
JavaPlugin.log(e);
}
}
return jelem;
}
Between those two methods, you should be able to initialize your custom UI with the exact information (i.e., "relative source path") you want.
If you look at the source of DirectoryFieldEditor, you will see it open its directory chooser dialog based on the value if its main Text field defined in StringFieldEditor:doLoad():
String JavaDoc value = getPreferenceStore().getString(getPreferenceName());
textField.setText(value);
That means you need, in your custom UI, to get the preference store and associate the right path with an id. You will use that id for your DirectoryFieldEditor initialization. Y oucan see an example here.
public static final String MY_PATH = "my.init.path";
IPreferenceStore store = myPlugin.getDefault().getPreferenceStore();
store.setValue(MY_PATH, theRightPath);
myDirFieldEditor = new DirectoryFieldEditor(MY_PATH, "&My path", getFieldEditorParent());
As you mention in the comments, all this will only initialize the eclipse-part GUI, not the native windows explorer launched by a DirectoryDialog:
this (the native interface) is based on:
parameters stored in BROWSEINFO Structure
used by the actual GUI SHBrowseForFolder Function, which actually displays a dialog box that enables the user to select a Shell folder.
That GUI initialize a root path based on filter path, so you need to also initialize (on eclipse side) that filter field with a path in order to get it pick up by the Windows-GUI SHBrowseForFolder.
According to DirectoryFieldEditor, that is exactly what getTextControl() (the field you initialized above) is for.
But the problem comes from the fact that field has been initialized with a relative path. Since that path is unknown by the underlying OS, it defaults back to root OS path.
You need to find a way to store the full system path of the project, not the relative path.
That way, that full path will be recognized by the Os and used as initial path.
For instance, from a IJavaElement, you can get its associated resource, and try to get the (full system) path from there.
Actually, from the IPackageFragment you should be able to call getPath(), and check if the IPath returned contains the full system path.

Related

How to get full history of IFile Eclipse Team IFileHistoryProvider when file has been moved within multimodule project?

I am trying to access the full repository history of an IFile object programatically from an Eclipse plugin. When the file has never been renamed or moved, the following code snippet works:
public long getFileHistoryTimestamp(IFile file, IProgressMonitor monitor, boolean lastModified) {
IProject project = file.getProject();
RepositoryProvider rProv = RepositoryProvider.getProvider(project);
long createdMillis, lastModifiedMillis;
if (rProv !=null) {
IFileHistoryProvider fhp = rProv.getFileHistoryProvider();
if (fhp != null) {
IFileHistory fHist = fhp.getFileHistoryFor((IResource) file,
IFileHistoryProvider.NONE, null);
IFileRevision[] revs = fHist.getFileRevisions();
Arrays.sort(revs, new Comparator<IFileRevision>(){
public int compare(IFileRevision o1, IFileRevision o2) {
return Long.compare(o1.getTimestamp(), o2.getTimestamp());
}
});
for (IFileRevision rev : revs) {
if (rev.getTimestamp() >= 0) {
createdMillis = rev.getTimestamp();
break;
}
}
if (revs.length > 0) {
lastModifiedMillis = revs[revs.length - 1].getTimestamp();
}
}
return lastModified ? lastModifiedMillis : createdMillis;
}
But, if the file has been moved (e.g. with a multi-module maven project to a new or different module/project), then the values returned above are only for the file in it's current location - i.e. it does not track any rename/moves. The same file in the Eclipse Team History view does track the full version history.
How can I access this full history within the Eclipse Team API?

Wagtail - how to get tags to work with `telepath` (tags in streamfield)?

I can use tags in regular page fields without any issue. When using tags within blocks (within a streamfield), the UI works and the tags are saved BUT the current page tags do not show up when loading the page in the admin. That's because the current value is not in the template anymore, it's in a JSON loaded via telepath.
I can confirm that the tags are saved and present in the data passed to initBlockWidget in the page source but these are ignored. Also, if I used a regular text field instead of the tag-widget, I can see the saved-values in the admin.
This is the code I have (which used to be enough before the refactor with telepath).
from wagtail.admin.widgets import AdminTagWidget
class TagBlock(TextBlock):
#cached_property
def field(self):
field_kwargs = {"widget": AdminTagWidget()}
field_kwargs.update(self.field_options)
return forms.CharField(**field_kwargs)
I think the following link is what I need to complete somehow to get it to work: https://docs.wagtail.io/en/stable/reference/streamfield/widget_api.html#form-widget-client-side-api
I've tried with this:
class AdminTagWidgetAdapter(WidgetAdapter):
class Media:
js = [
"wagtailadmin/js/vendor/tag-it.js",
"js/admin/admin-tag-widget-adapter.js",
]
register(AdminTagWidgetAdapter(), AdminTagWidget)
And under js/admin/admin-tag-widget-adapter.js:
console.log("adapter"); // this shows up in the console
class BoundWidget { // copied from wagtail source code
constructor(element, name, idForLabel, initialState) {
var selector = ':input[name="' + name + '"]';
this.input = element.find(selector).addBack(selector); // find, including element itself
this.idForLabel = idForLabel;
this.setState(initialState);
}
getValue() {
return this.input.val();
}
getState() {
return this.input.val();
}
setState(state) {
this.input.val(state);
}
getTextLabel(opts) {
const val = this.getValue();
if (typeof val !== 'string') return null;
const maxLength = opts && opts.maxLength;
if (maxLength && val.length > maxLength) {
return val.substring(0, maxLength - 1) + '…';
}
return val;
}
focus() {
this.input.focus();
}
}
// my code here:
class AdminTagWidget {
constructor(html, idPattern) {
this.html = html;
this.idPattern = idPattern;
}
boundWidgetClass = BoundWidget;
render(placeholder, name, id, initialState) {
console.log("RENDER", placeholder, name, id, initialState); // this does not show
var html = this.html.replace(/__NAME__/g, name).replace(/__ID__/g, id);
var idForLabel = this.idPattern.replace(/__ID__/g, id);
var dom = $(html);
$(placeholder).replaceWith(dom);
// eslint-disable-next-line new-cap
return new this.boundWidgetClass(dom, name, idForLabel, initialState);
}
}
console.log("here") // does show in the console
// variants I've tried:
//window.telepath.register('wagtail.admin.widgets.tags.AdminTagWidget', AdminTagWidget);
//window.telepath.register('wagtail.widgets.AdminTagWidget', AdminTagWidget);
window.telepath.register('path.where.its.used.AdminTagWidget', AdminTagWidget)
The log from my custom render method does not show. It seems that I'm not calling the right path within window.telepath.register but I don't know how what the string is supposed to be...
I'm not even sure if this is the right way forward.
Notes:
it works in regular field, the question is about tags in blocks
I'm using Wagtail version 2.13.2 but I've also tried with 2.15 without any difference.
In the console, I can log window.telepath and see my custom widget. It's just not "applied" to anything
Your WidgetAdapter class needs a js_constructor attribute:
class AdminTagWidgetAdapter(WidgetAdapter):
js_constructor = 'myapp.widgets.AdminTagWidget'
class Media:
js = [
"wagtailadmin/js/vendor/tag-it.js",
"js/admin/admin-tag-widget-adapter.js",
]
Any string value will work here - it just needs to uniquely identify the class, so it's recommended to use a dotted module-like path to avoid colliding with others. This then matches the string you pass to window.telepath.register on the Javascript side:
window.telepath.register('myapp.widgets.AdminTagWidget', AdminTagWidget)

How do I get the selected file's filesystem path using ResourceNavigator in RCP?

This is what I tried, but it failed.And it alerted that 'org.eclipse .core .internal.File cannot be cast to org.eclipse.core.resource.IProject'.
public void runDefaultAction(IStructuredSelection selection) {
Object element = selection.getFirstElement();
if (element instanceof IFile) {
openFileAction.selectionChanged(selection);
openFileAction.run();
String selectedPathString = ((IFile) element).getFullPath().toString();
System.out.println(selectedPathString);
//get project absolute path
ISelectionService service = navigator.getSite().getWorkbenchWindow()
.getSelectionService();
IStructuredSelection selection1 = (IStructuredSelection) service
.getSelection("BIT_DEC.myNavigator");
IProject iproject = (IProject) selection1.getFirstElement();
String real_file_path = iproject.getLocation().toString();
System.out.println(real_file_path);
.....
it alerted that 'org.eclipse .core .internal.File cannot be cast to org.eclipse.core.resource.IProject'.
Because:
Corrected last 3 lines of your code.
if(selection1.getFirstElement() instanceof IFile) {
IFile file = (IFile) selection1.getFirstElement();
String real_file_path = file.getLocation().toString();
System.out.println(real_file_path);
}
IFile has a getLocation() method that gives you the full path directly.
Note: The selection returned by the selection service will generally be the same as the selection you are given. You can get the project from an IFile by calling its getProject() method.

How to programmatically find a .java file in an Eclipse plugin from full classname?

Inside an Eclipse plugin, I'd like to open a file in editor.
I know the full package and class name
How can I determine the path of the .java file from this?
Take a look at IJavaProject.findType( name ) method. Once you have an IType, you can use getPath or getResource methods to locate the file. This method searches across a project and everything visible from that project.
To search the whole workspace, iterate through all the Java projects in the workspace, calling the findType method on each in turn.
You also need to know the source folder.
IProject prj = ResourcePlugin.getWorkspace().getRoot().getProject("project-name");
IFile theFile = prj.getFile(sourceFolder + packageName.replace('.','/') + className + ".java");
Generally you specify the file for an editor with an IFile. You can also ask an IFile for variants of the file's path.
I know this is a bit old but I had the same need and I had a look at how eclipse does it for stack trace elements (they have a hyperlink on them). The code is in org.eclipse.jdt.internal.debug.ui.console.JavaStackTraceHyperlink (the link is "lazy" so the editor to open is resolved only when you click on it).
What it does is it first searches for the type in the context of the launched application, then for in the whole workspace (method startSourceSearch) :
IType result = OpenTypeAction.findTypeInWorkspace(typeName, false);
And then opens the associated editor (method processSearchResult, source is the type retrieved above) :
protected void processSearchResult(Object source, String typeName, int lineNumber) {
IDebugModelPresentation presentation = JDIDebugUIPlugin.getDefault().getModelPresentation();
IEditorInput editorInput = presentation.getEditorInput(source);
if (editorInput != null) {
String editorId = presentation.getEditorId(editorInput, source);
if (editorId != null) {
try {
IEditorPart editorPart = JDIDebugUIPlugin.getActivePage().openEditor(editorInput, editorId);
if (editorPart instanceof ITextEditor && lineNumber >= 0) {
ITextEditor textEditor = (ITextEditor)editorPart;
IDocumentProvider provider = textEditor.getDocumentProvider();
provider.connect(editorInput);
IDocument document = provider.getDocument(editorInput);
try {
IRegion line = document.getLineInformation(lineNumber);
textEditor.selectAndReveal(line.getOffset(), line.getLength());
} catch (BadLocationException e) {
MessageDialog.openInformation(JDIDebugUIPlugin.getActiveWorkbenchShell(), ConsoleMessages.JavaStackTraceHyperlink_0, NLS.bind("{0}{1}{2}", new String[] {(lineNumber+1)+"", ConsoleMessages.JavaStackTraceHyperlink_1, typeName})); //$NON-NLS-2$ //$NON-NLS-1$
}
provider.disconnect(editorInput);
}
} catch (CoreException e) {
JDIDebugUIPlugin.statusDialog(e.getStatus());
}
}
}
}
Code has copyright from eclipse. Hopfully I'm allowed to reproduced it if this is mentionned.

Eclipe PDE: Jump to line X and highlight it

A qustion about Eclipse PDE development: I write a small plugin for Eclipse and have the following
* an org.eclipse.ui.texteditor.ITextEditor
* a line number
How can I automatically jump to that line and mark it? It's a pity that the API seems only to support offsets (see: ITextEditor.selectAndReveal()) within the document but no line numbers.
The best would be - although this doesn't work:
ITextEditor editor = (ITextEditor)IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file, true );
editor.goto(line);
editor.markLine(line);
It this possible in some way? I did not find a solution
on the class DetailsView I found the following method.
private static void goToLine(IEditorPart editorPart, int lineNumber) {
if (!(editorPart instanceof ITextEditor) || lineNumber <= 0) {
return;
}
ITextEditor editor = (ITextEditor) editorPart;
IDocument document = editor.getDocumentProvider().getDocument(
editor.getEditorInput());
if (document != null) {
IRegion lineInfo = null;
try {
// line count internaly starts with 0, and not with 1 like in
// GUI
lineInfo = document.getLineInformation(lineNumber - 1);
} catch (BadLocationException e) {
// ignored because line number may not really exist in document,
// we guess this...
}
if (lineInfo != null) {
editor.selectAndReveal(lineInfo.getOffset(), lineInfo.getLength());
}
}
}
Even though org.eclipse.ui.texteditor.ITextEditor deals wiith offset, it should be able to take your line number with the selectAndReveal() method.
See this thread and this thread.
Try something along the line of:
((ITextEditor)org.eclipse.jdt.ui.JavaUI.openInEditor(compilationUnit)).selectAndReveal(int, int);