You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

284 lines
8.3 KiB

  1. package com.sothr.imagetools.ui.component
  2. import java.util
  3. import javafx.application.Platform
  4. import javafx.collections.{ModifiableObservableListBase, ObservableList}
  5. import javafx.event.{ActionEvent, EventHandler}
  6. import javafx.geometry.Side
  7. import javafx.scene.Node
  8. import javafx.scene.control.{ContextMenu, MenuItem, MultipleSelectionModel}
  9. import javafx.scene.input.ContextMenuEvent
  10. import javafx.scene.layout._
  11. import javafx.scene.paint.Color
  12. import com.sothr.imagetools.engine.AppConfig
  13. import com.sothr.imagetools.ui.controller.AppController
  14. import grizzled.slf4j.Logging
  15. import scala.concurrent.ExecutionContext.Implicits.global
  16. import scala.concurrent._
  17. import scala.util.{Failure, Success}
  18. /**
  19. * Custom Tile Pane with a multi selection model
  20. *
  21. * Created by drew on 8/29/14.
  22. */
  23. class ImageTilePane extends TilePane with Logging {
  24. val selectionModel = new ImageTilePaneSelectionModel[ImageTile](this)
  25. def handleContextMenu(event: ContextMenuEvent) = {
  26. //Build and show a context menu
  27. debug("Context Menu Request Received")
  28. val numSelected = this.selectionModel.getSelectedIndices.size()
  29. if (numSelected > 0) {
  30. if (numSelected == 1) {
  31. val contextMenu = getSingleSelectionContextMenu
  32. debug("Showing context menu")
  33. contextMenu.show(event.getTarget.asInstanceOf[Node], Side.RIGHT, 0d, 0d)
  34. } else {
  35. val contextMenu = getMulipleSelectionContextMenu
  36. debug("Showing context menu")
  37. contextMenu.show(event.getTarget.asInstanceOf[Node], Side.RIGHT, 0d, 0d)
  38. }
  39. }
  40. }
  41. def getSingleSelectionContextMenu: ContextMenu = {
  42. debug("Building single-selection context menu")
  43. val contextMenu = new ContextMenu()
  44. val delete = new MenuItem("Delete")
  45. delete.setOnAction(new EventHandler[ActionEvent]() {
  46. def handle(e: ActionEvent) = {
  47. debug("Requesting Single Delete")
  48. deleteSelected()
  49. }
  50. })
  51. contextMenu.getItems.addAll(delete)
  52. contextMenu
  53. }
  54. def getMulipleSelectionContextMenu: ContextMenu = {
  55. debug("Building multi-selection context menu")
  56. val contextMenu = new ContextMenu()
  57. val delete = new MenuItem("Delete")
  58. delete.setOnAction(new EventHandler[ActionEvent]() {
  59. def handle(e: ActionEvent) = {
  60. debug("Requesting Multiple Delete")
  61. deleteSelected()
  62. }
  63. })
  64. contextMenu.getItems.addAll(delete)
  65. contextMenu
  66. }
  67. def imageSelected(imageTile: ImageTile) = {
  68. this.selectionModel.clearAndSelect(this.getChildren.indexOf(imageTile))
  69. }
  70. def addImageSelected(imageTile: ImageTile) = {
  71. this.selectionModel.select(this.getChildren.indexOf(imageTile))
  72. }
  73. def removeImageSelected(imageTile: ImageTile) = {
  74. this.selectionModel.clearSelection(this.getChildren.indexOf(imageTile))
  75. }
  76. def clearSelection() = {
  77. this.selectionModel.clearSelection()
  78. }
  79. //request deletion of selected images
  80. def deleteSelected() = {
  81. val f: Future[Unit] = Future {
  82. val selected = this.selectionModel.getSelectedItems
  83. val iterator = selected.iterator()
  84. while (iterator.hasNext) {
  85. val item = iterator.next()
  86. val imageTile = item.asInstanceOf[ImageTile]
  87. val data = imageTile.imageData
  88. val controller = AppConfig.getFxmlLoader.getController[AppController]()
  89. controller.engine.deleteImage(data)
  90. }
  91. val pane = this
  92. Platform.runLater(new Runnable() {
  93. override def run(): Unit = {
  94. //remove from the current panel
  95. pane.getChildren.removeAll(selected)
  96. clearSelection()
  97. }
  98. })
  99. }
  100. f onComplete {
  101. case Success(a) => info("Successfully deleted files")
  102. case Failure(f) => error("Failed to delete files", f)
  103. }
  104. }
  105. }
  106. /**
  107. * Multiple selection model for ImageTilePane
  108. *
  109. */
  110. class ImageTilePaneSelectionModel[ImageTile](parentTilePane: ImageTilePane) extends MultipleSelectionModel[ImageTile] {
  111. val selectedIndexes: ObservableList[Integer] = new ArrayObservableList[Integer]()
  112. override def getSelectedIndices: ObservableList[Integer] = {
  113. this.selectedIndexes
  114. }
  115. override def getSelectedItems: ObservableList[ImageTile] = {
  116. val selected = new ArrayObservableList[ImageTile]()
  117. val iterator = selectedIndexes.iterator()
  118. while (iterator.hasNext) {
  119. selected.add(this.parentTilePane.getChildren.get(iterator.next()).asInstanceOf[ImageTile])
  120. }
  121. selected
  122. }
  123. override def selectIndices(index: Int, indices: Int*): Unit = {
  124. clearSelectionFormatting
  125. this.selectedIndexes.clear()
  126. setSelectionFormatting(index)
  127. this.selectedIndexes.add(index)
  128. for (i <- indices) {
  129. setSelectionFormatting(i)
  130. this.selectedIndexes.add(i)
  131. }
  132. }
  133. override def selectAll(): Unit = {
  134. clearSelectionFormatting
  135. this.selectedIndexes.clear()
  136. for (index <- 0 until this.parentTilePane.getChildren.size()) {
  137. setSelectionFormatting(index)
  138. this.selectedIndexes.add(index)
  139. }
  140. }
  141. override def selectFirst(): Unit = {
  142. clearSelectionFormatting
  143. this.selectedIndexes.clear()
  144. setSelectionFormatting(0)
  145. this.selectedIndexes.add(0)
  146. }
  147. private def clearSelectionFormatting() = {
  148. val iterator = this.parentTilePane.getChildren.iterator()
  149. while (iterator.hasNext) {
  150. //remove the selection styling
  151. val imageTile: VBox = iterator.next().asInstanceOf[VBox]
  152. imageTile.setBorder(Border.EMPTY)
  153. }
  154. }
  155. private def setSelectionFormatting(index: Int): Unit = {
  156. setSelectionFormatting(this.parentTilePane.getChildren.get(index).asInstanceOf[ImageTile])
  157. }
  158. private def setSelectionFormatting(imageTile: ImageTile) = {
  159. val borderStroke = new BorderStroke(Color.BLUE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderStroke.THIN)
  160. imageTile.asInstanceOf[VBox].setBorder(new Border(borderStroke))
  161. }
  162. override def selectLast(): Unit = {
  163. clearSelectionFormatting
  164. this.selectedIndexes.clear()
  165. setSelectionFormatting(this.parentTilePane.getChildren.size() - 1)
  166. this.selectedIndexes.add(this.parentTilePane.getChildren.size() - 1)
  167. }
  168. override def clearAndSelect(index: Int): Unit = {
  169. clearSelectionFormatting
  170. this.selectedIndexes.clear()
  171. setSelectionFormatting(index)
  172. this.selectedIndexes.add(index)
  173. }
  174. override def clearSelection(index: Int): Unit = {
  175. val i = this.selectedIndexes.indexOf(index)
  176. if (i >= 0) {
  177. clearSelectionFormatting(index)
  178. this.selectedIndexes.remove(i)
  179. }
  180. }
  181. private def clearSelectionFormatting(index: Int) = {
  182. val tile = this.parentTilePane.getChildren.get(index).asInstanceOf[VBox]
  183. tile.setBorder(Border.EMPTY)
  184. }
  185. override def clearSelection(): Unit = {
  186. clearSelectionFormatting
  187. this.selectedIndexes.clear()
  188. }
  189. override def selectPrevious(): Unit = {
  190. if (this.selectedIndexes.size == 1) {
  191. val currentIndex = this.selectedIndexes.get(0)
  192. val nextIndex = if (currentIndex < 1) 0 else currentIndex - 1
  193. this.selectedIndexes.set(0, nextIndex)
  194. }
  195. }
  196. override def selectNext(): Unit = {
  197. if (this.selectedIndexes.size == 1) {
  198. val currentIndex = this.selectedIndexes.get(0)
  199. val nextIndex = if (currentIndex >= this.parentTilePane.getChildren.size - 1) this.parentTilePane.getChildren.size - 1 else currentIndex + 1
  200. this.selectedIndexes.set(0, nextIndex)
  201. }
  202. }
  203. override def select(index: Int): Unit = {
  204. //can only select once
  205. if (!this.selectedIndexes.contains(index)) {
  206. setSelectionFormatting(index)
  207. this.selectedIndexes.add(index)
  208. }
  209. }
  210. override def select(obj: ImageTile): Unit = {
  211. if (this.parentTilePane.getChildren.contains(obj)) {
  212. clearSelectionFormatting
  213. this.selectedIndexes.clear()
  214. setSelectionFormatting(obj)
  215. this.selectedIndexes.add(this.parentTilePane.getChildren.indexOf(obj))
  216. }
  217. }
  218. override def isEmpty: Boolean = {
  219. this.parentTilePane.getChildren.isEmpty
  220. }
  221. override def isSelected(index: Int): Boolean = {
  222. this.selectedIndexes.contains(index)
  223. }
  224. }
  225. class ArrayObservableList[E] extends ModifiableObservableListBase[E] {
  226. val delegate: util.ArrayList[E] = new util.ArrayList[E]()
  227. def get(index: Int): E = {
  228. delegate.get(index)
  229. }
  230. def size = {
  231. delegate.size
  232. }
  233. def doAdd(index: Int, element: E) = {
  234. delegate.add(index, element)
  235. }
  236. def doSet(index: Int, element: E): E = {
  237. delegate.set(index, element)
  238. }
  239. def doRemove(index: Int): E = {
  240. delegate.remove(index)
  241. }
  242. }