Skip to content

Commit

Permalink
Fix for issue: right click menu 6601 (#9271)
Browse files Browse the repository at this point in the history
  • Loading branch information
ethantr committed Dec 5, 2022
1 parent 4c82d3e commit a1ef245
Show file tree
Hide file tree
Showing 3 changed files with 282 additions and 40 deletions.
10 changes: 10 additions & 0 deletions src/main/java/org/jabref/gui/maintable/MainTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,16 @@ public MainTable(MainTableDataModel model,
});

database.getDatabase().registerListener(this);

MainTableColumnFactory rightClickMenuFactory = new MainTableColumnFactory(
database,
preferencesService,
preferencesService.getColumnPreferences(),
libraryTab.getUndoManager(),
dialogService,
stateManager);
// Enable the header right-click menu.
new MainTableHeaderContextMenu(this, rightClickMenuFactory).show(true);
}

/**
Expand Down
85 changes: 45 additions & 40 deletions src/main/java/org/jabref/gui/maintable/MainTableColumnFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,50 +74,55 @@ public MainTableColumnFactory(BibDatabaseContext database,
this.stateManager = stateManager;
}

public TableColumn<BibEntryTableViewModel, ?> createColumn(MainTableColumnModel column) {
TableColumn<BibEntryTableViewModel, ?> returnColumn = null;
switch (column.getType()) {
case INDEX:
returnColumn = createIndexColumn(column);
break;
case GROUPS:
returnColumn = createGroupColumn(column);
break;
case FILES:
returnColumn = createFilesColumn(column);
break;
case LINKED_IDENTIFIER:
returnColumn = createIdentifierColumn(column);
break;
case LIBRARY_NAME:
returnColumn = createLibraryColumn(column);
break;
case EXTRAFILE:
if (!column.getQualifier().isBlank()) {
returnColumn = createExtraFileColumn(column);
}
break;
case SPECIALFIELD:
if (!column.getQualifier().isBlank()) {
Field field = FieldFactory.parseField(column.getQualifier());
if (field instanceof SpecialField) {
returnColumn = createSpecialFieldColumn(column);
} else {
LOGGER.warn("Special field type '{}' is unknown. Using normal column type.", column.getQualifier());
returnColumn = createFieldColumn(column);
}
}
break;
default:
case NORMALFIELD:
if (!column.getQualifier().isBlank()) {
returnColumn = createFieldColumn(column);
}
break;
}
return returnColumn;
}

public List<TableColumn<BibEntryTableViewModel, ?>> createColumns() {
List<TableColumn<BibEntryTableViewModel, ?>> columns = new ArrayList<>();

columnPreferences.getColumns().forEach(column -> {

switch (column.getType()) {
case INDEX:
columns.add(createIndexColumn(column));
break;
case GROUPS:
columns.add(createGroupColumn(column));
break;
case FILES:
columns.add(createFilesColumn(column));
break;
case LINKED_IDENTIFIER:
columns.add(createIdentifierColumn(column));
break;
case LIBRARY_NAME:
columns.add(createLibraryColumn(column));
break;
case EXTRAFILE:
if (!column.getQualifier().isBlank()) {
columns.add(createExtraFileColumn(column));
}
break;
case SPECIALFIELD:
if (!column.getQualifier().isBlank()) {
Field field = FieldFactory.parseField(column.getQualifier());
if (field instanceof SpecialField) {
columns.add(createSpecialFieldColumn(column));
} else {
LOGGER.warn("Special field type '{}' is unknown. Using normal column type.", column.getQualifier());
columns.add(createFieldColumn(column));
}
}
break;
default:
case NORMALFIELD:
if (!column.getQualifier().isBlank()) {
columns.add(createFieldColumn(column));
}
break;
}
columns.add(createColumn(column));
});

return columns;
Expand Down
227 changes: 227 additions & 0 deletions src/main/java/org/jabref/gui/maintable/MainTableHeaderContextMenu.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
package org.jabref.gui.maintable;

import java.util.ArrayList;
import java.util.List;

import javafx.collections.ObservableList;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.RadioMenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.control.TableColumn;
import javafx.scene.layout.StackPane;

import org.jabref.gui.maintable.columns.MainTableColumn;

public class MainTableHeaderContextMenu extends ContextMenu {

private static final int OUT_OF_BOUNDS = -1;
MainTable mainTable;
MainTableColumnFactory factory;

/**
* Constructor for the right click menu
*
*/
public MainTableHeaderContextMenu(MainTable mainTable, MainTableColumnFactory factory) {
super();
this.mainTable = mainTable;
this.factory = factory;
constructItems(mainTable);
}

/**
* Handles showing the menu in the cursors position when right-clicked.
*/
public void show(boolean show) {
// TODO: 20/10/2022 unknown bug where issue does not show unless parameter is passed through this method.
mainTable.setOnContextMenuRequested(event -> {
// Display the menu if header is clicked, otherwise, remove from display.
if (!(event.getTarget() instanceof StackPane) && show) {
this.show(mainTable, event.getScreenX(), event.getScreenY());
} else if (this.isShowing()) {
this.hide();
}
event.consume();
});
}

/**
* Constructs the items for the list and places them in the menu.
*/
private void constructItems(MainTable mainTable) {
// Reset the right-click menu
this.getItems().clear();
List<TableColumn<BibEntryTableViewModel, ?>> commonColumns = commonColumns();

// Populate the menu with currently used fields
for (TableColumn<BibEntryTableViewModel, ?> column : mainTable.getColumns()) {
// Append only if the column has not already been added (a common column)
RightClickMenuItem itemToAdd = createMenuItem(column, true);
this.getItems().add(itemToAdd);

// Remove from remaining common columns pool
MainTableColumn searchCol = (MainTableColumn<?>) column;
if (isACommonColumn(searchCol)) {
commonColumns.removeIf(tableCol -> ((MainTableColumn) tableCol).getModel().equals(searchCol.getModel()));
}
}

SeparatorMenuItem separator = new SeparatorMenuItem();
this.getItems().add(separator);

// Append to the menu the current remaining columns in the common columns.

for (TableColumn<BibEntryTableViewModel, ?> tableColumn : commonColumns) {
RightClickMenuItem itemToAdd = createMenuItem(tableColumn, false);
this.getItems().add(itemToAdd);
}
}

/**
* Creates an item for the menu constructed with the name/visibility of the table column.
*
*/
@SuppressWarnings("rawtypes")
private RightClickMenuItem createMenuItem(TableColumn<BibEntryTableViewModel, ?> column, boolean isDisplaying) {
// Gets display name and constructs Radio Menu Item.
MainTableColumn tableColumn = (MainTableColumn) column;
String displayName = tableColumn.getDisplayName();
return new RightClickMenuItem(displayName, tableColumn, isDisplaying);
}

/**
* Returns the current position of the inputted column in the table (index).
*
*/
private int obtainIndexOfColumn(MainTableColumn searchColumn) {
ObservableList<TableColumn<BibEntryTableViewModel, ?>> columns = mainTable.getColumns();
for (int i = 0; i < columns.size(); i++) {
TableColumn<BibEntryTableViewModel, ?> column = columns.get(i);
MainTableColumnModel model = ((MainTableColumn) column).getModel();
if (model.equals(searchColumn.getModel())) {
return i;
}
}
return OUT_OF_BOUNDS;
}

/**
* Adds the column into the MainTable for display.
*/
@SuppressWarnings("rawtypes")
private void addColumn(MainTableColumn tableColumn, int index) {
if (index <= OUT_OF_BOUNDS || index >= mainTable.getColumns().size()) {
mainTable.getColumns().add(tableColumn);
} else {
mainTable.getColumns().add(index, tableColumn);
}
}

/**
* Removes the column from the MainTable to remove visibility.
*/
@SuppressWarnings("rawtypes")
private void removeColumn(MainTableColumn tableColumn) {
mainTable.getColumns().removeIf(tableCol -> ((MainTableColumn) tableCol).getModel().equals(tableColumn.getModel()));
}

/**
* Checks if a column is one of the commonly used columns.
*/
private boolean isACommonColumn(MainTableColumn tableColumn) {
return isColumnInList(tableColumn, commonColumns());
}

/**
* Determines if a list of TableColumns contains the searched column.
*/
private boolean isColumnInList(MainTableColumn searchColumn, List<TableColumn<BibEntryTableViewModel, ?>> tableColumns) {
for (TableColumn<BibEntryTableViewModel, ?> column:
tableColumns) {
MainTableColumnModel model = ((MainTableColumn) column).getModel();
if (model.equals(searchColumn.getModel())) {
return true;
}
}
return false;
}

/**
* Creates the list of the "commonly used" columns (currently based on the default preferences).
*/
private List<TableColumn<BibEntryTableViewModel, ?>> commonColumns() {
// Qualifier strings
String entryTypeQualifier = "entrytype";
String authorEditQualifier = "author/editor";
String titleQualifier = "title";
String yearQualifier = "year";
String journalBookQualifier = "journal/booktitle";
String rankQualifier = "ranking";
String readStatusQualifier = "readstatus";
String priorityQualifier = "priority";

// Create the MainTableColumn Models from qualifiers + types.
List<MainTableColumnModel> commonColumns = new ArrayList<>();
commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.GROUPS));
commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.FILES));
commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.LINKED_IDENTIFIER));
commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.NORMALFIELD, entryTypeQualifier));
commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.NORMALFIELD, authorEditQualifier));
commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.NORMALFIELD, titleQualifier));
commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.NORMALFIELD, yearQualifier));
commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.NORMALFIELD, journalBookQualifier));
commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.SPECIALFIELD, rankQualifier));
commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.SPECIALFIELD, readStatusQualifier));
commonColumns.add(new MainTableColumnModel(MainTableColumnModel.Type.SPECIALFIELD, priorityQualifier));

// Create the Table Columns from the models using factory methods.
List<TableColumn<BibEntryTableViewModel, ?>> commonTableColumns = new ArrayList<>();
for (MainTableColumnModel columnModel: commonColumns) {
TableColumn<BibEntryTableViewModel, ?> tableColumn = factory.createColumn(columnModel);
commonTableColumns.add(tableColumn);
}
return commonTableColumns;
}

/**
* RightClickMenuItem: RadioMenuItem holding position in MainTable and its visibility.
*
*/
private class RightClickMenuItem extends RadioMenuItem {
private int index;
private boolean visibleInTable;

RightClickMenuItem(String displayName, MainTableColumn column, boolean isVisible) {
super(displayName);
setVisibleInTable(isVisible);
// Flag item as selected if the item is already in the main table.
this.setSelected(isVisible);

setIndex(OUT_OF_BOUNDS);

// Set action to toggle visibility from main table when item is clicked
this.setOnAction(event -> {
if (isVisibleInTable()) {
setIndex(obtainIndexOfColumn(column));
removeColumn(column);
} else {
addColumn(column, this.index);
setIndex(obtainIndexOfColumn(column));
}
setVisibleInTable(!this.visibleInTable);
});
}

public void setIndex(int index) {
this.index = index;
}

public void setVisibleInTable(boolean visibleInTable) {
this.visibleInTable = visibleInTable;
}

public boolean isVisibleInTable() {
return visibleInTable;
}
}
}

0 comments on commit a1ef245

Please sign in to comment.