From 1c239d435bb406986dcd88eae2cda25758a9f6da Mon Sep 17 00:00:00 2001 From: death-claw <53543762+death-claw@users.noreply.github.com> Date: Thu, 14 Sep 2023 21:53:35 +0100 Subject: [PATCH] fixes #122 (#125) --- .../me/mnlr/vripper/VripperGuiApplication.kt | 8 + .../me/mnlr/vripper/view/CommonUtils.kt | 20 +++ .../me/mnlr/vripper/view/ProgressTableCell.kt | 47 ++++++ .../vripper/view/tables/ImagesTableView.kt | 55 ++++--- .../vripper/view/tables/PostsTableView.kt | 147 ++++++++++-------- .../view/tables/ThreadSelectionTableView.kt | 24 ++- .../vripper/view/tables/ThreadTableView.kt | 47 ++++-- .../src/main/resources/open-in-browser.png | Bin 0 -> 702 bytes 8 files changed, 238 insertions(+), 110 deletions(-) create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/CommonUtils.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/ProgressTableCell.kt create mode 100644 vripper-gui/src/main/resources/open-in-browser.png diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/VripperGuiApplication.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/VripperGuiApplication.kt index b109966d..834bc82d 100644 --- a/vripper-gui/src/main/kotlin/me/mnlr/vripper/VripperGuiApplication.kt +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/VripperGuiApplication.kt @@ -25,6 +25,10 @@ class VripperGuiApplication : App( private lateinit var context: ConfigurableApplicationContext //We are going to set application context here private var initialized = false + + init { + APP_INSTANCE = this + } override fun start(stage: Stage) { with(stage) { width = 1200.0 @@ -73,6 +77,10 @@ class VripperGuiApplication : App( } exitProcess(0) } + + companion object { + lateinit var APP_INSTANCE: Application + } } fun main(args: Array) { diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/CommonUtils.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/CommonUtils.kt new file mode 100644 index 00000000..09aa8bd7 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/CommonUtils.kt @@ -0,0 +1,20 @@ +package me.mnlr.vripper.view + +import com.sun.jna.WString +import me.mnlr.vripper.VripperGuiApplication +import me.mnlr.vripper.utils.Shell32 + +fun openFileDirectory(path: String) { + val os = System.getProperty("os.name") + if(os.contains("Windows")) { + Shell32.INSTANCE.ShellExecuteW(null, WString("open"), WString(path), null, null, 1) + } else if(os.contains("Linux")) { + Runtime.getRuntime().exec("xdg-open $path") + } else if(os.contains("Mac")) { + Runtime.getRuntime().exec("open -R $path") + } +} + +fun openLink(path: String) { + VripperGuiApplication.APP_INSTANCE.hostServices.showDocument(path) +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/ProgressTableCell.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/ProgressTableCell.kt new file mode 100644 index 00000000..8a09bd3d --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/ProgressTableCell.kt @@ -0,0 +1,47 @@ +package me.mnlr.vripper.view + +import javafx.beans.value.ObservableValue +import javafx.event.EventHandler +import javafx.scene.control.ProgressBar +import javafx.scene.control.TableCell +import javafx.scene.control.TableColumn +import javafx.scene.input.MouseEvent + +class ProgressTableCell : TableCell() { + + /* ************************************************************************* + * * + * Fields * + * * + **************************************************************************/ + private var progressBar: ProgressBar + + private var observable: ObservableValue? = null + + init { + styleClass.add("progress-bar-table-cell") + progressBar = ProgressBar() + progressBar.maxWidth = Double.MAX_VALUE + } + + fun setOnMouseClick(clickEvent: EventHandler) { + progressBar.onMouseClicked = clickEvent + } + + override fun updateItem(item: Double?, empty: Boolean) { + super.updateItem(item, empty) + if (empty) { + setGraphic(null) + } else { + progressBar.progressProperty().unbind() + val column: TableColumn? = tableColumn + observable = column?.getCellObservableValue(index) + if (observable != null) { + progressBar.progressProperty().bind(observable) + } else if (item != null) { + progressBar.progress = item + } + setGraphic(progressBar) + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ImagesTableView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ImagesTableView.kt index cebf913d..411540e1 100644 --- a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ImagesTableView.kt +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ImagesTableView.kt @@ -3,12 +3,16 @@ package me.mnlr.vripper.view.tables import javafx.collections.FXCollections import javafx.collections.ObservableList import javafx.geometry.Pos -import javafx.scene.control.TableView +import javafx.scene.control.* +import javafx.scene.image.ImageView import javafx.scene.input.MouseButton +import javafx.util.Callback import me.mnlr.vripper.controller.ImageController import me.mnlr.vripper.event.Event import me.mnlr.vripper.event.EventBus import me.mnlr.vripper.model.ImageModel +import me.mnlr.vripper.view.ProgressTableCell +import me.mnlr.vripper.view.openLink import me.mnlr.vripper.view.FxScheduler import tornadofx.* @@ -58,6 +62,23 @@ class ImagesTableView : Fragment("Photos") { override val root = vbox(alignment = Pos.CENTER_RIGHT) { tableView = tableview(items) { + setRowFactory { + val tableRow = TableRow() + val urlItem = MenuItem("Open link").apply { + setOnAction { + openLink(tableRow.item.url) + } + graphic = ImageView("open-in-browser.png").apply { + fitWidth = 18.0 + fitHeight = 18.0 + } + } + val contextMenu = ContextMenu() + contextMenu.items.addAll(urlItem) + tableRow.contextMenuProperty().bind(tableRow.emptyProperty() + .map { empty -> if (empty) null else contextMenu }) + tableRow + } column("Index", ImageModel::indexProperty) { sortOrder.add(this) } @@ -65,31 +86,21 @@ class ImagesTableView : Fragment("Photos") { prefWidth = 200.0 } column("Progress", ImageModel::progressProperty) { - cellFormat { - addClass(Stylesheet.progressBarTableCell) - graphic = cache { - progressbar(itemProperty().doubleBinding { it?.toDouble() ?: 0.0 }) { - setOnMouseClicked { - when (it.clickCount) { - 1 -> { - this@tableview.requestFocus() - this@tableview.focusModel.focus(this@cellFormat.tableRow.index) - if (it.isControlDown && it.button.equals(MouseButton.PRIMARY)) { - if (this@tableview.selectionModel.isSelected(this@cellFormat.tableRow.index)) { - this@tableview.selectionModel.clearSelection(this@cellFormat.tableRow.index) - } else { - this@tableview.selectionModel.select(this@cellFormat.tableRow.index) - } - } else if (it.button.equals(MouseButton.PRIMARY)) { - this@tableview.selectionModel.clearSelection() - this@tableview.selectionModel.select(this@cellFormat.tableRow.index) - } - } + cellFactory = Callback { + val cell = ProgressTableCell() + cell.setOnMouseClick { + when (it.clickCount) { + 1 -> { + this@tableview.requestFocus() + this@tableview.focusModel.focus(cell.tableRow.index) + if (it.button.equals(MouseButton.PRIMARY)) { + this@tableview.selectionModel.clearSelection() + this@tableview.selectionModel.select(cell.tableRow.index) } } - useMaxWidth = true } } + cell as TableCell } } column("Status", ImageModel::statusProperty) diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/PostsTableView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/PostsTableView.kt index 722e78ba..bf61a444 100644 --- a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/PostsTableView.kt +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/PostsTableView.kt @@ -1,16 +1,18 @@ package me.mnlr.vripper.view.tables -import com.sun.jna.WString import javafx.collections.FXCollections import javafx.collections.ObservableList import javafx.scene.control.* import javafx.scene.image.ImageView import javafx.scene.input.MouseButton +import javafx.util.Callback import me.mnlr.vripper.controller.PostController import me.mnlr.vripper.event.Event import me.mnlr.vripper.event.EventBus import me.mnlr.vripper.model.PostModel -import me.mnlr.vripper.utils.Shell32 +import me.mnlr.vripper.view.ProgressTableCell +import me.mnlr.vripper.view.openFileDirectory +import me.mnlr.vripper.view.openLink import me.mnlr.vripper.view.FxScheduler import tornadofx.* @@ -81,54 +83,69 @@ class PostsTableView : View() { } } - val contextMenu = ContextMenu() - val startItem = MenuItem("Start") - startItem.setOnAction { - startSelected() + val startItem = MenuItem("Start").apply { + setOnAction { + startSelected() + } + graphic = ImageView("play.png").apply { + fitWidth = 18.0 + fitHeight = 18.0 + } } - val playIcon = ImageView("play.png") - playIcon.fitWidth = 18.0 - playIcon.fitHeight = 18.0 - startItem.graphic = playIcon - - val stopItem = MenuItem("Stop") - stopItem.setOnAction { - stopSelected() + + val stopItem = MenuItem("Stop").apply { + setOnAction { + stopSelected() + } + graphic = ImageView("pause.png").apply { + fitWidth = 18.0 + fitHeight = 18.0 + } } - val stopIcon = ImageView("pause.png") - stopIcon.fitWidth = 18.0 - stopIcon.fitHeight = 18.0 - stopItem.graphic = stopIcon - - val deleteItem = MenuItem("Delete") - deleteItem.setOnAction { - deleteSelected() + + val deleteItem = MenuItem("Delete").apply { + setOnAction { + deleteSelected() + } + graphic = ImageView("trash.png").apply { + fitWidth = 18.0 + fitHeight = 18.0 + } + } + + val detailsItem = MenuItem("Images").apply { + setOnAction { + openPhotos(tableRow.item.postId) + } + graphic = ImageView("details.png").apply { + fitWidth = 18.0 + fitHeight = 18.0 + } } - val deleteIcon = ImageView("trash.png") - deleteIcon.fitWidth = 18.0 - deleteIcon.fitHeight = 18.0 - deleteItem.graphic = deleteIcon - - val detailsItem = MenuItem("Images") - detailsItem.setOnAction { - openPhotos(tableRow.item.postId) + + val locationItem = MenuItem("Open containing folder").apply { + setOnAction { + openFileDirectory(tableRow.item.path) + } + graphic = ImageView("file-explorer.png").apply { + fitWidth = 18.0 + fitHeight = 18.0 + } } - val detailsIcon = ImageView("details.png") - detailsIcon.fitWidth = 18.0 - detailsIcon.fitHeight = 18.0 - detailsItem.graphic = detailsIcon - - val locationItem = MenuItem("Open Download Directory") - locationItem.setOnAction { - openFileDirectory(tableRow.item.path) + + val urlItem = MenuItem("Open link").apply { + setOnAction { + openLink(tableRow.item.url) + } + graphic = ImageView("open-in-browser.png").apply { + fitWidth = 18.0 + fitHeight = 18.0 + } } - val locationIcon = ImageView("file-explorer.png") - locationIcon.fitWidth = 18.0 - locationIcon.fitHeight = 18.0 - locationItem.graphic = locationIcon + val contextMenu = ContextMenu() contextMenu.items.addAll( - startItem, stopItem, deleteItem, SeparatorMenuItem(), detailsItem, locationItem + startItem, stopItem, deleteItem, SeparatorMenuItem(), detailsItem, locationItem, urlItem ) tableRow.contextMenuProperty() .bind(tableRow.emptyProperty().map { empty -> if (empty) null else contextMenu }) @@ -138,32 +155,41 @@ class PostsTableView : View() { prefWidth = 300.0 } column("Progress", PostModel::progressProperty) { - cellFormat { - addClass(Stylesheet.progressBarTableCell) - graphic = cache { - progressbar(itemProperty().doubleBinding { it?.toDouble() ?: 0.0 }) { - setOnMouseClicked { + cellFactory = Callback { + val cell = ProgressTableCell() + cell.setOnMouseClick { + when (it.button) { + MouseButton.SECONDARY -> { + this@tableview.requestFocus() + this@tableview.focusModel.focus(cell.tableRow.index) + if (!this@tableview.selectionModel.isSelected(cell.tableRow.index)) { + this@tableview.selectionModel.clearSelection() + this@tableview.selectionModel.select(cell.tableRow.index) + } + } + MouseButton.PRIMARY -> { when (it.clickCount) { 1 -> { this@tableview.requestFocus() - this@tableview.focusModel.focus(this@cellFormat.tableRow.index) + this@tableview.focusModel.focus(cell.tableRow.index) if (it.isControlDown && it.button.equals(MouseButton.PRIMARY)) { - if (this@tableview.selectionModel.isSelected(this@cellFormat.tableRow.index)) { - this@tableview.selectionModel.clearSelection(this@cellFormat.tableRow.index) + if (this@tableview.selectionModel.isSelected(cell.tableRow.index)) { + this@tableview.selectionModel.clearSelection(cell.tableRow.index) } else { - this@tableview.selectionModel.select(this@cellFormat.tableRow.index) + this@tableview.selectionModel.select(cell.tableRow.index) } } else if (it.button.equals(MouseButton.PRIMARY)) { this@tableview.selectionModel.clearSelection() - this@tableview.selectionModel.select(this@cellFormat.tableRow.index) + this@tableview.selectionModel.select(cell.tableRow.index) } } - 2 -> openPhotos(this@cellFormat.tableRow.item.postId) + 2 -> openPhotos(cell.tableRow.item.postId) } } - useMaxWidth = true + else -> {} } } + cell as TableCell } } column("Status", PostModel::statusProperty) @@ -177,17 +203,6 @@ class PostsTableView : View() { } } - private fun openFileDirectory(path: String) { - val os = System.getProperty("os.name") - if(os.contains("Windows")) { - Shell32.INSTANCE.ShellExecuteW(null, WString("open"), WString(path), null, null, 1) - } else if(os.contains("Linux")) { - Runtime.getRuntime().exec("xdg-open $path") - } else if(os.contains("Mac")) { - Runtime.getRuntime().exec("open -R $path") - } - } - fun deleteSelected() { val postIdList = tableView.selectionModel.selectedItems.map { it.postId } confirm( diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadSelectionTableView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadSelectionTableView.kt index 6c9e27d9..cb1debf9 100644 --- a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadSelectionTableView.kt +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadSelectionTableView.kt @@ -3,12 +3,12 @@ package me.mnlr.vripper.view.tables import javafx.collections.FXCollections import javafx.collections.ObservableList import javafx.geometry.Pos -import javafx.scene.control.Label -import javafx.scene.control.SelectionMode -import javafx.scene.control.TableRow -import javafx.scene.control.TableView +import javafx.scene.control.* +import javafx.scene.image.ImageView +import javafx.scene.input.MouseButton import me.mnlr.vripper.controller.ThreadController import me.mnlr.vripper.model.ThreadSelectionModel +import me.mnlr.vripper.view.openLink import tornadofx.* class ThreadSelectionTableView : Fragment("Thread") { @@ -37,12 +37,26 @@ class ThreadSelectionTableView : Fragment("Thread") { val tableRow = TableRow() tableRow.setOnMouseClicked { - if (it.clickCount == 2 && tableRow.item != null) { + if (it.button.equals(MouseButton.PRIMARY) && it.clickCount == 2 && tableRow.item != null) { threadController.download(listOf(tableRow.item)) close() } } + val urlItem = MenuItem("Open link").apply { + setOnAction { + openLink(tableRow.item.url) + } + graphic = ImageView("open-in-browser.png").apply { + fitWidth = 18.0 + fitHeight = 18.0 + } + } + val contextMenu = ContextMenu() + contextMenu.items.addAll(urlItem) + tableRow.contextMenuProperty().bind(tableRow.emptyProperty() + .map { empty -> if (empty) null else contextMenu }) + tableRow } column("Post Index", ThreadSelectionModel::indexProperty) { diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadTableView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadTableView.kt index b5d42846..2fb56bae 100644 --- a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadTableView.kt +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadTableView.kt @@ -8,6 +8,7 @@ import me.mnlr.vripper.controller.ThreadController import me.mnlr.vripper.event.Event import me.mnlr.vripper.event.EventBus import me.mnlr.vripper.model.ThreadModel +import me.mnlr.vripper.view.openLink import me.mnlr.vripper.view.FxScheduler import tornadofx.* @@ -82,26 +83,38 @@ class ThreadTableView : View() { } } - val contextMenu = ContextMenu() - val selectItem = MenuItem("Select posts") - selectItem.setOnAction { - selectPosts(tableRow.item.threadId) + val selectItem = MenuItem("Select posts").apply { + setOnAction { + selectPosts(tableRow.item.threadId) + } + graphic = ImageView("popup.png").apply { + fitWidth = 18.0 + fitHeight = 18.0 + } + } + + val urlItem = MenuItem("Open link").apply { + setOnAction { + openLink(tableRow.item.link) + } + graphic = ImageView("open-in-browser.png").apply { + fitWidth = 18.0 + fitHeight = 18.0 + } } - val selectIcon = ImageView("popup.png") - selectIcon.fitWidth = 18.0 - selectIcon.fitHeight = 18.0 - selectItem.graphic = selectIcon - - val deleteItem = MenuItem("Delete") - deleteItem.setOnAction { - deleteSelected() + + val deleteItem = MenuItem("Delete").apply { + setOnAction { + deleteSelected() + } + graphic = ImageView("trash.png").apply { + fitWidth = 18.0 + fitHeight = 18.0 + } } - val deleteIcon = ImageView("trash.png") - deleteIcon.fitWidth = 18.0 - deleteIcon.fitHeight = 18.0 - deleteItem.graphic = deleteIcon - contextMenu.items.addAll(selectItem, SeparatorMenuItem(), deleteItem) + val contextMenu = ContextMenu() + contextMenu.items.addAll(selectItem, urlItem, SeparatorMenuItem(), deleteItem) tableRow.contextMenuProperty().bind(tableRow.emptyProperty() .map { empty -> if (empty) null else contextMenu }) tableRow diff --git a/vripper-gui/src/main/resources/open-in-browser.png b/vripper-gui/src/main/resources/open-in-browser.png new file mode 100644 index 0000000000000000000000000000000000000000..c76f4d1b530739bb428396fa74fa5409b2fcd114 GIT binary patch literal 702 zcmV;v0zv(WP)bhbSBhRaht@7>YoiVk{IAotbLy5LW9?uCFDgY$FJ3uejXRF1Nu){O zJn-Bc(sQ2Q`@W}#qKJryh+L^qDAd-)$1ZpA(TYDD1BYDphoec9#WVo=p@YEzcQk1l zz~RcnZ(g_1fnOVhMohI{>FIzA?f#ed@w%Zvaf(`m!y2 z)04BS?gqHC@C^8>ARf{ zjF|?QDM6bKVX_nGg+(vz*6NQ;eXtf>(aI#BkDAhLh?HCw`(i@h;7Z k{R}g|%juj%MAV=46Tk>5xjfV`k^lez07*qoM6N<$g8Ydun*aa+ literal 0 HcmV?d00001