Swt FormToolkit focus issue - java

I have a strange issue: there is a SectionPart with composite, which is create from FormToolkit#createComposite(getSection()). Composite contains some number of widgets, which are positioned vertically one under other (as in a usual form). When the cursor is inside some widget, let's say input filed and I am clicking right between two fields on empty space, then focus automatically jumps to the first field in this composite.
I've tried to set SWT.NO_FOCUS style bit to the first widget in the form (usually it is a TableComboViewer) but it didn't helped (it seems, that this bit is not set on TableCombo, which is inside TableComboViewer).
So, have anybody faced something similar, or are there any workarounds for this problem or any clues what could it be?
Upd1: setting NO_FOCUS style helps for non TableComboViewer widgets (in this case they are not receiving focus). In case of TableComboViewer TableCombo widget contains Text widget, which receives focus, but even, if I add NO_FOCUS bit, it is not applied to Text style. I've checked source of TableCombo and there is a method checkStyle, which does following:
private static int checkStyle (int style) {
int mask = SWT.BORDER | SWT.READ_ONLY | SWT.FLAT | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
return SWT.NO_FOCUS | (style & mask);
}
I am not actually sure what it does, cause I am not really good in bitwise operation, but seems, that this is the problem, why I can't set NO_FOCUS flag.
I don't understand though, why when I am clicking on Composite, it tries to set foxus on it's children, can I somehow suppress this?
Upd2: The reason is probably found, it is said, that:
When the view is activated, focus is transferred to the form, which passes it to the first control capable of accepting focus, our link in this case.
And it seems, that it is not possible to forbid this.
Thanks in advance,
AlexG

You problem lies in Composite.setFocus().. have a look at this:
public boolean setFocus () {
checkWidget ();
Control [] children = _getChildren ();
for (int i= 0; i < children.length; i++) {
if (children [i].setFocus ()) return true;
}
return super.setFocus ();
}
As you can see, this will try to set the focus on the first control in the composite that will allow for the focus...
[EDIT - the following is added to clarify...]
The above method would not be a problem if it wasn't for the MouseListener that is installed on all Composites in FormToolkit.adapt(Composite composite):
public void adapt(Composite composite) {
composite.setBackground(colors.getBackground());
composite.addMouseListener(new MouseAdapter() {
public void mouseDown(MouseEvent e) {
((Control) e.widget).setFocus();
}
});
if (composite.getParent() != null)
composite.setMenu(composite.getParent().getMenu());
}
I have solved this problem on a number of occasions by having my own FormToolkit.adapt(Composite composite) in a sub-class that does the right thing - I just exchange setFocus() with forceFocus(). Though that can occasionally give you other problems...

Related

how do I simulate "onStartCellEditing" for DefaultCellEditor

CellEditorListener has "editingStopped" and "editingCancelled". But how might I implement a piece of code which needs to run when a cell editing session starts?
A typical example might be where you want the text of a JTextField editor component to go selectAll() when you start editing. I'm tempted to think the thing to do is override one of the methods of DefaultCellEditor, such as getTableCellEditorComponent or getCellEditorValue or getComponent, but none of these explicitly says they are called at the start of an edit session.
Conversely, we do know that JTable.getCellEditor returns the editor if we are editing, but null if not. This is because the component is made into a child object of JTable when editing starts. It also seems to get the focus at the beginning of an editing session, so you might perhaps think of add a FocusListener to the editor component (JTextField, etc.). But is this guaranteed? Also, maybe focus can be lost and then return during an editing session.
There's a way of listening for the "addition" (as a child object) of this editor component: ContainerListener. Unless someone tells me different this appears to be the most direct and rational way of getting a call that an editing session has begun. But it seems odd that there isn't a more direct way...
A typical example might be where you want the text of a JTextField editor component to go selectAll() when you start editing.
You can override the editCellAt(...) method of JTable to select the text once editing has started:
#Override
public boolean editCellAt(int row, int column, EventObject e)
{
boolean result = super.editCellAt(row, column, e);
final Component editor = getEditorComponent();
if (editor != null && editor instanceof JTextComponent)
{
((JTextComponent)editor).selectAll();
if (e == null)
{
((JTextComponent)editor).selectAll();
}
else if (e instanceof MouseEvent)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
((JTextComponent)editor).selectAll();
}
});
}
}
return result;
}
Rob Camick's answer is great, but I have another one for "completists" to peruse.
I use Jython but it should be simple enough for Java people to understand. In Python/Jython you can add arbitrary "attributes" to (almost) any object. So in editCellAt I add an attribute "start_editing" which then signals to the caret listener added to the JTextField editor component that a session has just begun. If the caret dot and mark are equal (collapsed), if the click-count-to-start-editing == 2, and if the editor has the "start_editing" attr, you select-all (again), and you also remove the "start_editing" attr... it works (!), without spawning a new Runnable.
class DatesTable( javax.swing.JTable ):
def editCellAt(self, row, column, event_obj ):
result = self.super__editCellAt( row, column, event_obj )
if self.editorComponent:
self.editorComponent.requestFocus() # explanation below
self.editorComponent.selectAll()
if isinstance( event_obj, java.awt.event.MouseEvent ):
self.cellEditor.start_editing = None
return result
class DatesTableCellEditor( javax.swing.DefaultCellEditor ):
def __init__( editor_self, table, *args, **kvargs ):
jtf = javax.swing.JTextField()
class JTFCaretListener( javax.swing.event.CaretListener ):
def caretUpdate( self, caret_event ):
if hasattr( editor_self, 'start_editing' ):
del editor_self.start_editing
if caret_event.dot == caret_event.mark and editor_self.clickCountToStart == 2:
caret_event.source.selectAll()
jtf.addCaretListener( JTFCaretListener())
javax.swing.DefaultCellEditor.__init__( editor_self, jtf, **kvargs )
A similar result could be achieved in Java, clearly, using a private field of the editor, or something along those lines.
NB why have I put "requestFocus" in there? If you press key F2 on an editable table cell the editing starts and the editor component (typically a JTextField) gets focus automatically. However you can also start editing just by typing into the table cell. It took me much head-scratching to work out that, oddly, if you do this you do indeed start editing, but the editor component does not get focus automatically. So this is a way to "resolve this anomaly".
NB2 One final point about my solution. In a real-world implementation you also need to set a timer to remove the attribute "start_editing" after a certain finite time (e.g. 0.5 s). Otherwise you might click once, which would not start an editing session (if click-to-start == 2), and then click again 10 seconds later, after having started editing by another means, to find that selectAll happens confusingly and inexplicably. This attribute must be made to be like the tape in Mission Impossible: self-destruct after x seconds...

Validate CTabFolder before switching tabs

In a CTabFolder, I'd like to check the content for unsaved data before the user can switch from one tab to another. SWT does not provide a PreSelection event, as stated here.
I found a workaround, suggesting to switch back to the old tab when a selection is triggered, validate the data and then perform the desired switch again, if data is valid.
I do understand the general idea of this workaround, however, it is not working for me. oldPageIndex and newPageIndex do always have the same value, though I did not click on the same tab.
this.tabContainer.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent event) {
int oldPageIndex = tabContainer.getSelectionIndex();
int newPageIndex = tabContainer.indexOf((CTabItem)event.item);
// Here: oldPageIndex == newPageIndex
...
}
});
Is this workaround still working or is there anything I could possibly be doing wrong? Or maybe, has there been any fix for a real PreSelection event in the meantime? I tried using event.doit, but the SelectionEvent is fired, when the tabs have been switched already.
You can use the selection listener but as you have found the getSelectionIndex() does not give you the old tab. So you will have to maintain the old tab index yourself.
This is the technique used by the Eclipse FormEditor.

Eclipse RCP view will not update when editor data is modified

I'm attempting to do something that seems like it should be quite common, so I'm surprised I'm having a hard time finding a solution.
I have a simple Eclipse RCP application. It consists of a view, containing a treeViewer that displays elements of an xml file hierarchically. The other side is an editor, which contains various fields such as textboxes, etc, for displaying and also modifying the xml values. The treeviewer displays icons alongside the element name, and what I'm trying to do is change the icon to a "modified" version of the icon whenever a change is made in the editor - signifying that a value of that element has been changed. This is very similar to how Eclipse, when integrated with subversion, shows that a file has been modified from the base revision in the Package Explorer.
I'll try to just show the parts of the code relevant to this specific issue and hope I don't leave anything out. This is the editor class:
public class XmlEditor extends EditorPart
{
protected boolean dirty = false;
public void setDirty(boolean value)
{
dirty = value;
firePropertyChange(PROP_DIRTY);
}
}
and this is the view with the tree:
public class TreeView extends ViewPart implements IPropertyChangeListener {
public void createPartControl(Composite parent) {
treeViewer = new TreeViewer(parent, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL);
getSite().setSelectionProvider(treeViewer);
treeViewer.setLabelProvider(new TreeObjLabelProvider());
treeViewer.setContentProvider(new TreeObjContentProvider());
PlatformUI.getWorkbench().getWorkingSetManager().addPropertyChangeListener(this);
}
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().equals(IWorkbenchPartConstants.PROP_DIRTY)) {
treeViewer.refresh();
}
}
}
In this scenario, TreeView::propertyChange() is not getting called, even though firePropertyChange() is getting fired. Any ideas why? I'm also open to any other ideas that don't involve PropertyChangeListener, it just seemed like this would be the easiest way at the time. Thank you.
Here's how I ended up solving the problem:
Changed TreeView to implement IPropertyListener instead of IPropertyChangeListener
Implemented the propertyChanged() method to perform a treeViewer.refresh()
In the XmlEditor::createPartControl() method I got a reference to the Treeview part and then added it to the property listeners like so:
TreeView treeView = (TreeView) getSite().getPage().findView(TreeView.ID);
addPropertyListener(treeView);
Now, TreeView::propertyChanged() gets called after firePropertyChanged(), just like I needed. It took quite a bit of experimentation to learn the difference between IPropertyListener and IPropertyChangeListener, addPropertyListener() and addPartPropertyListener().
Thanks to nitind for giving me a new perspective, and for showing me about Decorators, which is definitely the right way to go as opposed to changing the tree icon to a modified version.
You fired a property change in the editor part, which is unrelated to the working set manager. Nothing you've done connects the view to the editor. If you want the two to talk to each other, write them to talk to each other, or at least create and react to events from making the modifications you describe.
I'm also pretty certain that's not how SVN shows that a file has been modified.
SVN is probably supplying a Decorator: http://eclipse.org/articles/Article-Decorators/decorators.html
Add this bunch of code in your create part control this will may be help you
ResourcesPlugin.getWorkspace().addResourceChangeListener(new IResourceChangeListener() {
#Override
public void resourceChanged(IResourceChangeEvent event) {
treeViewer.refresh();
}
});

How to create controls dynamically in JFace Wizard

I have a jFace wizard, I am using this to create a new project type eclipse plugin. As you can see from image below, I have one treeviewer on left side, and a SWT group on right side. What I want is when ever user selects one of the item from treeviewer, I should be able to create dynamic controls on right side SWT Group. Say user selects Test One, one right side I should be able to create few controls like label, text and few radio buttons on right side, similarly if user selects Test Two I should be able to create dynamic controls on right side.
Currently I tried below code:
tree.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
for (int i = 0; i < selection.length; i++) {
String tempStr = selection[i].toString();
tempStr = tempStr.replaceAll("TreeItem \\{", "");
String finalStr = tempStr.replaceAll("\\}", "");
if (finalStr.equals("Test One")) {
Button btn = new Button(g2, SWT.NONE); //g2 is right side group
btn.setText("Blaaaa");
btn.setVisible(true);
container.redraw();
}
}
But when I run, I see no changes on right group. Can anyone guide me what I am doing wrong? Any pointers would be very appreciated, since I am new to Eclipse development and SWT.
You probably didn't set a layout on the g2 group. This is the common cause for controls not showing up. You can also try using g2.layout() to ensure that the new controls are correctly laid out after you create them.
Additionally you could look at using a StackLayout so that once you create a set of controls you can just hide them all at once instead of destroying when the selection changes. This is often useful so that if the user comes back to a previous selection, they will find the data they entered in the same state when they switched the selection. Here is an example.

How to prevent GWT DialogBox from being dragged out of the screen?

I am looking for a simple solution for the issue that my GWT DialogBox can be dragged out of the screen. The host has the CSS rule overflow:hidden because I do not want any scrollbars to appear.
Obviously I need to attach somehow a listener to the dragging and prevent moves that would bring it outside. I can only see onMouseMove, beginDragging, endDragging methods in DialogBox.
"We" have worked around this issue in the following way:
#Override
protected void endDragging(MouseUpEvent event)
{
int genericMargin = 60;
int leftMargin = -(this.getOffsetWidth() - genericMargin);
int lowerMargin = Window.getClientHeight() - genericMargin;
int rightMargin = Window.getClientWidth() - genericMargin;
int upperMargin = 0;
if (this.getAbsoluteLeft() > rightMargin)
{this.setPopupPosition(rightMargin, this.getPopupTop()); }
if (this.getAbsoluteLeft() < leftMargin)
{ this.setPopupPosition(leftMargin, this.getPopupTop()); }
if (this.getAbsoluteTop() > lowerMargin)
{ this.setPopupPosition(this.getPopupLeft(), lowerMargin);}
if (this.getAbsoluteTop() < upperMargin)
{ this.setPopupPosition(this.getPopupLeft(), upperMargin);}
super.endDragging(event);
}
BTW it correctly works as it is! ;)
I would suggest trying gwtquery-dnd. I have been using the drag and drop plugin and it works great. It has an option to setContianment(Element elem) which is what you are looking for. Some other features is that it has snap so you can snap to other widgets if you wish to dock your dialog box somewhere. It also has the ability to specify a handle similar to the DialogBox header for dragging.
http://code.google.com/p/gwtquery-plugins/wiki/DragAndDropPluginForGWTDeveloppers
You can investigate com.google.gwt.user.client.ui.DialogBox source code and override all methods for your need. There are a couple of methods responsible for dragging there.
Not sure you can solve this problem in other ways. Otherwise you need to develop custom draggable popup panel which I am sure not a good solution.

Categories

Resources