diff --git a/app/src/main/scala/app/logorrr/views/logfiletab/LogFileTab.scala b/app/src/main/scala/app/logorrr/views/logfiletab/LogFileTab.scala index 42e96520..b29fcd97 100644 --- a/app/src/main/scala/app/logorrr/views/logfiletab/LogFileTab.scala +++ b/app/src/main/scala/app/logorrr/views/logfiletab/LogFileTab.scala @@ -16,6 +16,7 @@ import javafx.beans.property.SimpleListProperty import javafx.beans.{InvalidationListener, Observable} import javafx.collections.transformation.FilteredList import javafx.collections.{ListChangeListener, ObservableList} +import javafx.event.Event import javafx.scene.control._ import javafx.scene.layout._ @@ -147,7 +148,7 @@ class LogFileTab(val pathAsString: String def init(): Unit = { - setContextMenu(new ContextMenu(new OpenInFinderMenuItem(pathAsString))) + setTooltip(new LogFileTabToolTip(pathAsString, entries)) initBindings() @@ -164,10 +165,7 @@ class LogFileTab(val pathAsString: String }) /** don't monitor file anymore if tab is closed, free listeners */ - setOnClosed(_ => { - shutdown() - LogoRRRGlobals.removeLogFile(pathAsString) - }) + setOnCloseRequest((t: Event) => cleanupBeforeClose()) if (mutLogFileSettings.isAutoScrollActive) { startTailer() @@ -179,10 +177,56 @@ class LogFileTab(val pathAsString: String divider.setPosition(mutLogFileSettings.getDividerPosition()) + // we have to set the context menu in relation to the visibility of the tab itself + // otherwise those context menu actions can be performed without selecting the tab + // which is kind of confusing + selectedProperty().addListener(JfxUtils.onNew[java.lang.Boolean] { + case java.lang.Boolean.TRUE => { + // getTabPane can be null on initialisation + Option(getTabPane).foreach(_ => setContextMenu(mkContextMenu())) + } + case java.lang.Boolean.FALSE => setContextMenu(null) + }) + logTrace(s"Loaded `$pathAsString` with ${entries.size()} entries.") } + private def mkContextMenu(): ContextMenu = { + val openInFinderMenuItem = new OpenInFinderMenuItem(pathAsString) + val closeMenuItem = new CloseMenuItem(this) + val closeOtherFilesMenuItem = new CloseOtherFilesMenuItem(this) + val closeAllFilesMenuItem = new CloseAllFilesMenuItem(this) + + // close left/right is not always shown. see https://github.com/rladstaetter/LogoRRR/issues/159 + val leftRightCloser = + if (getTabPane.getTabs.size() == 1) { + Seq() + // current tab is the first one, show only 'right' + } else if (getTabPane.getTabs.indexOf(this) == 0) { + Seq(new CloseRightFilesMenuItem(this)) + // we are at the end of the list + } else if (getTabPane.getTabs.indexOf(this) == getTabPane.getTabs.size - 1) { + Seq(new CloseLeftFilesMenuItem(this)) + // we are somewhere in between, show both options + } else { + Seq(new CloseLeftFilesMenuItem(this), new CloseRightFilesMenuItem(this)) + } + + val items = Seq(closeMenuItem + , closeOtherFilesMenuItem + , closeAllFilesMenuItem) ++ leftRightCloser ++ Seq(openInFinderMenuItem) + + val menu = new ContextMenu() + menu.getItems.addAll(items: _*) + menu + } + + def cleanupBeforeClose(): Unit = { + shutdown() + LogoRRRGlobals.removeLogFile(pathAsString) + } + private def addListeners(): Unit = { chunkListView.addListeners() selectedProperty().addListener(selectedListener) diff --git a/app/src/main/scala/app/logorrr/views/logfiletab/OpenInFinderMenuItem.scala b/app/src/main/scala/app/logorrr/views/logfiletab/OpenInFinderMenuItem.scala index a52871d9..3bf64483 100644 --- a/app/src/main/scala/app/logorrr/views/logfiletab/OpenInFinderMenuItem.scala +++ b/app/src/main/scala/app/logorrr/views/logfiletab/OpenInFinderMenuItem.scala @@ -1,10 +1,12 @@ package app.logorrr.views.logfiletab import app.logorrr.util.OsUtil -import javafx.scene.control.MenuItem +import javafx.scene.control.{MenuItem, Tab} import java.awt.Desktop import java.nio.file.Paths +import scala.collection.immutable.Seq +import scala.jdk.CollectionConverters.CollectionHasAsScala class OpenInFinderMenuItem(pathAsString: String) extends MenuItem(if (OsUtil.isWin) { "Show Log in Explorer" @@ -17,3 +19,94 @@ class OpenInFinderMenuItem(pathAsString: String) extends MenuItem(if (OsUtil.isW setOnAction(_ => Desktop.getDesktop.open(Paths.get(pathAsString).getParent.toFile)) } + +class CloseMenuItem(fileTab: => LogFileTab) extends MenuItem("Close") { + + setOnAction(_ => Option(fileTab.getTabPane.getSelectionModel.getSelectedItem).foreach { t => + fileTab.cleanupBeforeClose() + fileTab.getTabPane.getTabs.remove(t) + }) + +} + +class CloseOtherFilesMenuItem(fileTab: => LogFileTab) extends MenuItem("Close Other Files") { + val tabPane = fileTab.getTabPane + setOnAction(_ => { + val toBeDeleted: Seq[Tab] = { + tabPane.getTabs.asScala.flatMap { t => + if (t.asInstanceOf[LogFileTab].pathAsString == fileTab.pathAsString) { + None + } else { + t.asInstanceOf[LogFileTab].cleanupBeforeClose() + Option(t) + } + }.toSeq + } + tabPane.getTabs.removeAll(toBeDeleted: _*) + }) + +} + +class CloseAllFilesMenuItem(fileTab: => LogFileTab) extends MenuItem("Close All Files") { + val tabPane = fileTab.getTabPane + setOnAction(_ => { + val toBeDeleted: Seq[Tab] = { + tabPane.getTabs.asScala.flatMap { t => + t.asInstanceOf[LogFileTab].cleanupBeforeClose() + Option(t) + }.toSeq + } + tabPane.getTabs.removeAll(toBeDeleted: _*) + }) + +} + + +class CloseLeftFilesMenuItem(fileTab: => LogFileTab) extends MenuItem("Close Files to the Left") { + val tabPane = fileTab.getTabPane + setOnAction(_ => { + var deletethem = true + val toBeDeleted: Seq[Tab] = { + tabPane.getTabs.asScala.flatMap { t => + if (t.asInstanceOf[LogFileTab].pathAsString == fileTab.pathAsString) { + deletethem = false + None + } else { + if (deletethem) { + t.asInstanceOf[LogFileTab].cleanupBeforeClose() + Option(t) + } else { + None + } + } + }.toSeq + } + tabPane.getTabs.removeAll(toBeDeleted: _*) + }) + +} + + +class CloseRightFilesMenuItem(fileTab: => LogFileTab) extends MenuItem("Close Files to the Right") { + val tabPane = fileTab.getTabPane + setOnAction(_ => { + var deletethem = false + val toBeDeleted: Seq[Tab] = { + tabPane.getTabs.asScala.flatMap { t => + if (t.asInstanceOf[LogFileTab].pathAsString == fileTab.pathAsString) { + deletethem = true + None + } else { + if (deletethem) { + t.asInstanceOf[LogFileTab].cleanupBeforeClose() + Option(t) + } else { + None + } + } + }.toSeq + } + tabPane.getTabs.removeAll(toBeDeleted: _*) + }) + +} \ No newline at end of file diff --git a/app/src/main/scala/app/logorrr/views/main/LogoRRRMainTabPane.scala b/app/src/main/scala/app/logorrr/views/main/LogoRRRMainTabPane.scala index 31dc449f..4d44abcf 100644 --- a/app/src/main/scala/app/logorrr/views/main/LogoRRRMainTabPane.scala +++ b/app/src/main/scala/app/logorrr/views/main/LogoRRRMainTabPane.scala @@ -30,7 +30,7 @@ class LogoRRRMainTabPane extends TabPane with CanLog { LogoRRRGlobals.setSomeActive(Option(logFileTab.pathAsString)) // to set 'selected' property in Tab and to trigger repaint correctly (see issue #9) getSelectionModel.select(logFileTab) - case _ => ??? + case x => getSelectionModel.select(null) } init()