diff --git a/app-tests/src/test/resources/app/logorrr/SimpleLog-2.txt b/app-tests/src/test/resources/app/logorrr/SimpleLog-2.txt index 69e06794..a05838e9 100644 --- a/app-tests/src/test/resources/app/logorrr/SimpleLog-2.txt +++ b/app-tests/src/test/resources/app/logorrr/SimpleLog-2.txt @@ -1,4 +1,5 @@ FINEST INFO WARNING -SEVERE \ No newline at end of file +SEVERE +not classified \ No newline at end of file diff --git a/app-tests/src/test/scala/app/logorrr/TestFiles.scala b/app-tests/src/test/scala/app/logorrr/TestFiles.scala index b1e88ffb..e0a09265 100644 --- a/app-tests/src/test/scala/app/logorrr/TestFiles.scala +++ b/app-tests/src/test/scala/app/logorrr/TestFiles.scala @@ -8,11 +8,11 @@ object TestFiles { val baseDir: Path = Paths.get("src/test/resources/app/logorrr/") - val simpleLog0: FileId = FileId(baseDir.resolve("SimpleLog-0.txt")) - val simpleLog1: FileId = FileId(baseDir.resolve("SimpleLog-1.txt")) - val simpleLog2: FileId = FileId(baseDir.resolve("SimpleLog-2.txt")) - val simpleLog3: FileId = FileId(baseDir.resolve("SimpleLog-3.txt")) - val simpleLog4: FileId = FileId(baseDir.resolve("SimpleLog-4.txt")) + val simpleLog0: FileId = FileId(baseDir.resolve("SimpleLog-0.txt")) // 4 entries + val simpleLog1: FileId = FileId(baseDir.resolve("SimpleLog-1.txt")) // 100 entries + val simpleLog2: FileId = FileId(baseDir.resolve("SimpleLog-2.txt")) // 5 entries + val simpleLog3: FileId = FileId(baseDir.resolve("SimpleLog-3.txt")) // 4 entries + val simpleLog4: FileId = FileId(baseDir.resolve("SimpleLog-4.txt")) // 4 entries val zipFileContaining10Files: FileId = FileId(baseDir.resolve("zip-containing-10-files.zip")) diff --git a/app-tests/src/test/scala/app/logorrr/issues/Issue236OpenMultipleTabsAndChooseDefaultFilterTest.scala b/app-tests/src/test/scala/app/logorrr/issues/Issue236OpenMultipleTabsAndChooseDefaultFilterTest.scala new file mode 100644 index 00000000..b723f2ca --- /dev/null +++ b/app-tests/src/test/scala/app/logorrr/issues/Issue236OpenMultipleTabsAndChooseDefaultFilterTest.scala @@ -0,0 +1,47 @@ +package app.logorrr.issues + +import app.logorrr.TestFiles +import app.logorrr.model.LogFileSettings +import app.logorrr.steps.CheckTabPaneActions +import app.logorrr.usecases.MultipleFileApplicationTest +import app.logorrr.views.search.FilterButton +import javafx.scene.control.ToggleButton +import org.junit.jupiter.api.Test +import org.testfx.api.FxAssert + +import java.util.function.Predicate + +/** + * https://github.com/rladstaetter/LogoRRR/issues/236 + * + * Shows that the default filters are used for a file after the first file was opened and the filter selection + * was changed. + * */ +class Issue236OpenMultipleTabsAndChooseDefaultFilterTest + extends MultipleFileApplicationTest(TestFiles.seq) + with CheckTabPaneActions { + + @Test def testIssue236(): Unit = { + // open first file + openFile(TestFiles.simpleLog0) + + // change filters to a non default configuration + val firstFilterTab1 = FilterButton.uiNode(TestFiles.simpleLog0, LogFileSettings.DefaultFilters.head) + waitAndClickVisibleItem(firstFilterTab1) + + // check that the toggle button is deselected + FxAssert.verifyThat(lookup(firstFilterTab1.ref), new Predicate[ToggleButton] { + override def test(t: ToggleButton): Boolean = !t.isSelected + }) + + // open second file + openFile(TestFiles.simpleLog1) + + // test that second file has the default filter configuration + val firstFilterTab2 = FilterButton.uiNode(TestFiles.simpleLog1, LogFileSettings.DefaultFilters.head) + FxAssert.verifyThat(lookup(firstFilterTab2.ref), new Predicate[ToggleButton] { + override def test(t: ToggleButton): Boolean = t.isSelected + }) + } + +} diff --git a/app-tests/src/test/scala/app/logorrr/services/file/EmptyFileIdService.scala b/app-tests/src/test/scala/app/logorrr/services/file/EmptyFileIdService.scala new file mode 100644 index 00000000..110a2c59 --- /dev/null +++ b/app-tests/src/test/scala/app/logorrr/services/file/EmptyFileIdService.scala @@ -0,0 +1,6 @@ +package app.logorrr.services.file + +/** + * Don't provide any FileId, for tests which don't need any file operations + */ +class EmptyFileIdService extends MockFileIdService(Seq()) diff --git a/app-tests/src/test/scala/app/logorrr/services/file/EmptyFileService.scala b/app-tests/src/test/scala/app/logorrr/services/file/EmptyFileService.scala deleted file mode 100644 index 05f350ad..00000000 --- a/app-tests/src/test/scala/app/logorrr/services/file/EmptyFileService.scala +++ /dev/null @@ -1,3 +0,0 @@ -package app.logorrr.services.file - -class EmptyFileService extends MockFileService(Seq()) diff --git a/app-tests/src/test/scala/app/logorrr/services/file/MockFileService.scala b/app-tests/src/test/scala/app/logorrr/services/file/MockFileIdService.scala similarity index 73% rename from app-tests/src/test/scala/app/logorrr/services/file/MockFileService.scala rename to app-tests/src/test/scala/app/logorrr/services/file/MockFileIdService.scala index f55818ef..b8a71fcb 100644 --- a/app-tests/src/test/scala/app/logorrr/services/file/MockFileService.scala +++ b/app-tests/src/test/scala/app/logorrr/services/file/MockFileIdService.scala @@ -8,11 +8,11 @@ import app.logorrr.io.FileId * * @param files which this service is returning */ -class MockFileService(files: Seq[FileId]) extends FileService { +class MockFileIdService(files: Seq[FileId]) extends FileIdService { private val it = files.iterator - override def openFile: Option[FileId] = { + override def provideFileId: Option[FileId] = { if (it.hasNext) { Option(it.next()) } else None diff --git a/app-tests/src/test/scala/app/logorrr/services/file/SingleFileService.scala b/app-tests/src/test/scala/app/logorrr/services/file/SingleFileIdService.scala similarity index 65% rename from app-tests/src/test/scala/app/logorrr/services/file/SingleFileService.scala rename to app-tests/src/test/scala/app/logorrr/services/file/SingleFileIdService.scala index d392703e..b74eb9e2 100644 --- a/app-tests/src/test/scala/app/logorrr/services/file/SingleFileService.scala +++ b/app-tests/src/test/scala/app/logorrr/services/file/SingleFileIdService.scala @@ -7,4 +7,4 @@ import app.logorrr.io.FileId * * @param fileId file reference to open */ -class SingleFileService(fileId: FileId) extends MockFileService(Seq(fileId)) +class SingleFileIdService(fileId: FileId) extends MockFileIdService(Seq(fileId)) diff --git a/app-tests/src/test/scala/app/logorrr/steps/FileMenuActions.scala b/app-tests/src/test/scala/app/logorrr/steps/FileMenuActions.scala index f190a9dd..e408540a 100644 --- a/app-tests/src/test/scala/app/logorrr/steps/FileMenuActions.scala +++ b/app-tests/src/test/scala/app/logorrr/steps/FileMenuActions.scala @@ -27,7 +27,7 @@ trait FileMenuActions extends VisibleItemActions { def quitApplication(): Unit = { waitAndClickVisibleItem(UiNodes.FileMenu) - waitAndClickVisibleItem(UiNodes.FileMenuQuitApplication) + waitAndClickVisibleItem(UiNodes.FileMenuCloseApplication) } } diff --git a/app-tests/src/test/scala/app/logorrr/usecases/MultipleFileApplicationTest.scala b/app-tests/src/test/scala/app/logorrr/usecases/MultipleFileApplicationTest.scala index 9d7651d0..b91518df 100644 --- a/app-tests/src/test/scala/app/logorrr/usecases/MultipleFileApplicationTest.scala +++ b/app-tests/src/test/scala/app/logorrr/usecases/MultipleFileApplicationTest.scala @@ -3,7 +3,7 @@ package app.logorrr.usecases import app.logorrr.conf.Settings import app.logorrr.io.FileId import app.logorrr.services.LogoRRRServices -import app.logorrr.services.file.MockFileService +import app.logorrr.services.file.MockFileIdService import app.logorrr.services.hostservices.MockHostServices import app.logorrr.steps.{CanStartApplication, VisibleItemActions} @@ -19,7 +19,7 @@ class MultipleFileApplicationTest(val files: Seq[FileId]) lazy val services: LogoRRRServices = LogoRRRServices(Settings.Default , new MockHostServices - , new MockFileService(files) + , new MockFileIdService(files) , isUnderTest = true) } diff --git a/app-tests/src/test/scala/app/logorrr/usecases/SingleFileApplicationTest.scala b/app-tests/src/test/scala/app/logorrr/usecases/SingleFileApplicationTest.scala index 6c9c9097..12b51250 100644 --- a/app-tests/src/test/scala/app/logorrr/usecases/SingleFileApplicationTest.scala +++ b/app-tests/src/test/scala/app/logorrr/usecases/SingleFileApplicationTest.scala @@ -3,7 +3,7 @@ package app.logorrr.usecases import app.logorrr.conf.Settings import app.logorrr.io.FileId import app.logorrr.services.LogoRRRServices -import app.logorrr.services.file.SingleFileService +import app.logorrr.services.file.SingleFileIdService import app.logorrr.services.hostservices.MockHostServices import app.logorrr.steps.CanStartApplication @@ -16,7 +16,7 @@ class SingleFileApplicationTest(val fileId: FileId) final def services: LogoRRRServices = LogoRRRServices(Settings.Default , new MockHostServices - , new SingleFileService(fileId) + , new SingleFileIdService(fileId) , isUnderTest = true) diff --git a/app-tests/src/test/scala/app/logorrr/usecases/StartEmptyApplicationTest.scala b/app-tests/src/test/scala/app/logorrr/usecases/StartEmptyApplicationTest.scala index 7140d7de..65f11db2 100644 --- a/app-tests/src/test/scala/app/logorrr/usecases/StartEmptyApplicationTest.scala +++ b/app-tests/src/test/scala/app/logorrr/usecases/StartEmptyApplicationTest.scala @@ -2,7 +2,7 @@ package app.logorrr.usecases import app.logorrr.conf.Settings import app.logorrr.services.LogoRRRServices -import app.logorrr.services.file.EmptyFileService +import app.logorrr.services.file.EmptyFileIdService import app.logorrr.services.hostservices.MockHostServices import app.logorrr.steps.CanStartApplication @@ -16,7 +16,7 @@ class StartEmptyApplicationTest final def services: LogoRRRServices = LogoRRRServices(Settings.Default , new MockHostServices - , new EmptyFileService + , new EmptyFileIdService , isUnderTest = true) diff --git a/app-tests/src/test/scala/app/logorrr/usecases/about/ShowAboutDialogTest.scala b/app-tests/src/test/scala/app/logorrr/usecases/about/ShowAboutDialogTest.scala index 8b7fd995..71b172c5 100644 --- a/app-tests/src/test/scala/app/logorrr/usecases/about/ShowAboutDialogTest.scala +++ b/app-tests/src/test/scala/app/logorrr/usecases/about/ShowAboutDialogTest.scala @@ -2,7 +2,7 @@ package app.logorrr.usecases.about import app.logorrr.conf.Settings import app.logorrr.services.LogoRRRServices -import app.logorrr.services.file.EmptyFileService +import app.logorrr.services.file.EmptyFileIdService import app.logorrr.services.hostservices.MockHostServices import app.logorrr.steps.{CanStartApplication, VisibleItemActions} import app.logorrr.usecases.TestFxBaseApplicationTest @@ -21,7 +21,7 @@ class ShowAboutDialogTest extends TestFxBaseApplicationTest final def services: LogoRRRServices = { LogoRRRServices(Settings.Default , mockHostServices - , new EmptyFileService + , new EmptyFileIdService , isUnderTest = true) } diff --git a/app-tests/src/test/scala/app/logorrr/usecases/search/SelectDefaultFilterTest.scala b/app-tests/src/test/scala/app/logorrr/usecases/search/SelectDefaultFilterTest.scala index 4e490f6c..a283cd07 100644 --- a/app-tests/src/test/scala/app/logorrr/usecases/search/SelectDefaultFilterTest.scala +++ b/app-tests/src/test/scala/app/logorrr/usecases/search/SelectDefaultFilterTest.scala @@ -3,7 +3,7 @@ package app.logorrr.usecases.search import app.logorrr.TestFiles import app.logorrr.model.LogFileSettings import app.logorrr.usecases.SingleFileApplicationTest -import app.logorrr.views.search.FilterButton +import app.logorrr.views.search.{Filter, FilterButton, UnclassifiedFilter} import app.logorrr.views.text.LogTextView import org.junit.jupiter.api.Test @@ -13,22 +13,46 @@ import org.junit.jupiter.api.Test class SelectDefaultFilterTest extends SingleFileApplicationTest(TestFiles.simpleLog2) { @Test def selectSpecificFilter(): Unit = { + // file has 5 entries - each line consisting of one entry corresponding to + // the default filters, like 'FINEST', 'INFO, ' WARNING', 'SEVERE' + // and a 'unclassified' line openFile(fileId) LogFileSettings.DefaultFilters.foreach { f => - LogFileSettings.DefaultFilters.foreach { - ff => waitAndClickVisibleItem(FilterButton.uiNode(fileId, ff)) // disable all filters - } - waitAndClickVisibleItem(FilterButton.uiNode(fileId, f)) // enable one specific filer - - val res = lookup(LogTextView.uiNode(fileId).ref).query[LogTextView] - assert(res.getItems.size() == 1) - waitAndClickVisibleItem(FilterButton.uiNode(fileId, f)) // enable disable specific filer - - LogFileSettings.DefaultFilters.foreach { - ff => waitAndClickVisibleItem(FilterButton.uiNode(fileId, ff)) // enable all filters - } + // deselect all filters except unclassified + clickFilters(LogFileSettings.DefaultFilters) + + // now, only 'unclassified' filter is active. since there are no unclassified + // entries available, the number of displayed log entries is one + checkNumberOfShownElements(1) + + // select one specific filter- now two lines are shown in total + waitAndClickVisibleItem(FilterButton.uiNode(fileId, f)) + checkNumberOfShownElements(2) + + // deselect filter again + waitAndClickVisibleItem(FilterButton.uiNode(fileId, f)) + clickFilters(LogFileSettings.DefaultFilters) + } + + // finally, deselect all filters + clickFilters(LogFileSettings.DefaultFilters) + // one entry is shown (unclassified) + checkNumberOfShownElements(1) + + // deselect unclassified filter works as well + clickFilters(Seq(UnclassifiedFilter(LogFileSettings.DefaultFilters.toSet))) + checkNumberOfShownElements(0) + } + + def checkNumberOfShownElements(expectedElements: Int): Unit = { + assert(lookup(LogTextView.uiNode(fileId).ref).query[LogTextView].getItems.size() == expectedElements) + } + + def clickFilters(filters: Seq[Filter]): Unit = { + filters.foreach { + ff => waitAndClickVisibleItem(FilterButton.uiNode(fileId, ff)) // enable all filters } } diff --git a/app/src/main/scala/app/logorrr/LogoRRRApp.scala b/app/src/main/scala/app/logorrr/LogoRRRApp.scala index bad5e4cb..8b6b77d6 100644 --- a/app/src/main/scala/app/logorrr/LogoRRRApp.scala +++ b/app/src/main/scala/app/logorrr/LogoRRRApp.scala @@ -5,7 +5,7 @@ import app.logorrr.conf.{LogoRRRGlobals, SettingsIO} import app.logorrr.io.FilePaths import app.logorrr.meta.AppMeta import app.logorrr.services.LogoRRRServices -import app.logorrr.services.file.DefaultFileService +import app.logorrr.services.file.DefaultFileIdService import app.logorrr.services.hostservices.{MacNativeHostService, NativeHostServices} import app.logorrr.util.{CanLog, JfxUtils, OsUtil} import app.logorrr.views.main.{LogoRRRMain, LogoRRRStage} @@ -36,7 +36,7 @@ object LogoRRRApp extends CanLog { Application.setUserAgentStylesheet("/app/logorrr/LogoRRR.css") LogoRRRGlobals.set(services.settings, services.hostServices) - val logoRRRMain = new LogoRRRMain(JfxUtils.closeStage(stage), services.fileOpenService, services.isUnderTest) + val logoRRRMain = new LogoRRRMain(JfxUtils.closeStage(stage), services.fileIdService, services.isUnderTest) LogoRRRStage.init(stage, logoRRRMain) logInfo(s"Started ${AppMeta.fullAppNameWithVersion} in '${Paths.get("").toAbsolutePath.toString}'") @@ -58,7 +58,7 @@ class LogoRRRApp extends javafx.application.Application with CanLog { val services = logorrr.services.LogoRRRServices( SettingsIO.fromFile(FilePaths.settingsFilePath) , hostServices - , new DefaultFileService(() => stage.getScene.getWindow) + , new DefaultFileIdService(() => stage.getScene.getWindow) , isUnderTest = false) LogoRRRApp.start(stage, services) diff --git a/app/src/main/scala/app/logorrr/conf/mut/MutLogFileSettings.scala b/app/src/main/scala/app/logorrr/conf/mut/MutLogFileSettings.scala index fa70f2d5..4ea7e1e8 100644 --- a/app/src/main/scala/app/logorrr/conf/mut/MutLogFileSettings.scala +++ b/app/src/main/scala/app/logorrr/conf/mut/MutLogFileSettings.scala @@ -15,10 +15,10 @@ object MutLogFileSettings { def apply(logFileSettings: LogFileSettings): MutLogFileSettings = { val s = new MutLogFileSettings + s.setFileId(logFileSettings.fileId) s.setSelectedLineNumber(logFileSettings.selectedLineNumber) s.setFontSize(logFileSettings.fontSize) s.setBlockSettings(logFileSettings.blockSettings) - s.setFileId(logFileSettings.fileId) s.firstOpenedProperty.set(logFileSettings.firstOpened) s.setDividerPosition(logFileSettings.dividerPosition) s.setFilters(logFileSettings.filters) diff --git a/app/src/main/scala/app/logorrr/model/LogFileSettings.scala b/app/src/main/scala/app/logorrr/model/LogFileSettings.scala index b9cff89c..31e42e7e 100644 --- a/app/src/main/scala/app/logorrr/model/LogFileSettings.scala +++ b/app/src/main/scala/app/logorrr/model/LogFileSettings.scala @@ -2,13 +2,12 @@ package app.logorrr.model import app.logorrr.conf.BlockSettings import app.logorrr.io.FileId -import app.logorrr.util.CanLog import app.logorrr.views.search.Filter import javafx.scene.paint.Color import pureconfig.generic.semiauto.{deriveReader, deriveWriter} import pureconfig.{ConfigReader, ConfigWriter} -import java.nio.file.{Path, Paths} +import java.nio.file.Path import java.time.Instant object LogFileSettings { @@ -27,7 +26,6 @@ object LogFileSettings { private val InfoFilter: Filter = new Filter("INFO", Color.GREEN, true) private val WarningFilter: Filter = new Filter("WARNING", Color.ORANGE, true) private val SevereFilter: Filter = new Filter("SEVERE", Color.RED, true) - val DefaultFilters: Seq[Filter] = Seq(FinestFilter, InfoFilter, WarningFilter, SevereFilter) private val DefaultFontSize = 12 @@ -57,6 +55,17 @@ object LogFileSettings { * * Filters define which keywords are relevant for this given log file. * + * @param fileId wraps file reference + * @param selectedLineNumber which line number was selected + * @param firstOpened used to sort log files in tabs + * @param dividerPosition position of divider for this view + * @param fontSize font size to use + * @param filters filters which should be applied + * @param blockSettings settings for the left view + * @param someLogEntryInstantFormat used timestamp format + * @param autoScroll true if 'follow mode' is active + * @param firstVisibleTextCellIndex which index is the first visible on the screen (depending on resolution, window size ...) + * @param lastVisibleTextCellIndex which index is the last visible on the screen (depending on resolution, window size ...) */ case class LogFileSettings(fileId: FileId , selectedLineNumber: Int @@ -68,8 +77,8 @@ case class LogFileSettings(fileId: FileId , someLogEntryInstantFormat: Option[LogEntryInstantFormat] , autoScroll: Boolean , firstVisibleTextCellIndex: Int - , lastVisibleTextCellIndex: Int) extends CanLog { + , lastVisibleTextCellIndex: Int) { - val path: Path = Paths.get(fileId.value).toAbsolutePath + val path: Path = fileId.asPath.toAbsolutePath } \ No newline at end of file diff --git a/app/src/main/scala/app/logorrr/services/LogoRRRServices.scala b/app/src/main/scala/app/logorrr/services/LogoRRRServices.scala index 139b8524..db3a764f 100644 --- a/app/src/main/scala/app/logorrr/services/LogoRRRServices.scala +++ b/app/src/main/scala/app/logorrr/services/LogoRRRServices.scala @@ -1,10 +1,19 @@ package app.logorrr.services import app.logorrr.conf.Settings -import app.logorrr.services.file.FileService +import app.logorrr.services.file.FileIdService import app.logorrr.services.hostservices.LogoRRRHostServices +/** + * Groups settings, acts as a kind of service provider interface + * + * @param settings application settings + * @param hostServices services which need native interfaces which in turn need special capabilities which + * have to be declared on packaging + * @param fileIdService service to lookup file ids, also needed because of security/packaging + * @param isUnderTest helper flag which tells the application it runs in test mode + */ case class LogoRRRServices(settings: Settings -, hostServices: LogoRRRHostServices - , fileOpenService: FileService + , hostServices: LogoRRRHostServices + , fileIdService: FileIdService , isUnderTest: Boolean) diff --git a/app/src/main/scala/app/logorrr/services/file/DefaultFileService.scala b/app/src/main/scala/app/logorrr/services/file/DefaultFileIdService.scala similarity index 73% rename from app/src/main/scala/app/logorrr/services/file/DefaultFileService.scala rename to app/src/main/scala/app/logorrr/services/file/DefaultFileIdService.scala index 78bb6746..2636f2cb 100644 --- a/app/src/main/scala/app/logorrr/services/file/DefaultFileService.scala +++ b/app/src/main/scala/app/logorrr/services/file/DefaultFileIdService.scala @@ -9,9 +9,9 @@ import javafx.stage.Window * * @param getWindow function to determine current window */ -class DefaultFileService(getWindow: () => Window) extends FileService { +class DefaultFileIdService(getWindow: () => Window) extends FileIdService { - override def openFile: Option[FileId] = { + override def provideFileId: Option[FileId] = { new LogoRRRFileChooser("Open log file").performShowAndWait(getWindow()) } diff --git a/app/src/main/scala/app/logorrr/services/file/FileIdService.scala b/app/src/main/scala/app/logorrr/services/file/FileIdService.scala new file mode 100644 index 00000000..bccf55bc --- /dev/null +++ b/app/src/main/scala/app/logorrr/services/file/FileIdService.scala @@ -0,0 +1,13 @@ +package app.logorrr.services.file + +import app.logorrr.io.FileId + +/** + * Trait to inject either Test FileId Services or query the native dialog + */ +trait FileIdService { + + /** returns either None or a valid FileId */ + def provideFileId: Option[FileId] + +} diff --git a/app/src/main/scala/app/logorrr/services/file/FileService.scala b/app/src/main/scala/app/logorrr/services/file/FileService.scala deleted file mode 100644 index b0e22cbb..00000000 --- a/app/src/main/scala/app/logorrr/services/file/FileService.scala +++ /dev/null @@ -1,13 +0,0 @@ -package app.logorrr.services.file - -import app.logorrr.io.FileId - -/** - * Trait to inject either Test File Services or the service which calls the native dialog - */ -trait FileService { - - /** returns either None or a valid FileId */ - def openFile: Option[FileId] - -} diff --git a/app/src/main/scala/app/logorrr/views/UiNodes.scala b/app/src/main/scala/app/logorrr/views/UiNodes.scala index 80731d09..39b990da 100644 --- a/app/src/main/scala/app/logorrr/views/UiNodes.scala +++ b/app/src/main/scala/app/logorrr/views/UiNodes.scala @@ -31,7 +31,7 @@ object UiNodes { val FileMenuCloseAll: UiNode = UiNode("file_menu_close_all") /** quit application */ - val FileMenuQuitApplication: UiNode = UiNode("file_menu_quit_application") + val FileMenuCloseApplication: UiNode = UiNode("file_menu_close_application") /** help menu */ val HelpMenu: UiNode = UiNode("help_menu") diff --git a/app/src/main/scala/app/logorrr/views/logfiletab/LogFileTabContent.scala b/app/src/main/scala/app/logorrr/views/logfiletab/LogFileTabContent.scala index 84f156c5..4aa6912c 100644 --- a/app/src/main/scala/app/logorrr/views/logfiletab/LogFileTabContent.scala +++ b/app/src/main/scala/app/logorrr/views/logfiletab/LogFileTabContent.scala @@ -68,12 +68,7 @@ class LogFileTabContent(fileId: FileId private val opsRegion: OpsRegion = new OpsRegion(opsToolBar, filtersToolBar) - private val pane = { - val s = new SplitPane(blockPane, logTextView) - //s.setStyle("-fx-background-color: #ffa07a;") - s - } - + private val pane = new SplitPane(blockPane, logTextView) def init(): Unit = { divider.setPosition(mutLogFileSettings.getDividerPosition) diff --git a/app/src/main/scala/app/logorrr/views/main/LogoRRRMain.scala b/app/src/main/scala/app/logorrr/views/main/LogoRRRMain.scala index e7335e90..ac26df65 100644 --- a/app/src/main/scala/app/logorrr/views/main/LogoRRRMain.scala +++ b/app/src/main/scala/app/logorrr/views/main/LogoRRRMain.scala @@ -3,7 +3,7 @@ package app.logorrr.views.main import app.logorrr.conf.LogoRRRGlobals import app.logorrr.io.{FileId, IoManager} import app.logorrr.model.LogFileSettings -import app.logorrr.services.file.FileService +import app.logorrr.services.file.FileIdService import app.logorrr.util.{CanLog, JfxUtils} import app.logorrr.views.logfiletab.LogFileTab import javafx.scene.layout.BorderPane @@ -15,7 +15,7 @@ import scala.concurrent.duration.Duration import scala.concurrent.{Await, Future} class LogoRRRMain(closeStage: => Unit - , logoRRRFileOpenService: FileService + , logoRRRFileOpenService: FileIdService , isUnderTest: Boolean) extends BorderPane with CanLog { private val mainTabPane = new MainTabPane diff --git a/app/src/main/scala/app/logorrr/views/main/MainMenuBar.scala b/app/src/main/scala/app/logorrr/views/main/MainMenuBar.scala index 5c5538b2..d05684d8 100644 --- a/app/src/main/scala/app/logorrr/views/main/MainMenuBar.scala +++ b/app/src/main/scala/app/logorrr/views/main/MainMenuBar.scala @@ -1,20 +1,20 @@ package app.logorrr.views.main import app.logorrr.io.FileId -import app.logorrr.services.file.FileService +import app.logorrr.services.file.FileIdService import app.logorrr.util.{CanLog, OsUtil} import app.logorrr.views.menubar.{FileMenu, HelpMenu} import javafx.scene.control.MenuBar -class MainMenuBar(fileOpenService: FileService +class MainMenuBar(fileIdService: FileIdService , openFile: FileId => Unit - , closeAllLogFiles: => Unit + , closeAllFiles: => Unit , closeApplication: => Unit , isUnderTest: Boolean) extends MenuBar with CanLog { - val fileMenu = new FileMenu(isUnderTest, fileOpenService, openFile, closeAllLogFiles, closeApplication) + val fileMenu = new FileMenu(isUnderTest, fileIdService, openFile, closeAllFiles, closeApplication) val helpMenu = new HelpMenu(openFile) setUseSystemMenuBar(OsUtil.isMac && !isUnderTest) diff --git a/app/src/main/scala/app/logorrr/views/menubar/FileMenu.scala b/app/src/main/scala/app/logorrr/views/menubar/FileMenu.scala index bf14462f..f21edffc 100644 --- a/app/src/main/scala/app/logorrr/views/menubar/FileMenu.scala +++ b/app/src/main/scala/app/logorrr/views/menubar/FileMenu.scala @@ -2,7 +2,7 @@ package app.logorrr.views.menubar import app.logorrr.conf.LogoRRRGlobals import app.logorrr.io.FileId -import app.logorrr.services.file.FileService +import app.logorrr.services.file.FileIdService import app.logorrr.util.{CanLog, OsUtil} import app.logorrr.views.UiNodes import javafx.scene.control.{Menu, MenuItem} @@ -30,49 +30,55 @@ class LogoRRRFileChooser(title: String) { object FileMenu { - class OpenMenuItem(fileOpenService: FileService - , openFile: FileId => Unit) + /** + * Menu Item to open a new file + * + * @param fileIdService underlying service to use + * @param openFile function to change global state, add new file to UI ... + */ + class OpenFileMenuItem(fileIdService: FileIdService + , openFile: FileId => Unit) extends MenuItem("Open") with CanLog { setId(UiNodes.FileMenuOpenFile.value) setOnAction(_ => { - fileOpenService.openFile match { + fileIdService.provideFileId match { case Some(fileId) => openFile(fileId) - case None => logTrace("Cancelled open file operation ...") + case None => logTrace("Cancel open file operation ...") } }) } - class CloseAllMenuItem(removeAllLogFiles: => Unit) extends MenuItem("Close All") { + class CloseAllFilesMenuItem(removeAllLogFiles: => Unit) extends MenuItem("Close All") { setId(UiNodes.FileMenuCloseAll.value) setOnAction(_ => removeAllLogFiles) } - class QuitMenuItem(closeApplication: => Unit) extends MenuItem("Quit") { - setId(UiNodes.FileMenuQuitApplication.value) + class CloseApplicationMenuItem(closeApplication: => Unit) extends MenuItem("Quit") { + setId(UiNodes.FileMenuCloseApplication.value) setOnAction(_ => closeApplication) } } class FileMenu(isUnderTest : Boolean - ,fileOpenService: FileService + , fileIdService: FileIdService , openFile: FileId => Unit - , closeAllLogFiles: => Unit - , closeApplication: => Unit) extends Menu("File") with CanLog { + , closeAllFiles: => Unit + , closeApplication: => Unit) extends Menu("File") { setId(UiNodes.FileMenu.value) - lazy val openMenuItem = new FileMenu.OpenMenuItem(fileOpenService, openFile) - lazy val closeAllMenuItem = new FileMenu.CloseAllMenuItem(closeAllLogFiles) - lazy val quitMenuItem = new FileMenu.QuitMenuItem(closeApplication) + lazy val openMenuItem = new FileMenu.OpenFileMenuItem(fileIdService, openFile) + lazy val closeAllMenuItem = new FileMenu.CloseAllFilesMenuItem(closeAllFiles) + lazy val closeApplicationMenuItem = new FileMenu.CloseApplicationMenuItem(closeApplication) def init(): Unit = { getItems.clear() getItems.add(openMenuItem) getItems.add(closeAllMenuItem) if (OsUtil.isLinux || OsUtil.isWin || isUnderTest) { - getItems.add(quitMenuItem) + getItems.add(closeApplicationMenuItem) } } diff --git a/app/src/main/scala/app/logorrr/views/search/Filter.scala b/app/src/main/scala/app/logorrr/views/search/Filter.scala index b52e4816..022c8303 100644 --- a/app/src/main/scala/app/logorrr/views/search/Filter.scala +++ b/app/src/main/scala/app/logorrr/views/search/Filter.scala @@ -1,6 +1,5 @@ package app.logorrr.views.search -import app.logorrr.views.search import javafx.beans.property.SimpleBooleanProperty import javafx.scene.paint.Color import pureconfig.generic.semiauto.{deriveReader, deriveWriter} @@ -55,22 +54,17 @@ class Filter(val pattern: String , override val color: Color , val active: Boolean) extends Fltr(color) { + /** will be bound to the 'selected' state of the filter button */ + private val activeProperty: SimpleBooleanProperty = new SimpleBooleanProperty() - private lazy val activeProperty = new SimpleBooleanProperty(active) + private def isActive: Boolean = activeProperty.get() - def isActive: Boolean = activeProperty.get() + def bind(filterButton: FilterButton): Unit = activeProperty.bind(filterButton.selectedProperty()) - def bind(filterButton: FilterButton): Unit = { - filterButton.selectedProperty().bindBidirectional(activeProperty) - } - - def unbind(button: FilterButton): Unit = { - button.selectedProperty().unbindBidirectional(activeProperty) - } + def unbind(): Unit = activeProperty.unbind() override def matches(searchTerm: String): Boolean = searchTerm.contains(pattern) - def withActive(): Filter = { - new search.Filter(pattern, color, isActive) - } + def withActive(): Filter = new Filter(pattern, color, isActive) + } \ No newline at end of file diff --git a/app/src/main/scala/app/logorrr/views/search/FilterButton.scala b/app/src/main/scala/app/logorrr/views/search/FilterButton.scala index a1496afa..e87572bf 100644 --- a/app/src/main/scala/app/logorrr/views/search/FilterButton.scala +++ b/app/src/main/scala/app/logorrr/views/search/FilterButton.scala @@ -1,7 +1,7 @@ package app.logorrr.views.search import app.logorrr.io.FileId -import app.logorrr.util.HashUtil +import app.logorrr.util.{CanLog, HashUtil} import app.logorrr.views.{UiNode, UiNodeFilterAware} import javafx.beans.{InvalidationListener, Observable} import javafx.scene.control.{ContentDisplay, ToggleButton, Tooltip} @@ -19,7 +19,7 @@ class FilterButton(val fileId: FileId , val filter: Filter , i: Int , updateActiveFilter: () => Unit - , removeFilter: Filter => Unit) extends ToggleButton(filter.pattern) { + , removeFilter: Filter => Unit) extends ToggleButton(filter.pattern) with CanLog { setId(FilterButton.uiNode(fileId, filter).value) setTooltip(new Tooltip(if (i == 1) "one item found" else s"$i items found")) @@ -29,13 +29,23 @@ class FilterButton(val fileId: FileId setGraphic(new RemoveFilterbutton(fileId, filter, removeFilter)) } setSelected(filter.active) +// logTrace(s"Fb: ${fileId.fileName} / ${filter.pattern}: setting selected to ${filter.active}") selectedProperty().addListener(new InvalidationListener { // if any of the buttons changes its selected value, reevaluate predicate // and thus change contents of all views which display filtered List - override def invalidated(observable: Observable): Unit = updateActiveFilter() + override def invalidated(observable: Observable): Unit = { + updateActiveFilter() + } }) - +/* + selectedProperty().addListener(new ChangeListener[lang.Boolean] { + override def changed(observableValue: ObservableValue[_ <: lang.Boolean], t: lang.Boolean, t1: lang.Boolean): Unit = { + val msg = s"Fb: ${fileId.fileName} / ${filter.pattern} Change from ${t} to ${t1}" + logTrace(msg) + } + }) +*/ def isUnclassified: Boolean = filter.isInstanceOf[UnclassifiedFilter] } diff --git a/app/src/main/scala/app/logorrr/views/search/FiltersToolBar.scala b/app/src/main/scala/app/logorrr/views/search/FiltersToolBar.scala index f7e03200..078f62bc 100644 --- a/app/src/main/scala/app/logorrr/views/search/FiltersToolBar.scala +++ b/app/src/main/scala/app/logorrr/views/search/FiltersToolBar.scala @@ -49,7 +49,7 @@ class FiltersToolBar(fileId: FileId } - /** if list is changed in any way, react to this event and either add or remove filter from UI */ + /** if filter list is changed in any way, react to this event and either add or remove filter from UI */ private def processFiltersChange(change: ListChangeListener.Change[_ <: Filter]): Unit = { while (change.next()) { if (change.wasAdded()) { @@ -67,7 +67,7 @@ class FiltersToolBar(fileId: FileId } private def updateUnclassified(): Unit = { - val unclassified = new UnclassifiedFilter(filterButtons.keySet) + val unclassified = UnclassifiedFilter(filterButtons.keySet) updateOccurrences(unclassified) val filterButton = new FilterButton(fileId, unclassified, occurrences(unclassified), updateActiveFilter, removeFilter) someUnclassifiedFilter.foreach(ftb => getItems.remove(ftb._2)) @@ -78,15 +78,15 @@ class FiltersToolBar(fileId: FileId private def addFilterButton(filter: Filter): Unit = { updateOccurrences(filter) - val searchTag = new FilterButton(fileId, filter, occurrences(filter), updateActiveFilter, removeFilter) - filter.bind(searchTag) - getItems.add(searchTag) - filterButtons = filterButtons.updated(filter, searchTag) + val filterButton = new FilterButton(fileId, filter, occurrences(filter), updateActiveFilter, removeFilter) + filter.bind(filterButton) + getItems.add(filterButton) + filterButtons = filterButtons.updated(filter, filterButton) } private def removeFilterButton(filter: Filter): Unit = { val button = filterButtons(filter) - filter.unbind(button) + filter.unbind() getItems.remove(button) filterButtons = filterButtons.removed(filter) } diff --git a/app/src/main/scala/app/logorrr/views/search/UnclassifiedFilter.scala b/app/src/main/scala/app/logorrr/views/search/UnclassifiedFilter.scala index 423e9ced..262691e3 100644 --- a/app/src/main/scala/app/logorrr/views/search/UnclassifiedFilter.scala +++ b/app/src/main/scala/app/logorrr/views/search/UnclassifiedFilter.scala @@ -1,6 +1,11 @@ package app.logorrr.views.search -class UnclassifiedFilter(filters: Set[Filter]) extends +/** + * Match everything except given filters + * + * @param filters a set of filters to build the complement + */ +case class UnclassifiedFilter(filters: Set[Filter]) extends Filter("Unclassified", Filter.unClassifiedFilterColor, true) { override def matches(searchTerm: String): Boolean = !filters.exists(_.matches(searchTerm)) diff --git a/docs/src/main/scala/app/logorrr/docs/ScreenShotterApp.scala b/docs/src/main/scala/app/logorrr/docs/ScreenShotterApp.scala index cfe5c8c2..0cc851ca 100644 --- a/docs/src/main/scala/app/logorrr/docs/ScreenShotterApp.scala +++ b/docs/src/main/scala/app/logorrr/docs/ScreenShotterApp.scala @@ -6,7 +6,7 @@ import app.logorrr.docs.Area._ import app.logorrr.io.Fs import app.logorrr.meta.AppMeta import app.logorrr.services.LogoRRRServices -import app.logorrr.services.file.DefaultFileService +import app.logorrr.services.file.DefaultFileIdService import app.logorrr.services.hostservices.NativeHostServices import app.logorrr.util.CanLog import app.logorrr.{LogoRRRApp, LogoRRRNative} @@ -51,7 +51,7 @@ class ScreenShotterApp extends javafx.application.Application with CanLog { val services = LogoRRRServices(updatedSettings , new NativeHostServices(getHostServices) - , new DefaultFileService(() => stage.getScene.getWindow) + , new DefaultFileIdService(() => stage.getScene.getWindow) , isUnderTest = false) LogoRRRApp.start(stage, services)