I have a Tableview with modifiable custum TimePicker cell.
I wrote a function that listens the changes in that cell but when I click on a specific hour, the clock doesn't stay open and close at the first click and i have to click again to select the minutes for exemple.
How can i let the clock open and make the editCommitEvent() when the clock close?
Thank you for your help :)
Here is the code of my custum cell.
PS: I use jfoenix TimePicker
public class TimePickerTableCell<Patient> extends TableCell<Patient, LocalTime> {
private JFXTimePicker timePicker;
private boolean listening = true;
// listener for changes in the timePicker
#SuppressWarnings({ "unchecked", "rawtypes" })
private final ChangeListener<LocalTime> listener = (observable, oldValue, newValue) -> {
if (listening) {
listening = false;
TableColumn<Patient, LocalTime> column = getTableColumn();
EventHandler<TableColumn.CellEditEvent<Patient, LocalTime>> handler = column.getOnEditCommit();
if (handler != null) {
// use TableColumn.onEditCommit if there is a handler
handler.handle(new TableColumn.CellEditEvent<>(
(TableView<Patient>) getTableView(),
new TablePosition<Patient, LocalTime>(getTableView(), getIndex(), column),
TableColumn.<Patient, LocalTime>editCommitEvent(),
newValue
));
} else {
// otherwise check if ObservableValue from cellValueFactory is
// also writable and use in that case
ObservableValue<LocalTime> observableValue = column.getCellObservableValue((Patient) getTableRow().getItem());
if (observableValue instanceof WritableValue) {
((WritableValue) observableValue).setValue(newValue);
}
}
listening = true;
}
};
public TimePickerTableCell () {
this.timePicker = new JFXTimePicker();
this.timePicker.valueProperty().addListener(listener);
this.timePicker.setOnMouseEntered((event)->{timePicker.requestFocus();timePicker.show();System.err.println("OUVERTURE TIMEPICKER");});
this.timePicker.setOnMouseExited((event)->{if(event.getY()<23)timePicker.hide();});
}
#Override
protected void updateItem(LocalTime item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
listening = false;
setGraphic(null);
} else {
listening = false;
setGraphic(this.timePicker);
this.timePicker.setValue(item);
this.timePicker.getStyleClass().add("time-picker");
listening = true;
}
}
public static <E> Callback<TableColumn<E, LocalTime>, TableCell<E, LocalTime>> forTableColumn() {
return column -> new TimePickerTableCell<>();
}
}
First, the reason the JFXTimePicker hides when you go to click the clock is (most likely) because of your onMouseExited handler. When you move your mouse over the popup it "exits" the JFXTimePicker and thus hides the clock.
You're also implementing an editable TableCell the wrong way. You should be overriding the startEdit() and cancelEdit() methods of the Cell class (which TableCell inherits from). You can look at the source code of classes like TextFieldTableCell for how it's done. I also worked up an example for doing this with JFXTimePicker:
import com.jfoenix.controls.JFXTimePicker;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.input.KeyCode;
import javafx.util.Callback;
import javafx.util.converter.LocalTimeStringConverter;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public class TimePickerTableCell<S> extends TableCell<S, LocalTime> {
// Static methods for creating TableColumn.cellFactory Callbacks
public static <S> Callback<TableColumn<S, LocalTime>, TableCell<S, LocalTime>> forTableColumn() {
return v -> new TimePickerTableCell<>();
}
public static <S> Callback<TableColumn<S, LocalTime>, TableCell<S, LocalTime>> forTableColumn(DateTimeFormatter formatter) {
return v -> new TimePickerTableCell<>(formatter);
}
// Formatter property
private final ObjectProperty<DateTimeFormatter> formatter = new SimpleObjectProperty<>(this, "formatter");
public final void setFormatter(DateTimeFormatter formatter) { this.formatter.set(formatter); }
public final DateTimeFormatter getFormatter() { return formatter.get(); }
public final ObjectProperty<DateTimeFormatter> formatterProperty() { return formatter; }
// JFXTimePicker field
private JFXTimePicker timePicker;
// Constructors
public TimePickerTableCell() {
this(DateTimeFormatter.ISO_LOCAL_TIME);
}
public TimePickerTableCell(DateTimeFormatter formatter) {
getStyleClass().add("time-picker-table-cell");
setFormatter(formatter);
}
// Display logic
#Override
protected void updateItem(LocalTime item, boolean empty) {
super.updateItem(item, empty);
setGraphic(null);
if (empty || item == null) {
setText(null);
} else {
setText(formatItem(item));
}
}
private String formatItem(LocalTime item) {
if (item == null) {
return null;
}
return getFormatter() == null ? item.toString() : getFormatter().format(item);
}
// Edit logic
#Override
public void startEdit() {
if (!isEditable() ||
!getTableColumn().isEditable() ||
!getTableView().isEditable()) {
return;
}
super.startEdit();
if (isEditing()) {
if (timePicker == null) {
createTimePicker();
}
timePicker.setValue(getItem());
setText(null);
setGraphic(timePicker);
// Wrapped this in a Platform#runLater call because otherwise
// I couldn't get this to work properly. Despite this, there are
// times where this still seems buggy.
Platform.runLater(() -> {
timePicker.requestFocus();
timePicker.getEditor().selectAll();
});
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText(formatItem(getItem()));
setGraphic(null);
}
private void createTimePicker() {
timePicker = new JFXTimePicker();
timePicker.setConverter(new LocalTimeStringConverter(getFormatter(), null));
formatter.addListener((observable, oldValue, newValue) ->
timePicker.setConverter(new LocalTimeStringConverter(newValue, null)));
timePicker.getEditor().setOnKeyReleased(event -> {
if (event.getCode() == KeyCode.ENTER) {
commitEdit(timePicker.getValue());
event.consume();
} else if (event.getCode() == KeyCode.ESCAPE) {
cancelEdit();
event.consume();
}
});
timePicker.focusedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue) {
cancelEdit();
}
});
}
}
Here, if the ESCAPE key is released or if the JFXTimePicker loses focus then the edit is cancelled. It appears that interacting with the clock does not cause the JFXTimePicker to lose focus (at least when I tried it).
If you want to commit the edit you have to press the ENTER key. This works (again, at least when I tried it) even if the clock is currently showing.
This doesn't commit the edit automatically when the clock closes but you should be able to add that behavior if desired. Since JFXTimePicker extends from ComboBoxBase it has properties such as onHiding and onHidden.
Note: If, after typing the time manually, you attempt to commit and the DateTimeFormatter is unable to parse the String it simply reverts to the old value. There is no indication of any error other than the fact the value hasn't changed. This seems to be behavior caused by the JFXTimePicker, however.
You also don't need to try and handle committing the value yourself, either. The TableColumn already attempts to set the new value on the underlying property by default. This is mentioned in the Javadoc of TableView (under the "Editing" header, emphasis mine):
When you call Cell.commitEdit(Object) an event is fired to the
TableView, which you can observe by adding an EventHandler via
TableColumn.setOnEditCommit(javafx.event.EventHandler). Similarly, you
can also observe edit events for edit start and edit cancel.
By default the TableColumn edit commit handler is non-null, with a
default handler that attempts to overwrite the property value for the
item in the currently-being-edited row. It is able to do this as the
Cell.commitEdit(Object) method is passed in the new value, and this is
passed along to the edit commit handler via the CellEditEvent that is
fired. It is simply a matter of calling
TableColumn.CellEditEvent.getNewValue() to retrieve this value.
If you do end up using your own EventHandler in setOnEditCommit then you need to implement the behavior yourself:
It is very important to note that if you call
TableColumn.setOnEditCommit(javafx.event.EventHandler) with your own
EventHandler, then you will be removing the default handler. Unless
you then handle the writeback to the property (or the relevant data
source), nothing will happen. You can work around this by using the
TableColumnBase.addEventHandler(javafx.event.EventType,
javafx.event.EventHandler) method to add a
TableColumn.editCommitEvent() EventType with your desired EventHandler
as the second argument. Using this method, you will not replace the
default implementation, but you will be notified when an edit commit
has occurred.
Related
I would like to calculate a table's cell content asynchronously in the background. I came up with the following solution:
public abstract class AsynchronousCellFactory<E, T> implements Callback<TableColumn<E, T>, TableCell<E, T>> {
#Override
public TableCell<E, T> call(final TableColumn<E, T> param) {
final TableCell<E, T> cell = new TableCell<E, T>() {
private Service<T> service;
#Override
public void updateItem(final T item, final boolean empty) {
super.updateItem(item, empty);
if (service != null) {
service.cancel();
}
if (empty || this.getTableRow() == null || this.getTableRow().getItem() == null) {
setText(null);
} else {
setText("Calculating..");
final E rowDataItem = (E) this.getTableRow().getItem();
service = new Service<T>() {
#Override
protected Task<T> createTask() {
return getTask(rowDataItem);
}
};
service.setOnSucceeded(e -> {
setText(e.getSource().getValue().toString());
});
service.setOnFailed(e -> {
final Throwable t = e.getSource().getException();
setText(t.getLocalizedMessage());
});
service.start();
}
}
};
return cell;
}
protected abstract Task<T> getTask(E rowDataItem);
}
I use this factory for almost all of my columns to calculate different values.
But this does not work very well.
I frequently see cell contents saying Calculating. When I double click on that cell, which triggers a repaint apparently, the correct value appears.
Furthermore, even worse, I see sometimes that cell content is "switched" between columns: stringA, that should be in columnA, is in comlumnB, for example. Again, a double click on that cell fixes that.
I know that those factories are reused by the table columns to calculate cell content for different items, I assume the error is connected to that fact.
EDIT: When I move the service field to the TableCell, the Calculating error seems to be fixed, nevertheless, the switched-values problem persists.
Find a MWE here. It is actually not a working example, since it does not reproduce the problem. I guess the cell factory is working OK, and I am doing something wrong somewhere else.
Ok, heres my situation. I need to disable both spinner buttons when the Editor is empty, which is the final piece i need to complete this "custom" component. Heres my SSCCE.
When the focus is lost: Default value sets to zero and text is updated.
It only accepts decimal values with 2 decimal places, it is meant to only accept money or percentages values.
Nothing else to add.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class test extends Application{
#Override
public void start(Stage primaryStage) throws Exception {
VBox v = new VBox();
v.setPadding(new Insets(20));
Spinner<Double> spinner = new Spinner<>();
spinner.setEditable(true);
Button dummy = new Button("dummy focus");
v.getChildren().addAll(spinner,dummy);
//----------------------------------HERE IS EVERYTHING RELATED TO THE SPINNER---------------------------------------------
spinner.setValueFactory(new SpinnerValueFactory.DoubleSpinnerValueFactory(0, 100));
spinner.getValueFactory().setValue(0.0);
spinner.getEditor().textProperty().addListener((obs,old,gnu)->{
if(gnu.isEmpty()) {
System.out.println("empty, buttons should be disabled here, they will be disabled after this ");
spinner.getValueFactory().setValue(0.0);
return;
}
System.out.println("enabling buttons");
if(!gnu.matches("^\\d*\\.?\\d*$")) {
try {
spinner.getEditor().setText(old);
spinner.getValueFactory().setValue(Double.parseDouble(old));
}catch (NumberFormatException e) {
System.out.println("invalid string, previous value was empty, no biggie you are safe: Current value : "+spinner.getValueFactory().getValue());
}
} else {
if((Double.parseDouble(gnu)*100)%1!=0) {
spinner.getEditor().setText(old);
spinner.getValueFactory().setValue(Double.parseDouble(old));
}
/*
* You can use this to validate inside a range, for example. PERCENTAGES : 0 ~ 100
*
double val = Double.parseDouble(gnu)*100;
if(val%1!=0 || val>10000 || val<0) {
spinner.getEditor().setText(old);
spinner.getValueFactory().setValue(Double.parseDouble(old));
}
*/
}
});
spinner.getEditor().setOnKeyPressed(e->{
switch (e.getCode()) {
case UP:
spinner.increment(1);
break;
case DOWN:
spinner.decrement(1);
break;
default:
break;
}
});
spinner.setOnScroll(e->{
if(e.getDeltaY()>0)
spinner.increment(1);
else
spinner.decrement(1);
});
spinner.getEditor().focusedProperty().addListener((obs,old,niu)->{
if(!niu && spinner.getEditor().getText().isEmpty()) {
spinner.getEditor().setText("0");
spinner.getValueFactory().setValue(0.0);
}
});
//-----------------------------------------------------------------------------------------------------------------------------------------
Scene sc = new Scene(v);
primaryStage.setScene(sc);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
EDIT:
It also happens with keypress and scroll events.
I was also searching for a way to disable only the buttons. I needed to disable changing the Spinner's value while still allowing copy/paste, so I did some digging in the source code.
What I found was that, while the buttons are not actually part of the Spinner object itself, they're part of the Spinner's SpinnerSkin (they're also not Buttons, but StackPanes), so I managed to disable only the buttons with the following (in Kotlin):
val editing = SimpleBooleanProperty(false)
val spinner = Spinner<Int>()
spinner.skinProperty().addListener { observable, oldValue, newValue ->
// only bind if the skin is an instance of `SpinnerSkin`
if (newValue != null && newValue is SpinnerSkin<*>) {
(skin as SpinnerSkin<*>).children
// only select the children that are `StackPane`s (the buttons)
.filter { it is StackPane }
// bind the `disableProperty` of the buttons to our property for whether we're editing
.forEach { disableProperty().bind(editing.not()) }
}
}
I had to listen to the property change because the the skinProperty is not set on initialization, but only after the CSS gets processed. If you are absolutely sure that your spinner has already displayed and the skin is set, you can just call getSkin instead.
I'm afraid you can't disable only the spinner's buttons. But what about setting the value just after the Editor (which is actually the TextField) is empty? By using such a solution you don't get any exceptions after clicking buttons - value is just incremented from 0. I modified your gnu.isEmpty() code a little.
if(gnu.isEmpty()) {
System.out.println("empty, buttons should be disabled here, they will be disabled after this ");
double valueToSet = 0.0;
spinner.getValueFactory().setValue(valueToSet);
Platform.runLater(() -> spinner.getEditor().setText(Double.toString(valueToSet)));
return;
}
Another thing is, that your code allows to put '0' as a first number, even if there are another numbers after. Check that code, should fix the problem (swap it with the whole if/else statement starting with if(!gnu.matches("^\\d*\\.?\\d*$"))):
if (!isDouble(gnu)) {
gnu = old;
}
spinner.getEditor().setText(gnu);
Where isDouble is a method:
private boolean isDouble(String string) {
boolean startsWithZero =
string.startsWith("0") &&
(string.length() > 1) &&
(!string.startsWith("0."));
boolean minusZeroCondition =
string.startsWith("-0") &&
(string.length() > 2) &&
(!string.startsWith("-0."));
boolean containsTypeSpecificLetters =
Pattern.matches(".*[a-zA-Z].*", string);
boolean isEmpty = string.equals("");
boolean isMinus = string.equals("-");
try {
Double.parseDouble(string);
return !(startsWithZero || minusZeroCondition || containsTypeSpecificLetters);
} catch (IllegalArgumentException exception) {
return isEmpty || isMinus;
}
}
I've created a simple TableView that is fed with data from a database, and what I want is just to be able to easily change the value of a numeric column of that table with JavaFx.
But... since I have some mental issue or something, I can't make it work.
Below it's the "SpinnerCell" component, and the issue I've been having is that even after the commitEdit is fired, when I get the items from the TableView, no values were altered. What am I missing from this update lifecycle?
import javafx.scene.control.Spinner;
import javafx.scene.control.TableCell;
public class SpinnerTableCell<S, T extends Number> extends TableCell<S, T> {
private final Spinner<T> spinner;
public SpinnerTableCell() {
this(1);
}
public SpinnerTableCell(int step) {
this.spinner = new Spinner<>(0, 100, step);
this.spinner.valueProperty().addListener((observable, oldValue, newValue) -> commitEdit(newValue));
}
#Override
protected void updateItem(T c, boolean empty) {
super.updateItem(c, empty);
if (empty || c == null) {
setText(null);
setGraphic(null);
return;
}
this.spinner.getValueFactory().setValue(c);
setGraphic(spinner);
}
}
Because your table cell is always showing the editing control (the Spinner), you bypass the usual table cell mechanism for beginning an edit. For example, in the TextFieldTableCell, if the cell is not in an editing state, then a label is shown. When the user double-clicks the cell, it enters an editing state: the cell's editingProperty() is set to true, and the enclosing TableView's editingCellProperty() is set to the position of the current cell, etc.
In your case, since this never happens, isEditing() is always false for the cell, and as a consequence, commitEdit() becomes a no-op.
Note that the CheckBoxTableCell is implemented similarly: its documentation highlights this fact. (The check box table cell implements its own direct update of properties via the selectedStateCallback.)
So there are two options here: one would be to enter an editing state when the spinner gains focus. You can do this by adding the following to the cell's constructor:
this.spinner.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (isNowFocused) {
getTableView().edit(getIndex(), getTableColumn());
}
});
Another option would be to provide a callback for "direct updates". So you could do something like:
public SpinnerTableCell(BiConsumer<S,T> update, int step) {
this.spinner = new Spinner<>(0, 100, step);
this.spinner.valueProperty().addListener((observable, oldValue, newValue) ->
update.accept(getTableView().getItems().get(getIndex()), newValue));
}
and then given a model class for the table, say
public class Item {
private int value ;
public int getValue() { return value ;}
public void setValue(int value) { this.value = value ;}
// ...
}
You could do
TableView<Item> table = ... ;
TableColumn<Item, Integer> valueCol = new TableColumn<>("Value");
valueCol.setCellValueFactory(cellData -> new SimpleIntegerProperty(cellData.getValue().getValue()).asObject());
valueCol.setCellFactory(tc -> new SpinnerTableCell<>(Item::setValue, 1));
I am trying to change the color of the table rows when I set a boolean.
So I have this code:
boolean searchmode = false;
....
columns.forEach(c -> c.setCellFactory(column -> {
return new TableCell<ShowableInWarenkorb, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : item);
if (searchmode) {
getStyleClass().add("searchmode");
} else{
getStyleClass().remove("searchmode");
}
}
};
}));
This CSS:
.searchmode {
-fx-background-color: rgba(153,153,153,0.3);
})
And then I switch searchmode eventually in my code before I am updating the table contents.
But the color does not change immediatley, sometimes I have to click a little bit around before it changes, how can I trigger it manually?
From your code, it looks like you want to apply this to all cells in the table. You can do this without a cell factory at all (though you may need one for other purposes).
Do
PseudoClass searchmodePseudoClass = PseudoClass.getPseudoClass("searchmode");
and then when you change the value of searchmode, do
table.pseudoClassStateChanged(searchmode);
In your css, do
.table-view:searchmode .table-cell {
-fx-background-color: rgba(153,153,153,0.3);
}
If you want to "automate" the update to the pseudoclass state, use a boolean property and add a listener:
private final BooleanProperty searchmode = new SimpleBooleanProperty(false);
public final boolean isSearchmode() {
return searchmodeProperty().get();
}
public final void setSearchmode(boolean searchmode) {
searchmodeProperty().set(searchmode);
}
public BooleanProperty searchmodeProperty() {
return searchmode ;
}
Then if you add the listener
searchmode.addListener((obs, wasSearchmode, isNowSearchmode) ->
table.pseudoClassStateChanged(searchmodePseudoClass, isNowSearchmode));
everything will be wired automatically so the table changes whenever you call setSearchmode(...).
I want to know whether how to capture the button clicked with AspectJ and get its parameter (eg. button name). I think for having more generalized capturing with AspectJ, it shoudl be used MouseListener so it can capture other UI elements in general!
Example:
In a GUI example I have defined 2 buttons that take some actions
public JButton btn1 = new JButton("Test1");
public JButton btn2 = new JButton("Test2");
btn1.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//take some actions
}
}
btn2.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//take some actions
}
}
How to capture these buttons with AspectJ, and get their parameters (eg. name)?
It is possible. I have provided two examples. The first that prints out for every JButton that has an ActionListener. The other example only prints out if a specific buttons is clicked.
Prints the text for every JButton clicked with an ActionListener:
#Pointcut("execution(* *.actionPerformed(*)) && args(actionEvent)")
public void buttonPointcut(ActionEvent actionEvent) {}
#Before("buttonPointcut(actionEvent)")
public void beforeButtonPointcut(ActionEvent actionEvent) {
if (actionEvent.getSource() instanceof JButton) {
JButton clickedButton = (JButton) actionEvent.getSource();
System.out.println("Button name: " + clickedButton.getText());
}
}
Prints the text for a specific JButton:
public static JButton j1;
#Pointcut("execution(* *.actionPerformed(*)) && args(actionEvent) && if()")
public static boolean button1Pointcut(ActionEvent actionEvent) {
return (actionEvent.getSource() == j1);
}
#Before("button1Pointcut(actionEvent)")
public void beforeButton1Pointcut(ActionEvent actionEvent) {
// logic before the actionPerformed() method is executed for the j1 button..
}
UPDATED:
You can do this in many different ways. For example add your buttons to the aspect directly. But I prefere to use a enum object between (ButtonManager in this case), so the code does not know about the aspect. And since the ButtonManager is an enum object, it is easy for the aspect to retrieve values from it.
I just tested it with a Swing button class from Oracle and it works. In the Swing class:
b1 = new JButton("Disable middle button", leftButtonIcon);
ButtonManager.addJButton(b1);
AspectJ is extremely powerful when it comes to manipulating classes, but it can not weave advises into specific objects since objects is not created at the time of weaving. So you can only work with objects at runtime and that is why I have added the addJButton(..) method above. That enables the aspect to check the advised button against a list of registered buttons.
The ButtonManager class:
public enum ButtonManager {
;
private static Collection<JButton> buttonList = new LinkedList<JButton>();
public static void addJButton(JButton jButton) {
buttonList.add(jButton);
}
public static Collection<JButton> getButtonList() {
return buttonList;
}
}
Modified pointcut and advice to only print the name of the buttons registered in the ButtonManager:
#Pointcut("execution(* *.actionPerformed(*)) && args(actionEvent) && if()")
public static boolean buttonListPointcut(ActionEvent actionEvent) {
Collection<JButton> buttonList = ButtonManager.getButtonList();
JButton registeredButton = null;
for (JButton jButton : buttonList) {
if (actionEvent.getSource() == jButton) {
registeredButton = jButton;
}
}
return registeredButton != null;
}
#Before("buttonListPointcut(actionEvent)")
public void beforeButtonListPointcut(ActionEvent actionEvent) {
JButton clickedButton = (JButton) actionEvent.getSource();
System.out.println("Registered button name: " + clickedButton.getText());
}
UPDATED 2
Okay, I believe I understand what you want. You want to listen to mouse events. That is possible. The downside is that you have to register all your GUI components that you want to listen for clicks with a mouse listener. It is not enough to register the JPanel of the JFrame with a MouseListener. So if you only have registered an ActionListener for your buttons, you also have to add a mouse listener.
I have created a quick solution that works for me. It only shows that it works. I have not tried to make the solution generic with many different GUI objects. But that should be quite easy to refactor in when you have got the basics to work.
In the Swing class:
private class MouseListener extends MouseInputAdapter {
public void mouseClicked(MouseEvent e) {}
}
In the init method of the Swing class:
MouseListener myListener = new MouseListener();
btn1.addMouseListener(myListener);
btn2.addMouseListener(myListener);
In the Aspect class:
#Pointcut("execution(* *.mouseClicked(*)) && args(mouseEvent)")
public void mouseEventPointcut(MouseEvent mouseEvent) {}
#Before("mouseEventPointcut(mouseEvent)")
public void beforeMouseEventPointcut(MouseEvent mouseEvent) {
if (mouseEvent.getSource() instanceof JButton) {
JButton clickedButton = (JButton) mouseEvent.getSource();
System.out.println("aspectJ --> mouseClicked: " + clickedButton.getText());
}
}
This results in the following output in the console:
aspectJ --> mouseClicked: Test1
I hope it helps!