I am making an application which contains 2 views. The first one contains a tree viewer which displays the folders from my system and the second one contains a table viewer which displays the content of a directory selected in the first view. Long story short: a file explorer.
//initialization of the table viewer
tableViewer = new TableViewer(parent, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL);
tableViewer.setContentProvider(new FileTableContentProvider());
tableViewer.setLabelProvider(new FileTableLabelProvider());
//from within the implementation of any view or editor
/*
* getSite() - Returns the site for this workbench part (a workbench part can be a view (IViewPart) or an editor (IEditorPart))
* this view is a selection provider; the view sends the event to all the views registered to the selection service
*/
getSite().setSelectionProvider(tableViewer);
//the table column "Name" is added to the table viewer
TableColumn columnName = new TableColumn(tableViewer.getTable(), SWT.LEFT);
columnName.setText("Name");
columnName.setResizable(true);
columnName.setWidth(200);
//the table column "Date modified" is added to the table viewer
TableColumn columnDateModified = new TableColumn(tableViewer.getTable(), SWT.LEFT);
columnDateModified.setText("Date modified");
columnDateModified.setResizable(true);
columnDateModified.setWidth(200);
//the table column "Type" is added to the table viewer
TableColumn columnType = new TableColumn(tableViewer.getTable(), SWT.LEFT);
columnType.setText("Type");
columnType.setResizable(true);
columnType.setWidth(200);
//make the header of the table visible
tableViewer.getTable().setHeaderVisible(true);
/*
* getSite().getPage() - gets the active workbench page.
*/
getSite().getPage().addSelectionListener("com.awebofcode.fileexplorer.view.filetree",(ISelectionListener)this);
/*
* add a doubleClickListener for:
* 1) if the object selected is a file, then the file will be opened with the associated program
* 2) if the object selected is a directory, then enter the folder and update the tree viewer
*/
tableViewer.addDoubleClickListener(new IDoubleClickListener(){
#Override
public void doubleClick(DoubleClickEvent event) {
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
File itemSelected = (File) selection.getFirstElement();
//if the selected item is a file a double click will launch the associated program
if (itemSelected.isFile() && itemSelected.exists()){
Program.launch(itemSelected.getAbsolutePath());
}else if (itemSelected.isDirectory()){
/*
* Update the tree viewer;
* PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().findView(FileTreeView.ID) --> returns the view with the specified ID
* setSelection() will send an event and the setSelectionChanged will run
*/
((FileTreeView) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().findView(FileTreeView.ID)).getTreeViewer().setSelection(selection);
}
}
});
//create the cell editor
CellEditor[] editors = new CellEditor[1];
editors[0] = new TextCellEditor(tableViewer.getTable());
tableViewer.setColumnProperties(new String[]{"Name", "Date modified", "Type"});
//assign the cell editors to the table viewer
tableViewer.setCellEditors(editors);
//set the cell modifier to the table viewer
tableViewer.setCellModifier(new NameCellModifier(tableViewer));
I created an action that will rename the selected file or folder from the second view.
When I click on the name of the file I don't want to enter in the editing mode.I want to stay in the selection mode and only after I click File -> Rename (action rename) the editing mode has to be enabled.
The problem is that I cannot find how to enable/disable the TextCellEditor.
I didn't find a clean solution. I will explain you what I have done so far. I have modified the code that I have posted. I use TableViewerColumn instead of TableColumn.
Class FileTableView:
public class FileTableView extends ViewPart implements ISelectionListener{
private TableViewer tableViewer;
private boolean cellEditable = false;
private FirstColumnEditingSupport obj;
public FileTableView() {
super();
}
#Override
public void createPartControl(Composite parent) {
//initialization of the table viewer
tableViewer = new TableViewer(parent, SWT.BORDER | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.MULTI); //SWT.MULTI - for multiple selection; SWT.FULL_SELECTION - for selection of an entire row
tableViewer.setContentProvider(new FileTableContentProvider());
//it is not necessary to add a label provider for the table viewer because we will set a label provider for every column of the table
//tableViewer.setLabelProvider(new FileTableLabelProvider());
//extract the table widget of the table viewer
final Table table = tableViewer.getTable();
//make the header of the table visible
table.setHeaderVisible(true);
//hide the lines of the table
table.setLinesVisible(false);
//create the columns of the table
createColumns(parent, tableViewer);
//set the sorter to the table viewer
tableComparator = new TableViewerComparator();
tableViewer.setComparator(tableComparator);
//from within the implementation of any view or editor
/*
* getSite() - Returns the site for this workbench part (a workbench part can be a view (IViewPart) or an editor (IEditorPart))
* this view is a selection provider; the view sends the event to all the views registered to the selection service
*/
getSite().setSelectionProvider(tableViewer);
tableViewer.addSelectionChangedListener(new ISelectionChangedListener(){
public void selectionChanged(SelectionChangedEvent event){
setCellEditable(false);
}
});
}
/*
* method used to update the viewer from outside
*/
public void refresh(){
tableViewer.refresh();
}
#Override
public void setFocus() {
tableViewer.getControl().setFocus();
}
//method that returns the table viewer
public TableViewer getTableViewer(){
return tableViewer;
}
/*
* get the value of the cellEditable
*/
public boolean getCellEditable(){
return cellEditable;
}
/*
* set the value of the cellEditable
*/
public void setCellEditable(boolean cellEditable){
this.cellEditable = cellEditable;
}
public FirstColumnEditingSupport getEditingSupport(){
return obj;
}
/*
* method that creates columns of the table
*/
private void createColumns(final Composite parent, final TableViewer viewer){
String[] titles = {"Name", "Date Modified", "Size"};
int[] width = {200, 200, 200};
//first column is for the name of the file
TableViewerColumn col = createTableViewerColumn(titles[0], width[0], 0);
col.setLabelProvider(new ColumnLabelProvider(){
public String getText(Object element){
return ((File) element).getName();
}
public Image getImage(Object element){
return getFirsColumnImage((File) element);
}
});
obj = new FirstColumnEditingSupport(tableViewer,this);
col.setEditingSupport(obj);
//second column is for the last date modified
col = createTableViewerColumn(titles[1], width[1], 1);
col.setLabelProvider(new ColumnLabelProvider(){
public String getText(Object element){
return formatLastModifiedDate((File) element);
}
});
//third column is for size
col = createTableViewerColumn(titles[2], width[2], 2);
col.setLabelProvider(new ColumnLabelProvider(){
public String getText(Object element){
return formatLength((File) element);
}
});
}
private TableViewerColumn createTableViewerColumn(String title, int width, final int columnNumber){
final TableViewerColumn viewerColumn = new TableViewerColumn(tableViewer, SWT.NONE);
final TableColumn column = viewerColumn.getColumn();
column.setText(title);
column.setWidth(width);
column.setResizable(true);
column.setMoveable(false);
column.addSelectionListener(getSelectionAdapter(column, columnNumber));
return viewerColumn;
}
private SelectionAdapter getSelectionAdapter(final TableColumn column, final int index){
SelectionAdapter selectionAdapter = new SelectionAdapter(){
public void widgetSelected(SelectionEvent e){
tableComparator.setColumn(index);
int direction = tableComparator.getDirection();
tableViewer.getTable().setSortDirection(direction);
tableViewer.getTable().setSortColumn(column);
tableViewer.refresh();
}
};
return selectionAdapter;
}
/*
* method used to return the last modified date in "dd-MM-yyyy hh:mm:ss" format
*/
private String formatLastModifiedDate(File file){
Date d = new Date(file.lastModified());
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
return sdf.format(d);
}
/*
* method used to return the length of the file in KB
*/
private String formatLength(File file){
long size = file.length()/1024;
NumberFormat f = new DecimalFormat("#,###,### KB");
return f.format(size);
}
}
FirstColumnEditingSupport.java file:
/*
* The EditingSupport implementation defines how the content can be changed.
*/
public class FirstColumnEditingSupport extends EditingSupport {
private TableViewer tableViewer;
private FileTableView view;
private TextCellEditor textEditor;
public FirstColumnEditingSupport(TableViewer viewer, FileTableView view) {
super(viewer);
this.tableViewer = viewer;
this.view = view;
textEditor = new TextCellEditor(tableViewer.getTable());
}
#Override
/*
* (non-Javadoc)
* #see org.eclipse.jface.viewers.EditingSupport#getCellEditor(java.lang.Object)
* EditingSupport returns in his getCellEditor() method an object of type CellEditor. This object creates the controls to change the data.
*/
protected CellEditor getCellEditor(Object element) {
return textEditor;
}
#Override
/*
* (non-Javadoc)
* #see org.eclipse.jface.viewers.EditingSupport#canEdit(java.lang.Object)
* The canEdit() method defines, if the cell can be edited.
*/
protected boolean canEdit(Object element) {
return view.getCellEditable();
}
#Override
/*
* (non-Javadoc)
* #see org.eclipse.jface.viewers.EditingSupport#getValue(java.lang.Object)
* The getValue() method receives the current object and returns the value which should be displayed.
*/
protected Object getValue(Object element) {
return ((File) element).getName();
}
#Override
/*
* (non-Javadoc)
* #see org.eclipse.jface.viewers.EditingSupport#setValue(java.lang.Object, java.lang.Object)
* The method setValue() in EditingSupport receives the changed value based on the user input. In this method you assign the value to your data object.
*/
protected void setValue(Object element, Object value) {
//value is the user input
File oldFile = (File) element;
//String path = oldFile.getAbsolutePath().substring(oldFile.getAbsolutePath().lastIndexOf(File.pathSeparatorChar));
//System.out.println(oldFile.getParent());
oldFile.renameTo(new File(oldFile.getParent() + "\\" + (String) value));
tableViewer.refresh();
}
public TextCellEditor getTextCellEditor(){
return textEditor;
}
}
My RenameAction file:
public class RenameAction extends Action implements ISelectionListener, ActionFactory.IWorkbenchAction {
private final IWorkbenchWindow window;
private IStructuredSelection itemSelected;
public final static String ID = PlatformUI.PLUGIN_ID + ".RenameAction";
public RenameAction(IWorkbenchWindow window){
this.window = window;
setId(ID);
setText("&Rename");
setToolTipText("Rename the file or directory selected");
window.getSelectionService().addSelectionListener(this);
}
#Override
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
if(selection.isEmpty() || !(selection instanceof IStructuredSelection)){
return;
}else {
itemSelected = (IStructuredSelection)selection;
}
}
public void dispose(){
window.getSelectionService().removeSelectionListener(this);
}
public void run(){
Object firstElement = itemSelected.getFirstElement();
File item = (File) firstElement;
if(item != null){
FileTableView myTreeView= (FileTableView) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().findView(FileTableView.ID);
myTreeView.setCellEditable(true);
}
}
}
My idea was:
by default: the cell editor of the first column is disabled
when the rename action is triggered (press the menu button), the cell editor of the column is enabled (myTreeView.setCellEditable(true))
the table line selected is in editing mode and you can change the name of the file. This part is not working quite well, because after you press the rename menu button, you can't see that the editing mode is enabled. After you click on the table line, the editing mode is activated. Also, the editing mode is enabled for all the table lines and I wanted to be enabled only for the selected one. For this problem I haven't got a solution.
after you finish typing the new name of the file, and change the selection (click on a different table line), the editing mode is disabled:
tableViewer.addSelectionChangedListener(new ISelectionChangedListener(){
public void selectionChanged(SelectionChangedEvent event){
setCellEditable(false);
}
});
I hope my solution can help you.
Related
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;
}
I have a view where I have two tree viewers. In the createPartControl() how I can set the selectionProviders for both the views. It takes either one of them.
Here is my snippet.
The View code:
IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
IWorkbenchPartSite activePartSite = workbenchWindow.getActivePage().getActivePart().getSite();
activePartSite.setSelectionProvider(inputTreeViewer);
activePartSite.setSelectionProvider(compositeTreeViewer);
// Here the last line the selection provider code for the top one.
// Hence I only get selections from the second tree.
How can we solve the problem? I tried with inheriting the view class from ISelectionProvider and overriding getSelection() method. But that was not of any help.
Can someone suggest something?
I suggest to add a focus listener to each of the trees and call partSite.setSelectionProvider() with the currently focused tree viewer. For example:
treeViewer1.getTree().addListener( SWT.FocusIn, event -> site.setSelectionProvider( treeViewer1 ) );
treeViewer2.getTree().addListener( SWT.FocusIn, event -> site.setSelectionProvider( treeViewer2 ) );
I'll leave it as an exercise to the reader to remove the listener-duplication.
Alternatively, you can create a proxy selection provider that listens for selection changes in both tree viewers and propagates the most recent selection. Something along these lines:
public class ProxySelectionProvider implements ISelectionProvider {
private ISelection selection;
private final Collection<ISelectionChangedListener> listeners;
public ProxySelectionProvider( StructuredViewer... viewers ) {
listeners = new ArrayList<>();
selection = StructuredSelection.EMPTY;
for( StructuredViewer viewer : viewers ) {
ISelectionChangedListener selectionListener = this::selectionChanged;
viewer.addSelectionChangedListener( selectionListener );
viewer.getControl().addDisposeListener( event -> viewer.removeSelectionChangedListener( selectionListener ) );
}
}
#Override
public void addSelectionChangedListener( ISelectionChangedListener listener ) {
listeners.add( listener );
}
#Override
public void removeSelectionChangedListener( ISelectionChangedListener listener ) {
listeners.remove( listener );
}
#Override
public ISelection getSelection() {
return selection;
}
#Override
public void setSelection( ISelection selection ) {
this.selection = selection;
}
private void selectionChanged( SelectionChangedEvent event ) {
selection = event.getSelection();
notifyListeners();
}
private void notifyListeners() {
SelectionChangedEvent event = new SelectionChangedEvent( this, selection );
new ArrayList<>( listeners ).forEach( listener -> listener.selectionChanged( event ) );
}
}
Use the selection provider like this:
ISelectionProvider selectionProvider = new ProxySelectionProvider( treeViewer1, treeViewer2 );
IWorkbenchPartSite partSite = workbenchWindow.getActivePage().getActivePart().getSite();
partSite.setSelectionProvider( selectionProvider );
I solved this problem using this approach.
public class SelectionProviderAdapater implements IPostSelectionProvider {
public class InternalListener implements ISelectionChangedListener, FocusListener {
#Override
public void focusGained(FocusEvent e) {
doFocusChanged(e.widget);
}
#Override
public void focusLost(FocusEvent e) {
}
#Override
public void selectionChanged(SelectionChangedEvent event) {
doSelectionChanged(event);
}
}
/**
* The array of viewers.
*/
private StructuredViewer[] viewers;
/**
* The current viewer in focus.
*/
private StructuredViewer viewerInFocus;
/**
* The list of selection changed listeners.
*/
private final ListenerList selectionChangedListeners = new ListenerList();
/**
* The list of post selection changed listeners.
*/
private final ListenerList postSelectionChangedListeners = new ListenerList();
/**
* The internal listener instance.
*/
private final InternalListener internalListener = new InternalListener();
/**
* The internal post selection listener instance.
*/
private final ISelectionChangedListener internalPostSelectionListener = new ISelectionChangedListener() {
#Override
public void selectionChanged(SelectionChangedEvent event) {
doPostSelectionChanged(event);
}
};
#Override
public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
postSelectionChangedListeners.add(listener);
}
#Override
public void addSelectionChangedListener(ISelectionChangedListener listener) {
selectionChangedListeners.add(listener);
}
/**
* Sets the viewer in focus and fires selection change events.
*
* #param control
* The {#link Widget} that has gained focus.
*/
private void doFocusChanged(Widget control) {
for (StructuredViewer viewer : viewers) {
if (viewer.getControl() == control) {
propagateFocusChanged(viewer);
return;
}
}
}
/**
* Fires post selection changed events if the {#link SelectionChangedEvent}
* 's selection provider is the current viewer in focus.
*
* #param event
* The selection changed event.
*/
private void doPostSelectionChanged(SelectionChangedEvent event) {
ISelectionProvider provider = event.getSelectionProvider();
if (provider == viewerInFocus) {
firePostSelectionChanged();
}
}
/**
* Fires selection changed events if the {#link SelectionChangedEvent}'s
* selection provider is the current viewer in focus.
*
* #param event
* The selection changed event.
*/
private void doSelectionChanged(SelectionChangedEvent event) {
ISelectionProvider provider = event.getSelectionProvider();
if (provider == viewerInFocus) {
fireSelectionChanged();
}
}
/**
* Notifies post selection changed listeners of a selection changed event.
*/
private void firePostSelectionChanged() {
if (postSelectionChangedListeners != null) {
SelectionChangedEvent event = new SelectionChangedEvent(this, getSelection());
Object[] listeners = postSelectionChangedListeners.getListeners();
for (Object listener : listeners) {
ISelectionChangedListener selectionChangedListener = (ISelectionChangedListener) listener;
selectionChangedListener.selectionChanged(event);
}
}
}
/**
* Notifies selection changed listeners of a selection changed event.
*/
private void fireSelectionChanged() {
if (selectionChangedListeners != null) {
SelectionChangedEvent event = new SelectionChangedEvent(this, getSelection());
Object[] listeners = selectionChangedListeners.getListeners();
for (Object listener : listeners) {
ISelectionChangedListener selectionChangedListener = (ISelectionChangedListener) listener;
selectionChangedListener.selectionChanged(event);
}
}
}
#Override
public ISelection getSelection() {
if (viewerInFocus != null) {
return viewerInFocus.getSelection();
}
return StructuredSelection.EMPTY;
}
public StructuredViewer getViewerInFocus() {
return viewerInFocus;
}
/**
* Sets the viewer as the viewer in focus and fires selection changed
* events.
*
* #param viewer
* The new viewer in focus.
*/
private void propagateFocusChanged(StructuredViewer viewer) {
if (viewer != viewerInFocus) {
viewerInFocus = viewer;
fireSelectionChanged();
firePostSelectionChanged();
}
}
#Override
public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
postSelectionChangedListeners.remove(listener);
}
#Override
public void removeSelectionChangedListener(ISelectionChangedListener listener) {
selectionChangedListeners.remove(listener);
}
#Override
public void setSelection(ISelection selection) {
if (viewerInFocus != null) {
viewerInFocus.setSelection(selection);
}
}
/**
* Sets the selection on the current viewer in focus.
*
* #param selection
* The selection to set.
* #param reveal
* true if the selection is to be made visible, and false
* otherwise
*/
public void setSelection(ISelection selection, boolean reveal) {
if (viewerInFocus != null) {
viewerInFocus.setSelection(selection, reveal);
}
}
/**
* Sets the collection of viewers to be monitored by this mediator.
*
* #param newViewers
* The collection of viewers.
*/
public void setViewers(Collection<? extends StructuredViewer> newViewers) {
// Remove listeners from any previous viewers.
if (viewers != null) {
for (StructuredViewer viewer : viewers) {
viewer.removeSelectionChangedListener(internalListener);
viewer.removePostSelectionChangedListener(internalPostSelectionListener);
Control control = viewer.getControl();
if (!control.isDisposed()) {
control.removeFocusListener(internalListener);
}
}
}
viewers = null;
viewerInFocus = null;
if (newViewers != null) {
viewers = newViewers.toArray(new StructuredViewer[newViewers.size()]);
for (StructuredViewer viewer : viewers) {
viewer.addSelectionChangedListener(internalListener);
viewer.addPostSelectionChangedListener(internalPostSelectionListener);
Control control = viewer.getControl();
control.addFocusListener(internalListener);
if (control.isFocusControl()) {
propagateFocusChanged(viewer);
}
}
}
}
}
Create an instance of this class in your view.Set the viewers using setViewers() method which are needed to be listened for selection.Then set it as the selection provider.It should start working.
So I am developing an Eclipse plug-in and using ColumnLabelProvider to provide label for the columns of my tree viewer.
However, in one of the columns, I only intend to display an image and no text. However, in the final display, Eclipse reserves blank space for the text element even if I return a null.
Is there any way to make it display only image and in the full space provided?
Here is the code snippet:
column4.setLabelProvider(new ColumnLabelProvider() {
#Override
public String getText(Object element) {
return null;
}
#Override
public Image getImage(Object element) {
/* Code to Display an image follows */
.....
}
});
ColumnLabelProvider will always leave space for the text.
You can use a class derived from OwnerDrawLabelProvider to draw the column yourself.
Something like:
public abstract class CentredImageCellLabelProvider extends OwnerDrawLabelProvider
{
protected CentredImageCellLabelProvider()
{
}
#Override
protected void measure(Event event, Object element)
{
}
#Override
protected void erase(final Event event, final Object element)
{
// Don't call super.erase() to suppress non-standard selection draw
}
#Override
protected void paint(final Event event, final Object element)
{
TableItem item = (TableItem)event.item;
Rectangle itemBounds = item.getBounds(event.index);
GC gc = event.gc;
Image image = getImage(element);
Rectangle imageBounds = image.getBounds();
int x = event.x + Math.max(0, (itemBounds.width - imageBounds.width) / 2);
int y = event.y + Math.max(0, (itemBounds.height - imageBounds.height) / 2);
gc.drawImage(image, x, y);
}
protected abstract Image getImage(Object element);
}
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
I have tableview with checkbox in each row and I have an action button. My problem is that, how can I get selected checkbox from tableview to apply an action when the button is pressed?
This is how I add the checkbox to the tableview
public void addCeckBoxToTableView() {
/** define a simple boolean cell value for the action column so that the column will only be shown for non-empty rows. */
tcCb.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Object, Boolean>,
ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Object, Boolean> p) {
return new SimpleBooleanProperty(p.getValue() != null);
}
});
/** create a cell value factory with an add button for each row in the table. */
tcCb.setCellFactory(new Callback<TableColumn<Object, Boolean>, TableCell<Object, Boolean>>() {
#Override
public TableCell<Object, Boolean> call(TableColumn<Object, Boolean> p) {
return new CheckBoxCell();
}
});
}
private class CheckBoxCell extends TableCell<Object, Boolean> {
CheckBox checkBox = new CheckBox();
HBox hb = new HBox(checkBox);
/**
* places button in the row only if the row is not empty.
*/
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(hb);
} else {
setGraphic(null);
}
}
}
cordially.
I'm of the same opinion as James: "with that setup you can't"
But you could do it this way
private TableView<Record> table;
private TableColumn<Record, Boolean> tcCb;
private Button actionButton;
public class Record {
private SimpleBooleanProperty selected = new SimpleBooleanProperty();
public SimpleBooleanProperty selectedProperty() {
return selected;
}
}
#Override
public void start(Stage primaryStage) throws Exception {
table = new TableView<Record>(FXCollections.observableArrayList(new Record(), new Record(), new Record()));
table.setEditable(true);
// Create "CheckBox" - Column
tcCb = new TableColumn<Record, Boolean>("Boolean-Column");
tcCb.setCellValueFactory(new PropertyValueFactory<Record, Boolean>("selected"));
tcCb.setCellFactory(CheckBoxTableCell.forTableColumn(tcCb));
tcCb.setEditable(true);
table.getColumns().add(tcCb);
// Create actionButton for retrieving cellData
actionButton = new Button("action");
actionButton.setOnAction(actionEvent -> {
for (int row = 0; row < table.getItems().size(); row++) {
System.out.println(tcCb.getCellData(row));
}
});
// The uninteresting stuff...
primaryStage.setScene(new Scene(new VBox(table, actionButton)));
primaryStage.show();
}
The UI elements should associate them with the object. In your example, you wanted to apply the action on items which are selected(Selected CheckBox).
The Object associated with the table can be like this
public class TableData{
private Boolean selected=Boolean.False;
public void setSelected(Boolean isSelected){
this.isSelected = isSelected;
}
public boolean isSelected(){
return this.selected;
}
}
So In TableCell,
When the checkBox is selected, update 'selected' Boolean value of TableData,by adding a selection action listener to the CheckBox.
then you can iterate through the TableData which you can get it from the TableView, to apply the actions upon Button selection.