How to get similar behaviour as the Java browsing perspective with two commonNavigator views? - eclipse-plugin

I'd like to reproduce the behavior of the Java browsing perspective as so : the user select a file (selectedFile) from a Custom Common Navigator View (VariabilityNavigator) and it displays only the related files to that selection in another Custom Common Navigator View (ConfigFileNavigator). It refreshes automatically as the selection goes.
I use Common navigator because I want to display the package of the related files and not the other ones or the empty ones. That I coded fine.
Could you please walk me thru the steps to do that ? Here is how far I got..
I have successfully coded the first view VariabilityNavigator.
I thought I would use a filter ConfigurationFilesFilter to get the selectedFile and use it to filter the related files.
I can get the selection from the VariabilityNavigator within the select(...) of ConfigurationFilesFilter code below
I then use it to filter the files I want to display using isRelatedToSelection. I haven't write that part yet, but it's not a problem for testing.
This method don't work for several reasons : it doesn't refresh and I have to click on the ConfigFileNavigator to get the result. What it should do : when I click on the first view and it should automatically display the result in the second view. Thanks for your help.
public class ConfigurationFilesFilter extends ViewerFilter {
String selectedFile;
public ConfigurationFilesFilter() {
}
#Override
public boolean select(Viewer viewer, Object parent, Object element) {
selectedFile = getVariabilityNavigatorSelection();
if (isChildElement(element)) {
return handleChild(element);
} else {
// manipulate tree viewer based on select decision for the sub elements
StructuredViewer sviewer = (StructuredViewer) viewer;
ITreeContentProvider provider = (ITreeContentProvider) sviewer.getContentProvider();
for (Object child : provider.getChildren(element)) {
if (select(viewer, element, child)) {
return true;
}
}
return false;
}
}
/**
*
* #param element
* #return true to be displayed
*/
private boolean handleChild(Object element) {
return true;
}
/**
* Check if an element is a java file and if it is related to the selection
*
* #param element
* #return boolean
*/
private boolean isChildElement(Object element) {
if (element instanceof IFile) {
if (((IFile) element).getFileExtension().equals("java")) {
return (isRelatedToSelection((IFile) element));
}
return false;
}
return false;
}
public String getVariabilityNavigatorSelection() {
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
IViewPart viewPart = page.findView(VariabilityNavigator.ID);
String VariabilityNavigatorSelection = "" ;
if (viewPart == null) {
VariabilityNavigatorSelection = "open Variability view. \r\nTo do so, open the menu as followed:\r\nWindow > Show view > Other... > SPL > Variability Navigator";
} else {
ISelectionProvider selProvider = viewPart.getSite().getSelectionProvider();
IStructuredSelection sel = (IStructuredSelection) selProvider.getSelection();
if (sel.isEmpty()) {
VariabilityNavigatorSelection = "selection vide";
}
Iterator<?> i = sel.iterator();
while (i.hasNext()) {
final Object o = i.next();
if (o instanceof IFile) {
VariabilityNavigatorSelection = ((IFile) o).getName();
} else
VariabilityNavigatorSelection = "Please select a file";
}
}
if (!VariabilityNavigatorSelection.isEmpty())
printTrace("selectPrint = " + VariabilityNavigatorSelection);
return VariabilityNavigatorSelection;
}

Related

How to set define different styles for the same paragraph

I'm trying to convert html text to generate a word table. It works pretty well, and the created word file is correct, except the character styles.
This is my first try with Apache POI.
So far, I was able to detect new line (<br>) tags from text paragraph (see code below). But I'd like to also check a few other tags such as <b>, <li>, <font> and set the right run values for each part.
For example :
This is my text <i> which now is in italic<b> but also in bold</b> depending on its importance</i>
I gess I should parse the text, and apply different runs for each part, but I don't know how to do.
private static XWPFParagraph getTableParagraph(XWPFTableCell cell, String text)
{
int fontsize= 11;
XWPFParagraph paragraph = cell.addParagraph();
cell.removeParagraph(0);
paragraph.setSpacingAfterLines(0);
paragraph.setSpacingAfter(0);
XWPFRun myRun1 = paragraph.createRun();
if (text==null) text="";
else
{
while (true)
{
int x = text.indexOf("<br>");
if (x <0) break;
String work = text.substring(0,x );
text= text.substring(x+4);
myRun1.setText(work);
myRun1.addBreak();
}
}
myRun1.setText(text);
myRun1.setFontSize(fontsize);
return paragraph;
}
While converting HTML text one never should go on the HTML using string methods only. XML as well as HTML are markup languages. Their content is markup and not only plain text. The markup needs to be traversed to get all the single nodes together with the meanings out of it. This traversing process never is trivial and so special libraries are there for. Deep inside those libraries also needs using string methods but those are wrapped into useful methods for traversing the markup.
For traversing HTML jsoup may be used for example. Especially NodeTraversor using NodeVisitor is useful for traversing HTML.
My example creates a ParagraphNodeVisitor which implements NodeVisitor. This interface requests method public void head(Node node, int depth) which is called every time the NodeTraversor is on head of a node and public void tail(Node node, int depth) which is called every time the NodeTraversor is on tail of a node. In those methods the process for handling the single nodes can be implemented. In our case main part of the process is whether we need a new XWPFRun and what settings this run needs.
Example:
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.jsoup.select.NodeVisitor;
import org.jsoup.select.NodeTraversor;
public class HTMLtoDOCX {
private XWPFDocument document;
public HTMLtoDOCX(String html, String docxPath) throws Exception {
this.document = new XWPFDocument();
XWPFParagraph paragraph = null;
Document htmlDocument = Jsoup.parse(html);
Elements htmlParagraphs = htmlDocument.select("p");
for(Element htmlParagraph : htmlParagraphs) {
System.out.println(htmlParagraph);
paragraph = document.createParagraph();
createParagraphFromHTML(paragraph, htmlParagraph);
}
FileOutputStream out = new FileOutputStream(docxPath);
document.write(out);
out.close();
document.close();
}
void createParagraphFromHTML(XWPFParagraph paragraph, Element htmlParagraph) {
ParagraphNodeVisitor nodeVisitor = new ParagraphNodeVisitor(paragraph);
NodeTraversor.traverse(nodeVisitor, htmlParagraph);
}
private class ParagraphNodeVisitor implements NodeVisitor {
String nodeName;
boolean needNewRun;
boolean isItalic;
boolean isBold;
boolean isUnderlined;
int fontSize;
String fontColor;
XWPFParagraph paragraph;
XWPFRun run;
ParagraphNodeVisitor(XWPFParagraph paragraph) {
this.paragraph = paragraph;
this.run = paragraph.createRun();
this.nodeName = "";
this.isItalic = false;
this.isBold = false;
this.isUnderlined = false;
this.fontSize = 11;
this.fontColor = "000000";
}
#Override
public void head(Node node, int depth) {
nodeName = node.nodeName();
System.out.println("Start "+nodeName+": " + node);
if ("#text".equals(nodeName)) {
run.setText(((TextNode)node).text​());
} else if ("i".equals(nodeName)) {
isItalic = true;
} else if ("b".equals(nodeName)) {
isBold = true;
} else if ("u".equals(nodeName)) {
isUnderlined = true;
} else if ("br".equals(nodeName)) {
run.addBreak();
} else if ("font".equals(nodeName)) {
fontColor = (!"".equals(node.attr("color")))?node.attr("color").substring(1):"000000";
fontSize = (!"".equals(node.attr("size")))?Integer.parseInt(node.attr("size")):11;
}
run.setItalic(isItalic);
run.setBold(isBold);
if (isUnderlined) run.setUnderline(UnderlinePatterns.SINGLE); else run.setUnderline(UnderlinePatterns.NONE);
run.setColor(fontColor); run.setFontSize(fontSize);
}
#Override
public void tail(Node node, int depth) {
nodeName = node.nodeName();
System.out.println("End "+nodeName);
if ("#text".equals(nodeName)) {
run = paragraph.createRun(); //after setting the text in the run a new run is needed
} else if ("i".equals(nodeName)) {
isItalic = false;
} else if ("b".equals(nodeName)) {
isBold = false;
} else if ("u".equals(nodeName)) {
isUnderlined = false;
} else if ("br".equals(nodeName)) {
run = paragraph.createRun(); //after setting a break a new run is needed
} else if ("font".equals(nodeName)) {
fontColor = "000000";
fontSize = 11;
}
run.setItalic(isItalic);
run.setBold(isBold);
if (isUnderlined) run.setUnderline(UnderlinePatterns.SINGLE); else run.setUnderline(UnderlinePatterns.NONE);
run.setColor(fontColor); run.setFontSize(fontSize);
}
}
public static void main(String[] args) throws Exception {
String html =
"<p>Text without tags. <b> Then bold <br/> having break.</b> Then without tags again.</p>"
+"<p><font size='32' color='#0000FF'><b>First paragraph.</font></b><br/>Just like a heading</p>"
+"<p>This is my text <i>which now is in italic <b>but also in bold</b> depending on its <u>importance</u></i>.<br/>Now a <b><i><u>new</u></i></b> line starts <i>within <b>the same</b> paragraph</i>.</p>"
+"<p><b>Last <u>paragraph <i>comes</u> here</b> finally</i>.</p>"
+"<p>But yet <u><i><b>another</i></u></b> paragraph having <i><font size='22' color='#FF0000'>special <u>font</u> settings</font></i>. Now default font again.</p>"
;
HTMLtoDOCX htmlToDOCX = new HTMLtoDOCX(html, "./CreateWordParagraphFromHTML.docx");
}
}
Result:
Disclaimer: This is a working draft showing the principle. Neither it is fully ready nor it is code ready for use in productive environments.

Is it somehow possible to have two master views and one detail view?

If I have for example one master view on the left and one in the middle, each showing oder Java Beans/POJOs, can I use a shared detail view that somehow listens to the active beans of each view and then displays the currently selected one in more detail? A one to one relation is quite easy to manage by using your Context library.
#ViewDocking(areaId ="left", position=1, displayName="Profiles", menuEntry = #WindowMenuEntry(path = "", position=0), accelerator="Shortcut+1")
public class ProfileListView extends BorderPane implements LocalContextProvider {
private final SimpleContextContent content = new SimpleContextContent();
private final SimpleContext context = new SimpleContext(content);
#FXML
private ListView<Profile> listview;
public ProfileListView() {
load();
// add some profiles
listview.getItems().add(new Profile("Profile1"));
listview.getItems().add(new Profile("Profile2"));
listview.getItems().add(new Profile("Profile3"));
// setup selection listener
listview.getSelectionModel().selectedItemProperty().addListener((value, oldProfile, newProfile) -> {
// set active profile and remove old one
content.remove(oldProfile);
content.add(newProfile);
});
// setup double click listener
configureClickListener();
}
private Profile getSelectedProfile() {
return listview.getSelectionModel().getSelectedItem();
}
private void configureClickListener() {
listview.setOnMouseClicked(event -> {
// check if it was a double click
if(event.getClickCount() == 2) {
System.out.println(getSelectedProfile());
// inject into editor pane
// calls the procedure to create a tab in the center area...
}
});
}
private void load() {
FXMLLoaders.loadRoot(this);
}
#Override
public Context getLocalContext() {
return context;
}
}
This is one master view holding a list view of items.
The other one would be the same, docking to the right as another tab and holding POJOs of type 'Action'.
The detail view is here:
#ViewDocking(areaId = "right", displayName = "Properties", accelerator = "Shortcut+2", menuEntry = #WindowMenuEntry(path = "", position = 0), position = 1)
public class ProfilePropertiesView extends BorderPane implements LocalContextProvider, ActiveContextSensitive {
private Context activeContext;
private SimpleContextContent content = new SimpleContextContent();
private SimpleContext context = new SimpleContext(content);
private Profile profile;
private IWindowService service = new NullWindowService();
#FXML
private PropertySheet propertysheet;
public ProfilePropertiesView() {
load();
// retrieve framework service, TODO: use tracker
BundleContext ctx = FrameworkUtil.getBundle(getClass()).getBundleContext();
service = ctx.getService(ctx.getServiceReference(IWindowService.class));
// initialize callback
service.addCallback(title -> {
System.out.println("callback called " + title);
// update the property sheet ui by re-creating the items list
// updateUI();
// we can safely return null
return null;
});
// configure editor factory so the user is able to use a combobox
propertysheet.setPropertyEditorFactory(new CustomPropertyEditorFactory(service));
}
private void load() {
FXMLLoaders.loadRoot(this);
}
#Override
public Context getLocalContext() {
return context;
}
private void contextChanged() {
// find profile information
Profile found = activeContext.find(Profile.class);
// if the found profile is null, ignore it
if (found != null) {
// reset if profile is valid
if (profile != null) {
reset();
}
// create reference and register
profile = found;
register();
}
}
private void register() {
// retrieve observablelist of bean properties if some profile is selected
if(profile != null) {
ObservableList<Item> items = createDetailedList(profile);
propertysheet.getItems().setAll(items);
}
}
private void updateUI() {
// clear property elements and re-create them
reset();
// re-create items
ObservableList<Item> items = createDetailedList(profile);
propertysheet.getItems().addAll(items);
}
private ObservableList<Item> createDetailedList(Object bean) {
ObservableList<Item> list = FXCollections.observableArrayList();
try {
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass(), Object.class);
Arrays.stream(beanInfo.getPropertyDescriptors()).map(pd -> new DetailedBeanProperty(bean, pd)).forEach(list::add);
} catch (IntrospectionException e) {
e.printStackTrace();
}
return list;
}
private void reset() {
propertysheet.getItems().clear();
}
#Override
public void setActiveContext(Context activeContext) {
this.activeContext = activeContext;
this.activeContext.addContextListener(Profile.class, event -> contextChanged());
// trigger change
contextChanged();
}
}
The current ProfilePropertiesView is just configured to display the properties of the selected profile. I want it to be able to display the current information of the last selected POJO in the UI. That means that if the user selected a Profile from the ListView, that profile should be displayed in the properties view. If he selected an Action from the Table (which is displayed in the center), the properties of the Action should be displayed.
Do I just need to register a new ContextListener for the Action.class
POJO and then call a method to populate the PropertiesView? I was
unsure if this is the right solution...
Yes, just add another ContextListener to the activeContext for every POJO type you want to observe.
Also note that in the constructor of views it's better to use a ServiceTracker instead of looking for the service via BundleContext as the service might not be available yet, depending on the order the bundles are loaded.
You can find a sample which uses a ServiceTracker here: https://stackoverflow.com/a/35974498/506855

what is "Dojo Name Text Box" onremove event?

I have a Dojo Name Text Box "xe:djextNameTextBox" on my form. By clicking [x] it removes a name from the list. How do I check what exact name was removed or clicked without parsing all values in getComponent("myNameBox").getValue()?
In my opinion the only way to handle this issue is to parse all values to get the information which name exactly was removed. I had to deal with the same problem in a previous project.
XPage
Set viewScope var "viewFiltersAsString":
<xp:this.beforeRenderResponse><![CDATA[#{javascript:log('beforerenderresponse (start rendering)');
requestScope.start = new Date().getTime();
viewScope.put('viewFiltersAsString', viewController.getViewFiltersAsString());
viewController.actionSetPage();}]]></xp:this.beforeRenderResponse>
Extension Library Dojo Name List Text Box control:
<xe:djextListTextBox id="djFilters" multipleSeparator=","
value="#{viewScope.viewFiltersAsString}" displayLabel="true"
title="Hier klicken um Filter zu löschen">
<xe:this.dataProvider>
<xe:simpleValuePicker caseInsensitive="false"
labelSeparator="~" valueListSeparator=","
valueList="#{viewController.viewFiltersLabelsAsString}">
</xe:simpleValuePicker>
</xe:this.dataProvider>
<xp:eventHandler event="onChange" submit="true" refreshMode="partial"
refreshId="${javascript:compositeData.refreshId}" execMode="partial"
action="#{javascript:viewController.setViewFiltersAsString(#Trim(viewScope.get('viewFiltersAsString')).toString())}">
</xp:eventHandler>
</xe:djextListTextBox>
Managed Bean "viewController"
/**
* Converts all filters to a useable format for dojo List Textbox
* #return string of filters
*/
public String getViewFiltersAsString() {
String filtersAsString = "";
for (ViewFilter filter : viewFilters) {
if (filtersAsString == "") filtersAsString = filter.getKey();
else filtersAsString += "," + filter.getKey();
}
return filtersAsString;
}
public void setViewFiltersAsString(String viewFiltersAsString) {
if (viewFiltersAsString != null && !viewFiltersAsString.equals("")) {
List<String> currentfilters = Converter.toList(",", viewFiltersAsString);
for (ViewFilter filter : viewFilters) {
boolean remove = true;
for (String currentFilter : currentfilters) {
if (filter.getKey().equals(currentFilter)) {
remove = false;
break;
}
}
if (remove) {
// user can click only one filter at one time
setActionViewFilter(viewFilters.removeFilter(filter));
break;
}
}
} else {
setActionViewFilter(viewFilters.removeFilter(viewFilters.get(0)));
}
}
Hint: viewFilters is a java.util.List of ViewFilter objects and a ViewFilter is a simple java class holding information about a filter (in your case a name)

Using StyledCellLabelProvider in a TableViewer killed my tooltips

I have a TableViewer in my Eclipse plugin.
When I was just using a regular label provider, my tooltips worked beautifully:
However, when I switched to have my LabelProvider implement IStyledLabelProvider, my tooltips went haywire:
Here is the code creating the StyledString
#Override
public StyledString getStyledText(final Object element) {
if( !(element instanceof MyInterface<?>) ) {
return null;
}
final String elemText = getColumnText(element, this.columnIndex);
final StyledString styledString = new StyledString(elemText == null ? "" : elemText);
if( !(element instanceof MyObject) ) {
return styledString;
}
final MyObject settingElement = (MyObject) element;
// grayed out text
if( settingElement.shouldBeGray() ) {
styledString.setStyle(0, elemText.length(), AdaptabilityStyles.GRAY_STYLER;
} else if( !settingElement.shouldBeBlue() ) {
styledString.setStyle(0, elemText.length(), AdaptabilityStyles.BLUE_STYLER);
}
return styledString;
}
And getTooltTipText()
#Override
public String getToolTipText(final Object element) {
return getColumnText(element, this.columnIndex);
}
What am I doing wrong?
As I was writing this question, I wanted to reference a bug report that I am familiar with that is related to tooltips. I looked at the bug report again and came across the following line:
For now, I simply try this :
ColumnViewerToolTipSupport.enableFor(commonViewer)
I wasn't calling that method when I created my viewer. When I tried that, my tooltips came back (though slightly different than they were before.

How to display a Labels 'Error on ErrorProvider1'

Goal
I want to display the text that I put in the Label's "Error on ErrorProvider1" attribute whenever I get an error. See the following label's attributes below.
I try to display the text in the red rectangle into my ErrorProvider1 SetError(control, value) function.
If TextBox1.Text.Trim.Contains("'") Then
ErrorProvider1.SetError(lblErr, ErrorProvider1.GetError(lblErr))
Else
ErrorProvider1.SetError(lblErr, "")
End If
How can I retrieve the 'Error on ErrorProvider1' text from the lblErr to display it in the ErrorProvider1 SetError value?
The ErrorProvider component is very awkward to use effectively. It is fixable however, I'll give an example in C# that extends the component with some new capabilities:
ShowError(Control ctl, bool enable) displays the text that you entered at design-time when the enable argument is true. The easier-to-use version of SetError().
HasErrors returns true if the any active warning icons are displayed. Handy in your OK button's Click event handler.
FocusError() sets the focus to the first control that has a warning icon, if any. It returns false if no warnings are remaining.
SetError() is a replacement of ErrorProvider.SetError(). You only need it if you add any controls after the form's Load event fired or if you need to modify the warning text.
Add a new class to your project and paste the code shown below. Compile. Drop it from the top of the toolbox onto the form. The design-time behavior is identical. Modestly tested.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.ComponentModel.Design;
class MyErrorProvider : ErrorProvider {
public void ShowError(Control ctl, bool enable) {
// Easy to use version of SetError(), uses design-time text
if (!enable) base.SetError(ctl, "");
else {
if (errors.ContainsKey(ctl)) base.SetError(ctl, errors[ctl]);
else base.SetError(ctl, "No error text available");
}
}
public bool HasErrors {
// True if any errors are present
get {
foreach (var err in errors)
if (!string.IsNullOrEmpty(base.GetError(err.Key))) return true;
return false;
}
}
public bool FocusError() {
// Set the focus to the first control with an active error
foreach (var err in errors) {
if (!string.IsNullOrEmpty(base.GetError(err.Key))) {
err.Key.Focus();
return true;
}
}
return false;
}
public new void SetError(Control ctl, string text) {
// Use this only to add/modify error text after the form's Load event
if (!string.IsNullOrEmpty(text)) {
if (errors.ContainsKey(ctl)) errors[ctl] = text;
else errors.Add(ctl, text);
}
base.SetError(ctl, text);
}
private void initialize(object sender, EventArgs e) {
// Preserve error text
copyErrors(((Form)sender).Controls);
}
private void copyErrors(Control.ControlCollection ctls) {
foreach (Control ctl in ctls) {
var text = this.GetError(ctl);
if (!string.IsNullOrEmpty(text)) {
errors.Add(ctl, text);
base.SetError(ctl, "");
}
copyErrors(ctl.Controls);
}
}
private Dictionary<Control, string> errors = new Dictionary<Control, string>();
// Plumbing to hook the form's Load event
[Browsable(false)]
public new ContainerControl ContainerControl {
get { return base.ContainerControl; }
set {
if (base.ContainerControl == null) {
var form = value.FindForm();
if (form != null) form.Load += initialize;
}
base.ContainerControl = value;
}
}
public override ISite Site {
set {
// Runs at design time, ensures designer initializes ContainerControl
base.Site = value;
if (value == null) return;
IDesignerHost service = value.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (service == null) return;
IComponent rootComponent = service.RootComponent;
this.ContainerControl = rootComponent as ContainerControl;
}
}
}
Your issue is that you are replacing the error message when nothing is wrong. As noted in your comment below, you are storing the localized error message in the label's Tag, so you can do the following:
If TextBox1.Text.Trim.Contains("'") Then
ErrorProvider1.SetError(lblErr, lblErr.Tag)
Else
ErrorProvider1.SetError(lblErr, "")
End If
You were correct to use ErrorProvider1.GetError(Control) to get the value. It's just that you're more than likely replacing it with an empty string before you were retrieving it.