Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Recent Files Menu and Improve Recent Sessions #1616

Merged
merged 5 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/java/org/broad/igv/prefs/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ private Constants() {

//
public static final String RECENT_SESSIONS = "IGV.Session.recent.sessions";
public static final String RECENT_URLS = "IGV.Session.recent.urls";
public static final String LAST_EXPORTED_REGION_DIRECTORY = "LAST_EXPORTED_REGION_DIRECTORY";
public static final String LAST_TRACK_DIRECTORY = "LAST_TRACK_DIRECTORY";
public static final String LAST_SNAPSHOT_DIRECTORY = "LAST_SNAPSHOT_DIRECTORY";
Expand Down
26 changes: 19 additions & 7 deletions src/main/java/org/broad/igv/prefs/IGVPreferences.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@
import org.broad.igv.renderer.SequenceRenderer;
import org.broad.igv.sam.mods.BaseModificationColors;
import org.broad.igv.track.TrackType;
import org.broad.igv.ui.IGV;
import org.broad.igv.ui.IGVMenuBar;
import org.broad.igv.ui.UIConstants;
import org.broad.igv.ui.*;
import org.broad.igv.ui.color.ColorUtilities;
import org.broad.igv.ui.color.PaletteColorTable;
import org.broad.igv.ui.util.MessageUtils;
Expand Down Expand Up @@ -280,7 +278,7 @@ public void put(String key, String value) {
// Explicitly setting removes override
overrideKeys.remove(key);

if (value == null || value.trim().length() == 0) {
if (value == null || value.isBlank()) {
userPreferences.remove(key);
} else {
userPreferences.put(key, value);
Expand All @@ -297,7 +295,7 @@ public void put(String key, boolean b) {

public void putAll(Map<String, String> updatedPrefs) {
for (Map.Entry<String, String> entry : updatedPrefs.entrySet()) {
if (entry.getValue() == null || entry.getValue().trim().length() == 0) {
if (entry.getValue() == null || entry.getValue().isBlank()) {
remove(entry.getKey());
} else {
put(entry.getKey(), entry.getValue());
Expand Down Expand Up @@ -610,14 +608,28 @@ public Rectangle getApplicationFrameBounds() {
* @param recentSessions
*/
public void setRecentSessions(String recentSessions) {
remove(RECENT_SESSIONS);
put(RECENT_SESSIONS, recentSessions);
}


public String getRecentSessions() {
return get(RECENT_SESSIONS, null);
public RecentFileSet getRecentSessions() {
String sessionsString = get(RECENT_SESSIONS, null);
return RecentFileSet.fromString(sessionsString, UIConstants.NUMBER_OF_RECENT_SESSIONS_TO_LIST);
}

public void setRecentUrls(String recentUrls) {
remove(RECENT_URLS);
put(RECENT_URLS, recentUrls);
}


public RecentUrlsSet getRecentUrls() {
String sessionsString = get(RECENT_URLS, null);
return RecentUrlsSet.fromString(sessionsString, UIConstants.NUMBER_OF_RECENT_SESSIONS_TO_LIST);
}


public String getDataServerURL() {
String masterResourceFile = get(DATA_SERVER_URL_KEY);
return masterResourceFile;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.broad.igv.ui;

import javax.swing.*;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;

/**
* MenuListener which updates the availble menu items based on the contents of a changeable collection.
*
* Whenever the menu is selected the relevant JMenuItems are regenerated according to the current values in the backing
* collection.
*
* Items are inserted in a row beneath a given separator.
*
* @param <T> element type of the collection
*/
public class DynamicMenuItemsAdjustmentListener<T> implements MenuSelectedListener {
private final JMenu menu;

private final JSeparator insertionPoint;
private final Collection<T> values;
private final Function<T, JMenuItem> itemConstructor;

//Store the currently visible components here so they can be removed when necessary
private final List<JComponent> activeComponents;

/**
*
* @param menu the menu to modify
* @param insertionPoint a JSeparator which acts as an anchor to always insert elements below. This element is hidden
* when the collection is empty
* @param values a collection which is used to generate menu items
* @param itemConstructor a function to create a JMenuItem from an element in the collection
*/
public DynamicMenuItemsAdjustmentListener(JMenu menu, JSeparator insertionPoint, Collection<T> values, Function<T, JMenuItem> itemConstructor) {
this.menu = menu;
this.insertionPoint = insertionPoint;
this.values = values;
this.itemConstructor = itemConstructor;
this.activeComponents = new ArrayList<>();
}

private List<JMenuItem> getCurrentItems() {
return values.stream().map(itemConstructor).toList();
}

@Override
public void menuSelected(MenuEvent e) {
List<JMenuItem> newComponents = getCurrentItems();

// We definitely don't want to be doing this while other things are also changing the menu
// this should at least protect against multiple of these listeners modifying the same menu at once.
synchronized (menu) {
activeComponents.forEach(menu::remove);
if (newComponents.isEmpty()) {
insertionPoint.setVisible(false);
} else {
insertionPoint.setVisible(true);

final int componentIndex = Arrays.asList(menu.getMenuComponents()).indexOf(insertionPoint);
for (int i = 0; i < newComponents.size(); i++) {
menu.insert(newComponents.get(i), componentIndex + i + 1);
}
activeComponents.addAll(newComponents);
}
}

menu.revalidate();
menu.repaint();
}
}
77 changes: 47 additions & 30 deletions src/main/java/org/broad/igv/ui/IGV.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,10 @@ public class IGV implements IGVEventObserver {
private Timer sessionAutosaveTimer = new Timer();

// Misc state
private Map<String, List<Track>> overlayTracksMap = new HashMap();
private Set<Track> overlaidTracks = new HashSet();
private LinkedList<String> recentSessionList = new LinkedList<String>();
private Map<String, List<Track>> overlayTracksMap = new HashMap<>();
private Set<Track> overlaidTracks = new HashSet<>();
private RecentFileSet recentSessionList;
private RecentUrlsSet recentUrlsList;

// Vertical line that follows the mouse
private boolean rulerEnabled;
Expand Down Expand Up @@ -514,25 +515,15 @@ final public void doViewPreferences() {
final public void saveStateForExit() {

// Store recent sessions
if (!getRecentSessionList().isEmpty()) {

int size = getRecentSessionList().size();
if (size > UIConstants.NUMBER_OF_RECENT_SESSIONS_TO_LIST) {
size = UIConstants.NUMBER_OF_RECENT_SESSIONS_TO_LIST;
}

String recentSessions = "";
for (int i = 0; i <
size; i++) {
recentSessions += getRecentSessionList().get(i);

if (i < (size - 1)) {
recentSessions += ";";
}
RecentFileSet recentSessions = getRecentSessionList();
if (!recentSessions.isEmpty()) {
PreferencesManager.getPreferences().setRecentSessions(recentSessions.asString());
}

}
PreferencesManager.getPreferences().remove(RECENT_SESSIONS);
PreferencesManager.getPreferences().setRecentSessions(recentSessions);
// Store recent files
RecentUrlsSet recentUrls = getRecentUrls();
if (!recentUrls.isEmpty()) {
PreferencesManager.getPreferences().setRecentUrls(recentUrls.asString());
}

// Stop the timer that is triggering the timed autosave
Expand Down Expand Up @@ -991,9 +982,8 @@ public boolean loadSession(String sessionPath, String locus) {
}

mainFrame.setTitle(UIConstants.APPLICATION_NAME + " - Session: " + sessionPath);
if (!recentSessionList.contains(sessionPath)) {
recentSessionList.addFirst(sessionPath);
}

getRecentSessionList().add(sessionPath);
this.menuBar.enableReloadSession();

//If there's a RegionNavigatorDialog, kill it.
Expand All @@ -1006,7 +996,7 @@ public boolean loadSession(String sessionPath, String locus) {
} catch (Exception e) {
String message = "Error loading session session: " + e.getMessage();
MessageUtils.showMessage(message);
recentSessionList.remove(sessionPath);
getRecentSessionList().remove(sessionPath);
log.error(e);
return false;
} finally {
Expand Down Expand Up @@ -1068,9 +1058,8 @@ public void saveSession(File targetFile) throws IOException {
String sessionPath = targetFile.getAbsolutePath();
session.setPath(sessionPath);
mainFrame.setTitle(UIConstants.APPLICATION_NAME + " - Session: " + sessionPath);
if (!recentSessionList.contains(sessionPath)) {
recentSessionList.addFirst(sessionPath);
}

getRecentSessionList().add(sessionPath);
this.menuBar.enableReloadSession();

// No errors so save last location
Expand Down Expand Up @@ -1130,10 +1119,36 @@ public MainPanel getMainPanel() {
return contentPane.getMainPanel();
}

public LinkedList<String> getRecentSessionList() {
public RecentFileSet getRecentSessionList() {
if(recentSessionList == null){
recentSessionList = PreferencesManager.getPreferences().getRecentSessions();
//remove sessions that no longer exist
recentSessionList.removeIf(file -> !(new File(file)).exists());
}
return recentSessionList;
}

public RecentUrlsSet getRecentUrls() {
if(recentUrlsList == null){
recentUrlsList = PreferencesManager.getPreferences().getRecentUrls();
}
return recentUrlsList;
}

/**
* Add new values to the recent URLS set. Calling this method rather than adding them directly
* allows showing the menu when the first URL is added to the collection.
* @param toAdd
*/
public void addToRecentUrls(Collection<ResourceLocator> toAdd){
RecentUrlsSet recentFiles = getRecentUrls();
recentFiles.addAll(toAdd);
if(!recentFiles.isEmpty()){
menuBar.showRecentFilesMenu();
}
}


public IGVContentPane getContentPane() {
return contentPane;
}
Expand Down Expand Up @@ -1173,7 +1188,7 @@ public void loadResources(Collection<ResourceLocator> locators) {

for (final ResourceLocator locator : locators) {

// If its a local file, check explicitly for existence (rather than rely on exception)
// If it's a local file, check explicitly for existence (rather than rely on exception)
if (locator.isLocal()) {
File trackSetFile = new File(locator.getPath());
if (!trackSetFile.exists()) {
Expand All @@ -1182,9 +1197,11 @@ public void loadResources(Collection<ResourceLocator> locators) {
}
}


try {
List<Track> tracks = load(locator);
addTracks(tracks);

} catch (Exception e) {
log.error("Error loading track", e);
messages.append("Error loading " + locator + ": " + e.getMessage());
Expand Down
Loading
Loading