I have two Jlists of vectors filled with data extracted from a mysql db. What I want is when the user select an item (menu) from Jlist1 (which I called menuList) Jlist2 (which I called productList) must display the products of that menu and other things, such as the ability to insert a new product in THAT menu or in a new menu just created.
I've accomplished this task in a way which I think is weak, by using some boolean variables which tells if the user is inserting a product in an existing menu or in a newly created one. Please, can you suggest me a better solution (if exists)? Here is an extract of the most significant part of the code, this is the method which saves a new product in the db:
private void bAddProdActionPerformed(java.awt.event.ActionEvent evt) {
//If new menu is saved, get the new menu's Id
if (newMenuIsSaved == true) {
Product newProduct = new Product();
newMenuId = DBConnection.getNewMenuId();
newProduct.setMenuId(newMenuId);
newProduct.setProductName(productName.getText());
if (checkPPriceValidity(productPrice.getText(), newProduct)) {
int result = DBConnection.insertProduct(newProduct);
if (result == 1) {
reloadProductList();
disableProductButtons();
}
}
} else {
Product newProduct = new Product();
//If a new menu wasn't saved, get the menuId from the selected one (from menuList):
Menu selectedMenu = (Menu) menuList.getSelectedValue();
newProduct.setMenuId(selectedMenu.getMenuId());
newProduct.setProductName(productName.getText());
if (checkPPriceValidity(productPrice.getText(), newProduct)) {
int result = DBConnection.insertProduct(newProduct);
if (result == 1) {
reloadProductList();
newMenuIsSaved = false;
disableProductButtons();
bNewProduct.setEnabled(true);
}
}
}
}
And here is the method reloadProductList():
private void reloadProductList() {
modelProductList.clear();
if (newMenuIsSaved) {
Vector<Product> productVoices = DBConnection.fillProductList(newMenuId);
for (int i = 0; i < productVoices.size(); i++) {
modelProductList.addElement((Product) productVoices.get(i));
}
} else {
Vector<Product> productVoices = DBConnection.fillProductList(selectedMenuId);
for (int i = 0; i < productVoices.size(); i++) {
modelProductList.addElement((Product) productVoices.get(i));
}
}
}
Thank you very much.
One way is to add a ListSelectionListener to menuList and have the handler set the other list's model to display the details for the selected row.
Related
I use:
Java 10 SE
Java Swing
Eclipse IDE
I have JTable, the contents gets loaded at runtime dynamically. It has some JComboBoxes. If I select the JComboBox, and then attempt to reload the table, the JComboBox appears visible at the time when the table loading is in progress.
Besides that, if the JComboBox's contents gets updated (elsewhere in different table, when the combo supposed to reflect that new contents), that new contents does not get visible staright away after loading the JTable dynamically.
The snap-shot sample of the app:
That's, the table being loaded at runtime up, and in the middle you have vsisble JComboBox persistent from the previous selection.
How to:
Get rid off that persistent JComboBox
Make the data visible instantly, upon update under the combo, once you load the table dynamically
I have the public final class TableColumnEditor extends DefaultCellEditor{
which returns the JComboBox on a specific column:
else if(row == ROW_2_DEVS_WORK_WEEKEND) {
ProjectMetrics metrics = new ProjectMetrics();
JComboBox<String> combo = new JComboBox<>();
combo.setBackground(Color.WHITE);
for(String devs : metrics.identifyDevsThatWorkAtWeekend()) {
combo.addItem(devs);
}
return combo;
}
I have the public final class TableColumnRenderer extends DefaultTableCellRenderer{
which makes sure that the view displays the JComboBox under that specific column:
else if(row == ROW_2_DEVS_WORK_WEEKEND) {
ProjectMetrics metrics = new ProjectMetrics();
JComboBox<String> combo = new JComboBox<>();
combo.setBackground(Color.WHITE);
for(String devs : metrics.identifyDevsThatWorkAtWeekend()) {
combo.addItem(devs);
break;
}
return combo;
}
The table gets loaded dynamically right here (non-essential things removed):
public static void reloadTableDynamically(JTable metricsTable){
DefaultTableModel model = (DefaultTableModel)metricsTable.getModel();
if(projectData.isEmpty()) {
metricsTable.clearSelection();
int rowCount = model.getRowCount();
for(int item = (rowCount - 1); item >= 0; item--) {
model.removeRow(item);//clears previous rows
}
metricsTable.repaint();
return;
}
model.getDataVector().clear();
int rowCount = constantRows + ((devsTask.size() == 0) ? 1 : devsTask.size());
try {
new Thread(()-> {
int lastRowID = 0;
int devsTaskID = 0;
for(int item = 0; item < rowCount; item++) {
Object[] input = null;
if(item == 0) {
input = new Object[] {"", metrics.getProjectDateRange(), "" };
}//similar branches removed
else {
devsTaskID++;
input = new Object[] {"", devsTask.get(devsTaskID).getDeveloper(), ""};
}
model.addRow(input);
metricsTable.scrollRectToVisible(new java.awt.Rectangle(metricsTable.getCellRect(lastRowID++, 0, true)));
metricsTable.repaint();
try {
Thread.sleep(Config.getInstance().getReloadInOutTable());
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
metricsTable.scrollRectToVisible(new java.awt.Rectangle(metricsTable.getCellRect(projectData.size() - 1, 0, true)));
metricsTable.repaint();//so that to reach the last row
}).start();
}
catch(Exception e) {
}
}
What do you think?
Well, I figured out how to overcome this problem.
Firstly, the JComboBox gets updated on EDT(Event Despatch Thread).
/**
* #param combo The JComboBox ref.
* #param toDisplay The value to add to it
*/
public static void updateComboBoxOnEventDespatchThread(JComboBox<String> combo, String toDisplay) {
Runnable doComboUpdate = new Runnable() {
public void run() {
combo.addItem(toDisplay);
}
};
SwingUtilities.invokeLater(doComboUpdate);
}
Under the JTable column editor:
else if(row == ROW_2_DEVS_WORK_WEEKEND) {
ProjectMetrics metrics = new ProjectMetrics();
JComboBox<String> combo = new JComboBox<>();
combo.setBackground(Color.WHITE);
Runnable doComboInsert = new Runnable() {
public void run() {
int id = 0;
for(String devs : metrics.identifyDevsThatWorkAtWeekend()) {
UIutils.updateComboBoxOnEventDespatchThread(combo, "("+ ++id +") " + devs);
}
}
};
SwingUtilities.invokeLater(doComboInsert);
return combo;
}
But the main fix, without which both issues do not go away, is following.
That is, I noticed that in order for data to appear under the table instantly, firstly, you need to select any other unrelated table's cell.
That is, the Java thread, which loads the JTable at runtime, does need to have this:
if(model.getRowCount() > 0) {
metricsTable.selectAll();
}
That's probably a hack, but it works for me!
I've working on a GUI that the user can browse text files from the SYSTEM and then when the user press "Start" button the program reading the text file/s, create lists from its data and supposed to add it to TableView. I'm stuck on inserting the data from the lists to the table. I've created the columns names by file names and added it to table:
tblConfigurationSystemColumns.add("Parameter Name");
tblSystemColumn.stream().map((str) -> str.split("PCM")).forEachOrdered((a) -> {
tblConfigurationSystemColumns.add(a[0].trim());
});
for (int i = 0; i < tblConfigurationSystemColumns.size(); i++) {
TableColumn col = new TableColumn(tblConfigurationSystemColumns.get(i));
tableConfigurationSystem.getColumns().addAll(col);
}
The column names coming from the list tblConfigurationSystemColumns. This list may be changed from each use of the GUI by number of file you browse from the system. (for now let think that we have 2 strings inside: "column1","column2")
I need to add items to column1 from the list SysParameter , and to column2 from list SysValues.
How can I add values from each list to each column by rows?
If you need any more code please tell me (just let you know, the only code that I have it the list creating from the files).
EDIT:
This is what I got after the column building.
after this I need to get the "Parameter" and the "Value" for each column(as you can see).
I've made a list that get the "Parameter" from the text file, and another list that get the "Value" from the text file.
how can I put each list to it's column?
This is the code that build this lists:
boolean inCESystem = false;
for (final String line : list) {
if (line.contains("CE-") && !(line.contains("CE-system-equipment-pm") || line.contains("inbound") || line.contains("outbound"))) {
inCESystem = true;
}
else if (line.trim().isEmpty()) {
inCESystem = false;
}
else if (inCESystem) {
CE_System.add(line);
}
}
boolean inCESystemInbound = false;
for (final String line : list) {
if (line.contains("CE-") && (line.contains("inbound")) ) {
inCESystemInbound = true;
}
else if (line.trim().isEmpty()) {
inCESystemInbound = false;
}
else if (inCESystemInbound) {
CE_System.add("inbound_loadlock - "+line.trim());
}
}
boolean inCESystemOutbound = false;
for (final String line : list) {
if (line.contains("CE-") && (line.contains("outbound")) ) {
inCESystemOutbound = true;
}
else if (line.trim().isEmpty()) {
inCESystemOutbound = false;
}
else if (inCESystemOutbound) {
CE_System.add("outbound_loadlock - "+line.trim());
}
}
/*
* Check the CE list to split each object per parameter and value to different lists
*/
CE_System.stream().map((str) -> str.split(",")).map((a) -> {
CE_SystemParameter.add(a[0].trim()); //Parameters
return a;
}).forEachOrdered((a) -> {
if(a.length > 1) {
CE_System_Value.add(a[1].trim()); //Values
} else {
CE_System_Value.add(""); //add blank if parameter doesn't have value
}
});
EDIT 2: Text file example
CE-system:
No features to set for this item...
CE-system-componentmanager:
Bootstrap Parallelism ,Parallel Bootstrapping
CE-system-components:
No features to set for this item...
CE-system-components-accessmanager:
Access control enable ,disabled
Access policy prototyping ,enabled
Access user group ,enabled
Implicit roles access policy ,disabled
World access policy ,disabled
CE-system-components-eqlog:
EquipmentLog Enable ,false
Line that contains "CE-" its just title to know that is should be in the "Configuration" Tab.
each line inside is the "parameter" and the value(after the comma).
EDIT 3: The table should look like this example (This example is from my code in Java SWT)
Thank you very much guys.
The data for a TableView is held in the ObservableList of the items property. A TableView is designed to hold a list of POJOs that contain various properties. Each of the properties will correspond to a TableColumn who obtains the value of these properties using a Callback.
Since you are browsing text files let's say you define a POJO like so:
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.property.SimpleStringProperty;
public class TextFile {
private final StringProperty name = new SimpleStringProperty(this, "name");
public final void setName(String name) { this.name.set(name); }
public final String getName() { return name.get(); }
public final StringProperty nameProperty() { return name; }
private final LongProperty size = new SimpleLongProperty(this, "size");
public final void setSize(long size) { this.size.set(size); }
public final long getSize() { return size.get(); }
public final LongProperty sizeProperty() { return size; }
public TextFile() {}
public TextFile(String name, long size) {
setName(name);
setSize(size);
}
}
From this you'll want a TableView of TextFiles that has a TableColumn for name and a TableColumn for size. To tell a TableColumn how to obtain the correct value you set the cellValueFactory with the appropriate Callback. This Callback accepts a TableColumn.CellDataFeatures and returns an ObservableValue. If the ObservableValue changes the TableColumn will update the item of the corresponding TableCell.
ObservableList<TextFile> files = ...;
TableView<TextFile> table = new TableView<>();
table.setItems(files);
TableColumn<TextFile, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(features -> features.getValue().nameProperty());
table.getColumns().add(nameCol);
TableColumn<TextFile, Number> sizeCol = new TableColumn<>("Size");
sizeCol.setCellValueFactory(features -> features.getValue().sizeProperty());
table.getColumns().add(sizeCol);
Note that each TextFile in files is a row in the TableView.
I guess you are looking for something like that:
TableColumn< YourObject, String> col = new TableColumn<>();
col.setCellValueFactory(new PropertyValueFactory("nameOfThePropertyYouWantToDisplay");
TableColumn< YourObject, String> col2 ....
TableView < YourObject> table = new TableView();
table.setItems(observableListOfYourObject);
Look here for a detailed description: https://docs.oracle.com/javafx/2/ui_controls/table-view.htm
Choose the item type accordingly. Your description indicates the following properties:
The table data is not edited once it's loaded.
You cannot hardcode the number of files.
Therefore a suitable choice of data structure would be List<String>. Each list contains one element for every column.
public void initializeTableColumns(TableView<List<String>> table, File file, File... files) {
List<String> fileItems = readFile(file);
TableColumn<List<String>, String> column = new TableColumn<>(file.getName());
column.setCellValueFactory(cd -> new SimpleStringProperty(cd.getValue().get(0));
table.getColumns().add(column);
for (String s : fileItems) {
List<String> row = new ArrayList<>(files.length + 1);
row.add(s);
table.getItems().add(row);
}
for (int fileIndex = 0; fileIndex < files.length; fileIndex++) {
File f = files[fileIndex];
fileItems = readFile(f);
int itemCount = Math.min(fileItems.size(), table.getItems().size());
// add items from file
for (int i = 0; i < itemCount; i++) {
table.getItems().get(i).add(fileItems.get(i));
}
if (itemCount <= table.getItems.size()) {
// fill items that may be missing
for (int i = itemCount; i < table.getItems().size(); i++) {
table.getItems().get(i).add(null);
}
} else {
// add missing rows
for (int i = table.getItems.size(); i < itemCount; i++) {
List<String> row = new ArrayList<>(files.length + 1);
for (int j = 0; j <= fileIndex; j++) {
row.add(null);
}
row.add(fileItems.get(i));
table.getItems().add(row);
}
}
final index = fileIndex + 1;
column = new TableColumn<>(f.getName());
column.setTableColumn(cd -> new SimpleStringProperty(cd.getValue().get(index)));
table.getColumns().add(column);
}
}
I am making a program that makes use of the java SWT's Tree structure. This is tree is build upon data that is read from external files and user input.
To this tree I've also attached a right-click menu.
The main problem is that this tree's menu is spanned all over his items, and I would want it to be shown only on some level.
I also want to have different menus or menu options for different tree levels.
All the tree's creation is in a function:
public void createTree(final Composite container){
try{
for(Object element : container.getChildren()){
if(element instanceof Tree){
((Tree) element).dispose();
}
}
// function to verify if the tree is still in my container composite, and if so, I'll dispose the tree, because I am going to construct a new one
final Tree variantTree = new Tree(container, SWT.CHECK | SWT.V_SCROLL | SWT.H_SCROLL);//creating new tree object
variantTree.setBounds(10, 65, 400, 400);//dimensions
//variantTree.setData("variant"); -- wanted to abuse this method to make a difference between the tree's level,
//but this attribute is shared across all the tree, and I cannot make a distinction between my levels
if(..){
//here I am using the data saved in a tree data structure to populate my SWT Tree
}
variantTree.addListener(SWT.Selection, new Listener(){
//this is a listener used for the tree's checkboxes, using them to select different items from my tree
});
final Menu treeMenu = new Menu(variantTree);
variantTree.setMenu(treeMenu);//adding the right click menu
treeMenu.addMenuListener(new MenuAdapter(){
//the menu's items are declared here: the buttons that I want to have and the their listeners
//i am disposing the menu's items if there are any
MenuItem newItem = new MenuItem(treeMenu, SWT.NONE);
newItem.setText("new button... ");
newItem.addListener(SWT.Selection, new Listener(){
#Override
public void handleEvent(Event event) {
String item = variantTree.getSelection()[0].getText();//can I use this to make different menus for different tree levels?
for(Node element : TestControl.getTree().getChildren()){//getting the tree data structure that I have saved in a static way via a class
if(element.getName().equals(item)){
//opening a file dialog here and doing some operations
}
}
});
});
//this is where I want to make the distinction between the levels of the tree, such as that I will have this menu show up only for the root of my tree, or the first level of the tree, since I will have multiple trees
variantTree.addMenuDetectListener(new MenuDetectListener(){
#Override
public void menuDetected(MenuDetectEvent event) {
// TODO Auto-generated method stub
if(/*condition*/){ //I messed around with the tree's methods and attributes, nothing so far has come in handy to help me make a distinction between my tree's levels
event.doit = true;
//System.out.println(variantTree.getSelectionCount()); --I don't know if this can help me either, I tried to see if this will return differently for every level of the tree, but the return values is always 0
//find some kind of handle
}else{
event.doit = false;
}
}
});
Also, I want to have another menu being displayed for the second level of the tree, could I be using the String item = variantTree.getSelection()[0].getText();, modified in a sort of way to match my needs? Let's say, String item = variantTree.getSelection()[1].getText(); for the second level?
My SWT Tree will have a maximum depth of 3 for each root, and it looks something like this:
root1 - want the menu only for this item
|---child - level 1
|------child - level 2
root2 - want the menu only for this item
|---child - level 1
|-------child - level 2
Thank you.
No need to compare TreeItem texts, just check the level of the item by e.g. checking if it's got a parent.
public static void main(String[] args)
{
Display display = Display.getDefault();
final Shell shell = new Shell(display);
shell.setText("StackOverflow");
shell.setLayout(new FillLayout());
final Tree tree = new Tree(shell, SWT.NONE);
for (int i = 0; i < 10; i++)
{
TreeItem item = new TreeItem(tree, SWT.NONE);
item.setText("Parent " + i);
for (int j = 0; j < 3; j++)
{
TreeItem child = new TreeItem(item, SWT.NONE);
child.setText("Child " + i + " " + j);
for (int k = 0; k < 3; k++)
{
TreeItem grandChild = new TreeItem(child, SWT.NONE);
grandChild.setText("Child " + i + " " + j + " " + k);
}
}
}
final Menu menu = new Menu(tree);
tree.setMenu(menu);
tree.addMenuDetectListener(new MenuDetectListener()
{
#Override
public void menuDetected(MenuDetectEvent e)
{
TreeItem treeItem = tree.getSelection()[0];
e.doit = getLevelOfItem(treeItem) < 2;
}
});
menu.addMenuListener(new MenuAdapter()
{
public void menuShown(MenuEvent e)
{
TreeItem item = tree.getSelection()[0];
setMenu(menu, item);
}
});
shell.pack();
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
private static void setMenu(Menu menu, TreeItem item)
{
int level = getLevelOfItem(item);
MenuItem[] items = menu.getItems();
for (MenuItem i : items)
{
i.dispose();
}
switch (level)
{
case 0:
for(int i = 0; i < 2; i++)
{
MenuItem newItem = new MenuItem(menu, SWT.NONE);
newItem.setText("Menu item " + i);
}
break;
case 1:
for(int i = 0; i < 4; i++)
{
MenuItem newItem = new MenuItem(menu, SWT.NONE);
newItem.setText("Menu item " + i);
}
break;
}
}
private static int getLevelOfItem(TreeItem item)
{
int counter = 0;
while(item.getParentItem() != null)
{
item = item.getParentItem();
counter++;
}
return counter;
}
This will show a different menu for level 0 and 1 and no menu for level 2.
I have solved my problem in the following way.
If you actually see my code, there are the following lines:
for(Node element : TestControl.getTree().getChildren()){//getting the tree data structure that I have saved in a static way via a class
if(element.getName().equals(item)){
//opening a file dialog here and doing some operations
}
}
I am getting the first level of my tree data structure, and then I'm verifying if the root names of my SWT tree are equal to the roots of the tree data structure.
Using the same method, this is what will result in the end for the SWT Tree's menu listener:
variantTree.addMenuDetectListener(new MenuDetectListener(){
#Override
public void menuDetected(MenuDetectEvent event) {
// TODO Auto-generated method stub
for(Node element : TestControl.getTree().getChildren()){
if(variantTree.getSelection()[0].getText().equals(element.getName())){
event.doit = true;
break;//need this, because it will propagate only to the last item, or it will have a strange behaviour
}else{
event.doit = false;
}
}
}
});
where element is a node from my tree data structure.
Hope it helps anyone that will have the same problem.
I've been struggling for hours with this method which has a strange behavior.
Scenario: two jlists, menuList and productList filled with data from a mysql db, the first contains the "categories" and the second the products of that category. Both of them are populated with a Vector of objects. The problem is when I perform a search of a products it works fine in all circumstances except immediately after I save a new menu (category). In this case, the index of the menuList is successfully moved to the right category in which the products I was searching is in, but productList remains blank.
This is the code of the method which makes the search.
private void bFindProductActionPerformed(java.awt.event.ActionEvent evt) {
Product product = new Product();
product.setProductName(searchedProduct.getText());
bNewProduct.setEnabled(false);
Vector<Product> productVoices = DBConnection.searchProduct(product);
if (!productVoices.isEmpty()) {
modelProductList.clear();
disableProductButtons();
menuName.setText("");
productName.setText("");
int idMenu = 0;
for (int i = 0; i < productVoices.size(); i++) {
Product current = (Product) productVoices.get(i);
modelProductList.addElement(current);
idMenu = current.getMenuId();
}
productList.setModel(modelProductList);
for (int i = 0; i < modelMenuList.getSize(); i++) {
Menu current = (Menu) modelMenuList.getElementAt(i);
if (idMenu == current.getMenuId()) {
int currentId = modelMenuList.indexOf(current);
menuList.setSelectedIndex(currentId);
menuList.requestFocus();
}
}
} else {
JOptionPane.showMessageDialog(null, "No product found", "", JOptionPane.WARNING_MESSAGE);
searchedProduct.setText("Search...");
}
}
And this is the code of the methos which saves a new menu (or updates an existing one):
private void bSaveMenuActionPerformed(java.awt.event.ActionEvent evt) {
//If there's a selected menu updates
if (!menuList.isSelectionEmpty()) {
Menu selectedMenu = (Menu) menuList.getSelectedValue();
listIndex = menuList.getSelectedIndex();
selectedMenu.setMenuName(menuName.getText());
int result = DBConnection.updateMenu(selectedMenu);
if (result == 1) {
reloadMenuList(); //this re-fill the menuList
menuList.setSelectedIndex(listIndex);
menuList.setEnabled(true);
bFindProduct.setEnabled(true);
bNewMenu.setEnabled(true);
bNewProduct.setEnabled(false);
}
} else { //If there isn't a selected menu, creates a new menu
Menu newMenu = new Menu();
newMenu.setMenuName(menuName.getText());
int result = DBConnection.insertMenu(newMenu);
if (result == 1) {
reloadMenuList();
newMenuIsSaved = true;
menuList.setEnabled(true);
bFindProduct.setEnabled(true);
}
}
}
Thank you.
I want to add multiple elements to JList using JComboBox. When user select an item from the JComboBox, it should add to JList. If the item already exists in the List message should pop up to notify that. How do I do this?
private void cmbBagSizeItemStateChanged(java.awt.event.ItemEvent evt) {
DefaultListModel listModel = new DefaultListModel();
lstBagSize.setModel(listModel);
if ((evt.getStateChange() == ItemEvent.SELECTED)) {
if (listModel.getSize() != 0) {
for (int i = 0; i < listModel.getSize(); i++) {
listModel.addElement(cmbBagSize.getModel().getSelectedItem());
break;
}
} else {
listModel.addElement(cmbBagSize.getModel().getSelectedItem());
}
}
}
I managed to find the solution to above issue. I used method called contains to check where any duplicates exists.
DefaultListModel listModel = new DefaultListModel();
if (listModel.contains(this.cmbBagSize.getSelectedItem())) {
JOptionPane.showMessageDialog(null, "Duplicate");
} else {
listModel.addElement(this.cmbBagSize.getSelectedItem());
this.lstBagSize.setModel(listModel);
}