add/remove buttons in java SWT app, while app is running. - java

I am writing a application for manipulating a custom sqlite DB.
DB is quite complicated, it has tree like structure.
main
--->operator1
------>name
------>address
--->operator2
------>name
------>address
------>tariffs
---------->name
---------->price
I need to have something like a 'path' in order to easily browse through the table and edit stuff... My data is organized as SWT Table. I have SWT.MouseDoubleClick listener attached to it. I intent to 'step into' my operator data by double clicking on a particular table row. The problem is, how to get back to the 'main view', I need some sort of navigation for that purpose.
My current idea is to create a container and add necessary buttons into it. Similar to
nautilus
Notice, the path is created as consecutive buttons, aligned horizontally:
mentis -> Dropbox -> Photos
The big question is how to do that ;)
I am able to create a button and add it to may container, however this works only when application starts. I don't know how to add buttons to my container when the app is running.
In my main class I have sth like this:
Composite pathBarContainer = new Composite(shell, SWT.BORDER);
pathBarContainer.setSize(shell.getBounds().width, 20);
pathBarContainer.setLayout(new FillLayout(SWT.HORIZONTAL));
GridData gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gridData.horizontalSpan = 3;
pathBarContainer.setLayoutData(gridData);
pathBar = new PathBar(pathBarContainer, shell, contentProvider);
pathBar.getPathBar();
this is my PathBar class:
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
public class PathBar {
Composite parent;
Composite pathBar;
Shell mainShell;
ContentProvider contentProvider;
Button mainButton;
Button nextButton;
public PathBar(Composite parent_in, Shell mainShell_in, ContentProvider cp) {
parent = parent_in;
mainShell = mainShell_in;
contentProvider = cp;
pathBar = new Composite(parent, SWT.BORDER_DOT);
//pathBar.setSize(100, 300);
pathBar.setLayout(new GridLayout(10, true));
mainButton = new Button(pathBar, SWT.PUSH);
mainButton.setText("main");
}
public Composite getPathBar() {
return pathBar;
}
public void addMainButton() {
mainButton = new Button(pathBar, SWT.PUSH);
mainButton.setText("main");
pathBar.redraw();
//parent.redraw();
//mainShell.redraw();
}
public void addButton() {
nextButton = new Button(pathBar, SWT.PUSH);
nextButton.setText("sth");
pathBar.redraw();
parent.redraw();
System.out.println("addButton");
}
}
Methods addMainButton() and addButton() are supposed to be run from eventHandler... attached to my SWT table...
How to solve this ?
Pls help :)

You need to tell it to redo your layout after you add buttons.
pathBar.layout(true);

Related

Add buttons on a treeViewer

I would like to add a button on some rows of my TreeViewer. To do that, I’ve used a method that I saw on a forum and it was working on a TableViewer.
I‘ve implemented my own Label provider on the column where I want the button to be. So I’ve overriden the class update(ViewerCell cell) which calls my method addButton(cell):
(I have simplified the code for a better comprehension)
public class SelectVariableButtonLabelProvider extends ColumnLabelProvider {
#Override
public void update(ViewerCell cell) {
if(...){
addButton(cell);
}
}
private void addButton(ViewerCell cell) {
TreeItem item = (TreeItem) cell.getItem();
Button btn = new Button((Composite) cell.getViewerRow().getControl(), SWT.NONE);
btn.setText(" select variable ");
//action when the button is clicked
btn.addListener(SWT.Selection, new SelectVariableButtonListener(tree,
DataTypeTreeUtils.getTreeNodeDataTypeInstance(cell.getElement()), viewer));
TreeEditor editor = new TreeEditor(item.getParent());
editor.grabHorizontal = true;
editor.grabVertical = true;
// editor.horizontalAlignment = SWT.RIGHT;
editor.minimumWidth = btn.getSize().x + 110;
editor.setEditor(btn, item, cell.getColumnIndex());
editor.layout();
}
}
It’s almost working. Except that the buttons of the column of buttons is duplicated when I want to extend the column.
screenshot of the bug
The left “column of buttons'' : is completely working. The buttons are functional and they adapt themself to the extension of the nodes in the tree Viewer.
The right “column of buttons” : is fixed on the viewer and the buttons are not completely functional. And when I want to extend or not the nodes in the tree, the buttons are not corresponding to their rows anymore. (These are also the buttons in the foreground).
So I would like to not have the right columns which probably appeared because of a bug. I think this could be due to the composite to which the button is initialized :
Button btn = new Button((Composite) cell.getViewerRow().getControl(), SWT.NONE);
Or just because buttons are simply bugging when they are on Tree Viewers ? The same method is working on Table Viewers.
Just in case and if it helps, this is the declaration of the viewer:
viewer = new TreeViewer(treeContainer, SWT.FULL_SELECTION);
viewer.setContentProvider(new TreeNodeTreeContentProvider());
viewer.setLabelProvider(new CustomColumnLabelProvider());
viewer.getTree().setHeaderVisible(true);
viewer.getTree().setLinesVisible(true);
and this is the declaration of the column where I want to add the buttons:
//column with the buttons "select variable"
TreeViewerColumn viewerSetValueColumn = new TreeViewerColumn(viewer, SWT.NONE);
viewerSetValueColumn.getColumn().setWidth(110);
viewerSetValueColumn.setLabelProvider(new SelectVariableButtonLabelProvider(viewer, getAllVariables()));
EDIT:
I would like to have buttons in some rows of a column of my treeViewer. But I want them to be always visible, so I would like to avoid using editing support.
I've used the LabelProvider to do it but it caused a bug (screenshot of the bug).
Does anyone know how to add buttons to a treeViewer using the labelProvider?

adding text box on top of gwt java

I am currently working on a school project where we are creating a GWT web application which uses a GeoChart widget to display information about the servers we have crawled. Simply put, I would wish to create a text box on top of our GeoChart widget which shows an interactive world map that takes up the whole screen right now to input information. I have searched quite extensively but I have been unable to come up with an answer.
Here is the code as follows:
#Override
public void onModuleLoad() {
dataReader = (DataReaderAsync) GWT.create(DataReader.class);
RootLayoutPanel.get().add(getSimpleLayoutPanel());
// Create the API Loader
ChartLoader chartLoader = new ChartLoader(ChartPackage.CORECHART);
chartLoader.loadApi(new Runnable() {
#Override
public void run() {
getSimpleLayoutPanel().setWidget(getGeoChart());
drawGeoChart();
}
});
}
As GeoChart is a widget, it is wrapped under(i am not sure if this is the right word) a SimpleLayoutPanel right now which will display it into a full screen. As stated above, I would wish to include text above the geoChart. From my understanding, I would need to create another widget containing my text and add both the GeoChart widget and the text box widget into it. What would be the best way to go about doing this?
I believe DialogBox could solve your problem. People usually program the DialogBox in a way that it only pops up into display when certain event is triggered and disappears after user finishes some operation. In your particular case, you can simply make the DialogBox shows up from the beginning and never disappears. And the best part of it: you don't need to add the DialogBox widget to the geoChart widget. Calling dialogBox.center() or dialogBox.show() will do the magic for you.
Here is the sample code.
#Override
public void onModuleLoad() {
dataReader = (DataReaderAsync) GWT.create(DataReader.class);
RootLayoutPanel.get().add(getSimpleLayoutPanel());
// Create the API Loader
ChartLoader chartLoader = new ChartLoader(ChartPackage.CORECHART);
chartLoader.loadApi(new Runnable() {
#Override
public void run() {
getSimpleLayoutPanel().setWidget(getGeoChart());
drawGeoChart();
}
});
// NOTE: the first argument 'false' makes sure that this dialog box
// will not disappear when user clicks outside of it
// NOTE: the second argument 'false' makes sure that mouse and keyboard
// events outside of the dialog box will NOT be ignored
DialogBox dialogBox = new DialogBox(false, false);
DialogBox.setText("Demo");
HorizontalPanel panel = new HorizontalPanel();
panel.setSpacing(5);
InlineLabel labelOfTextBox = new InlineLabel("Label");
TextBox textBox = new TextBox();
panel.add(labelOfTextBox);
panel.add(textBox);
dialogBox.setWidget(panel);
// show up in the center
dialogBox.center();
}
Dear all thanks for answering my question. To rectify this problem, I have made use of the custom widget API within GWT(known as Composite). Here's the code as below:
private static class CombinedWidget extends Composite {
public CombinedWidget() {
// place the check above the text box using a vertical panel.
VerticalPanel panel = new VerticalPanel();
DockLayoutPanel dPanel = new DockLayoutPanel(Unit.EM);
panel.setSpacing(13);
panel.add(nameProject);
nameProject.setStyleName("gwt-Group-Label");
panel.add(className);
panel.add(nameKs);
panel.add(nameEsmond);
panel.add(nameBowen);
panel.add(nameAaron);
dPanel.addWest(panel, 13);
dPanel.add(getGeoChart());
// all composites must call initWidget() in their constructors.
initWidget(dPanel);
setWidth("100%");
}
Actually I sort of changed from the original idea. Instead of putting it on the very top, I attached the labels into a VerticalPanel and then created a CombinedWidget(custom widget) which adds both a VerticalPanel and DockLayoutPanel together. I then added the VerticalPanel(containing all the labels) and the GeoChart into the DockLayoutPanel.
This solved my problem of displaying both the labels and the GeoChart on the same page(as originally i added it into a VerticalPanel but it would not work as the app would not read the GeoChart due to the VerticalPanel being overlayed on top of the GeoChart).
If you guys want a picture of my app to visualise, please say so!

Updating JTabbedPane when new tab is added

I'm working on a project, and have run into a little bit of a logic error, hopefully one of you can clear this up.
I'm building an application that will display a SQL database (among other things). Currently, the way I have things set up, I have a JTabbedPane inside a Container (BorderLayout.CENTER) not that this is really pertinent information.
Anywho, I would like to add a tab once the user has connected to a database (and eventually selected which 'table' to see. For now however, there is only one table to be displayed.
So, when the user hits 'Connect', ideally the connection will be successful, at which point in time a JTable is populated with the database information.
Once this table is initialized and ready to go, add it to a new JPanel, and add that panel to the JTabbedPane.
This is where the error comes in. I 'believe' my logic thus far is correct, and I don't get any compiler/runtime errors, the new tab just isn't shown (and if I click where it should be) nothing happens.
Below is some of my code, if anything needs clarified please don't hesitate to ask!
This is the Table_Builder Class code (I will clean it up once it is working properly!)
public class Table_Builder extends Framework
{
private DefaultTableModel updated_table_model;
private JTable updated_table;
private JScrollPane table;
public Table_Builder()
{
// no implemention needed
}
public Table_Builder(Vector rows, Vector columns)
{
updated_table_model = new DefaultTableModel(rows, columns);
updated_table = new JTable(updated_table_model);
updated_table.setCellSelectionEnabled(true);
updated_table.setFillsViewportHeight(false);
table = new JScrollPane(updated_table);
JPanel tab2 = new JPanel();
tab2.add(table);
tab2.setVisible(true);
center.add("Table Viewer", tab2);
// I'm thinking some sort of listener needs to be active, so it knows I'm adding a new
// tab, but I'm not sure how this actually works.
center.addPropertyChangeListener("foregroud", null);
center.repaint();
// center has already been added to container so i don't think that needs to be done again?
}
Framework
protected void center_panel()
{
JPanel tab1 = new JPanel();
tab1.add(//emitted);
center.setPreferredSize(new Dimension(1340, 950));
center.setBackground(new Color(90, 90, 90));
center.addTab("Tab1", tab1);
container.add(center, BorderLayout.CENTER);
}
Best Regards,
Mike
UPDATE:
Framework has these variables I am using to build the 'Frame'
Framework is a borderlayout (east, west, north, south, center)
protected JTabbedPane center // this is the center panel
protected Container container // this will house all panels to be added
As seen above, I am currently adding tabs by
1.) creating a new JPanel
2.) adding (whatever needs to be displayed) to the jpanel
3.) adding that jpanel to the JTabbedPane
this is done by
center.addTab("Tab name here", panel to be added);
The javadoc for this says
center.addTab("String title", Component component);
This works as intended, the problem I am encountering, is that this is done prior to server connection. After the user connects to the server, I would like to add a new tab, which is being done from Table_Builder, which inherits from Framework (which is why center and container are protected and not private).
Your code for adding a tab in the constructor is the following:
JPanel tab2 = new JPanel();
tab2.add(table);
tab2.setVisible(true);
center.add("Table Viewer", tab2);
// I'm thinking some sort of listener needs to be active, so it knows I'm adding a new
// tab, but I'm not sure how this actually works.
center.addPropertyChangeListener("foregroud", null);
center.repaint();
There are 2 errors and a lot of unnecessary lines. The errors are:
center.add("Table Viewer", tab2); is using the add function of the Container class. When you wanted to use center.addTab("Table Viewer", tab2);.
Just to clear up what #peeskillet was pointing out, there is not a "foregroud" property, nor a "forground" (as per your comment), but a "foreground" property.
Now what you need to do is just the following:
JPanel tab2 = buildTableViewerTab();
center.addTab("Table Viewer", tab2);
Where buildTableViewerTab() (returning a JPanel) is the code necessary to create the JPanel that you desire. Just create the component and add it to the tabbedPane properly.
To show how this code works here is a simple executable application demonstrating this functionality. Again, what #peeskillet was asking you in his second comment is to do this same example but in your own way and with your code demonstrating the errors you were encountering. Although doing this you probably would have found them.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
public class AddTabsExample
{
public static final void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new AddTabsExample();
}
});
}
public AddTabsExample()
{
JFrame frame = new JFrame("Tab adder frame");
final JTabbedPane tabbedPane = new JTabbedPane();
frame.add(tabbedPane);
JButton addButton = new JButton("Add tab");
addButton.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent arg0)
{
JPanel newTabComponent = new JPanel();
int tabCount = tabbedPane.getTabCount();
newTabComponent.add(new JLabel("I'm tab " + tabCount));
tabbedPane.addTab("Tab " +tabCount, newTabComponent);
}
});
frame.add(addButton, BorderLayout.SOUTH);
addButton.doClick(); //add the first tab
frame.setSize(800, 300);//frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
}
Execution result:
call revalidate() on your center, then repaint.

How to stop multiple selections with multiple StyledText widgets

I'm working on an application using SWT in Java, and I've encountered some wierd behavior from multiple StyledText widgets. It's fairly consistent: if there are more than one StyledText widget displayed in the window/view/editor at once, you can select whatever you want from each one individually, at the same time. In the screenshot, there are 4 separate widgets with 4 separate selections.
My expectation is that if I start selecting from one widget, whatever other widget that might already have a selection should then lose it, similarly to the behavior you would expect from a web browser; only one selection at a time.
I'd like to fix this problem, but I hope to avoid needing to make some manager that listens to every widget and turns off selections when a new one is made. Is there a better way to do this? (Also why is this happening in the first place?)
Why is it happening?
To best of my knowledge the behavior of a widget depends on its implementation i.e. whether it is a
native widget (like org.eclipse.swt.widgets.Text) or
custom widget (like org.eclipse.swt.custom.StyledText)
The difference lies in the handling of the mouse down or mouse up events.
For example,
In case of org.eclipse.swt.widgets.Text the left mouse down eventually translates to OS.SendMessage (hwnd, OS.WM_LBUTTONUP, wParam, lParam);
Whereas
org.eclipse.swt.custom.StyledText uses a mouse event handler and extra processing in the handleMouseDown(Event event) method. Most of the functionality or UI is done using custom draw/redraw/validate/invalidate/update methods.
To put it in a very crude win32 sdk way:
There are some controls provided by windows/win32 GDI
And, some are custom user drawn controls
See the below SWT code which uses text, styledtext, browser etc for testing. Also Note the browser control is not exactly a win32 control it is a wrapper control around Internet Explorer activex or mozilla's gecko engine, therefore its behavior is same as that of styled text.
Any possible solution ?
Well I can only think of borrowing SWT's styledtext code and then making a version which suits me. Or
As you have already mentioned, use some listener to reset all the other unfocused widgets (which even in my opinion is not a very clean solution).
Test Code & Output
Code:
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
public class StyledTextTest {
private static Display display;
public static void main(String[] args)
{
display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new GridLayout(2,true));
shell.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
createStyledText(shell);
createStyledText(shell);
createText(shell);
createText(shell);
createCombo(shell);
createCombo(shell);
createCustomCombo(shell);
createCustomCombo(shell);
createBrowser(shell);
createBrowser(shell);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
private static void createCustomCombo(Composite parent)
{
new Label(parent, SWT.NONE).setText("Custom Combo");
CCombo c = new CCombo(parent, SWT.DROP_DOWN);
c.setItems(new String[] {"test best", "best rest", "rest test"});
c.select(0);
c.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
}
private static void createCombo(Composite parent)
{
new Label(parent, SWT.NONE).setText("Combo");
Combo c = new Combo(parent, SWT.DROP_DOWN);
c.setItems(new String[] {"test best", "best rest", "rest test"});
c.select(0);
c.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
}
static void createBrowser(Composite parent)
{
new Label(parent, SWT.NONE).setText("Browser");
Browser browser = new Browser(parent, SWT.NONE);
browser.setText("<div>This is a test !!</div>");
browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
}
static void createText(Composite parent) {
new Label(parent, SWT.NONE).setText("Text");
final Text text = new Text(parent, SWT.BORDER);
text.setText("0123456789 ABCDEFGHIJKLM NOPQRSTUVWXYZ");
}
static void createStyledText(Composite parent)
{
new Label(parent, SWT.NONE).setText("Styled Text");
StyledText text = new StyledText (parent, SWT.BORDER|SWT.SINGLE);
text.setText("0123456789 ABCDEFGHIJKLM NOPQRSTUVWXYZ");
// make 0123456789 appear bold
StyleRange style1 = new StyleRange();
style1.start = 0;
style1.length = 10;
style1.fontStyle = SWT.BOLD;
text.setStyleRange(style1);
// make ABCDEFGHIJKLM have a red font
StyleRange style2 = new StyleRange();
style2.start = 11;
style2.length = 13;
style2.foreground = display.getSystemColor(SWT.COLOR_RED);
text.setStyleRange(style2);
// make NOPQRSTUVWXYZ have a blue background
StyleRange style3 = new StyleRange();
style3.start = 25;
style3.length = 13;
style3.fontStyle = SWT.BOLD | SWT.ITALIC;
text.setStyleRange(style3);
}
}

What's wrong with JSplitPanel (or JTabbedPane)?

I have two panels that i wish to display to the user. I decided to add them to a JTabbedPane. I also want to allow the user to have a side by side view of them both at the same time. So I added the two panels to my JTabbedPane and then i created a JSplitPanel as such :
tabs.addTab("Align Image Points", imageControlPanel);
tabs.addTab("Align Map Points", mapControlPanel);
JSplitPane splitPane = new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT, true, imageControlPanel,
mapControlPanel);
tabs.addTab("Side by side view", splitPane);
The resulting JTabbedPane only has one tab! When i remove the JSplitPane everything works ok. Two tabs. I then tried simplifying the problem to post here and i came up with this :
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
public class Test {
public static void main(String[] args) {
JFrame f = new JFrame("Test");
JButton b1 = new JButton("First");
JButton b2 = new JButton("Second");
JSplitPane s = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,true,b1,b2);
JTabbedPane tabs = new JTabbedPane();
tabs.addTab("First", b1);
tabs.addTab("Second", b2);
tabs.addTab("Both", s);
f.getContentPane().add(tabs);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
}
This gave me another problem! It displays 3 tabs but the third tab's split pane displays no buttons!
So my question is What is wrong with JSplitPanel? You can't have it display objects that are already displayed in another tab? It makes no sense. Please help me.
(Note: i don't want to duplicate the components that i am about to display as i want them to be the same reference)
Swing UIs are hierarchical and you can only add a component to the hierarchy once. If you add a component to more than one container you'll get unpredictable results. You are correct to not want to duplicate the components, but you'll need a listener on the JTabbedPane to add and remove each component from the tab or the JSplitView as the selection of the tabs changes.
tabs.addChangeListener( new ChangeListener() {
public void stateChanged(ChangeEvent e) {
// Reorganise the display based on the current tab selection.
}
}
I had the same problem that you had. what I had resolved for this issue, I made the each GUI as MVC pattern(Model-view-controller) that controller knows how to iterative with gui components.
I created a new instance of GUIs(View) on each Tab;however, I injected the same instance of controller for that GUI as constructor parameter since the controller knows how to handle GUI flow and behaviors.
for example,
GUIView1Controller controller1 = new GUIView1Controller();
GUIView2Controller controller2 = new GUIView2Controller();
// Add new instance GUI ; however , use the same instance of controller
JSplitPane s = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,true,
new GUIView1(controller1), new GUIView2(controller2));
JTabbedPane tabs = new JTabbedPane();
tabs.addTab("First", new GUIView1(controller1));
tabs.addTab("Second", new GUIView2(controller2));
tabs.addTab("Both", s );
GUIView1 and GUIView2 will register all GUI listeners to the controller, so the controller will be notified and take an action for the listeners. whatever the GUIView1 on "First" tab is changed, the GUIView1 on "Both" tab also is updated as the same behaviors of the GUIView1 on "First" tab.
The drawback was you have to create a new instance of the GUIView on the tab and JSplitPane; however, the controller can control and share all gui events and behaviors.
I hope it helps.
Tiger.

Categories