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. override def selectLast(): Unit = {
  148. clearSelectionFormatting
  149. this.selectedIndexes.clear()
  150. setSelectionFormatting(this.parentTilePane.getChildren.size()-1)
  151. this.selectedIndexes.add(this.parentTilePane.getChildren.size()-1)
  152. }
  153. override def clearAndSelect(index: Int): Unit = {
  154. clearSelectionFormatting
  155. this.selectedIndexes.clear()
  156. setSelectionFormatting(index)
  157. this.selectedIndexes.add(index)
  158. }
  159. override def clearSelection(index: Int): Unit = {
  160. val i = this.selectedIndexes.indexOf(index)
  161. if (i >= 0) {
  162. clearSelectionFormatting(index)
  163. this.selectedIndexes.remove(i)
  164. }
  165. }
  166. override def clearSelection(): Unit = {
  167. clearSelectionFormatting
  168. this.selectedIndexes.clear()
  169. }
  170. override def selectPrevious(): Unit = {
  171. if (this.selectedIndexes.size == 1) {
  172. val currentIndex = this.selectedIndexes.get(0)
  173. val nextIndex = if (currentIndex < 1) 0 else currentIndex - 1
  174. this.selectedIndexes.set(0, nextIndex)
  175. }
  176. }
  177. override def selectNext(): Unit = {
  178. if (this.selectedIndexes.size == 1) {
  179. val currentIndex = this.selectedIndexes.get(0)
  180. val nextIndex = if (currentIndex >= this.parentTilePane.getChildren.size-1) this.parentTilePane.getChildren.size-1 else currentIndex + 1
  181. this.selectedIndexes.set(0, nextIndex)
  182. }
  183. }
  184. override def select(index: Int): Unit = {
  185. //can only select once
  186. if (! this.selectedIndexes.contains(index)) {
  187. setSelectionFormatting(index)
  188. this.selectedIndexes.add(index)
  189. }
  190. }
  191. override def select(obj: ImageTile): Unit = {
  192. if (this.parentTilePane.getChildren.contains(obj)) {
  193. clearSelectionFormatting
  194. this.selectedIndexes.clear()
  195. setSelectionFormatting(obj)
  196. this.selectedIndexes.add(this.parentTilePane.getChildren.indexOf(obj))
  197. }
  198. }
  199. override def isEmpty: Boolean = {
  200. this.parentTilePane.getChildren.isEmpty
  201. }
  202. override def isSelected(index: Int): Boolean = {
  203. this.selectedIndexes.contains(index)
  204. }
  205. private def clearSelectionFormatting(index: Int) = {
  206. val tile = this.parentTilePane.getChildren.get(index).asInstanceOf[VBox]
  207. tile.setBorder(Border.EMPTY)
  208. }
  209. private def clearSelectionFormatting() = {
  210. val iterator = this.parentTilePane.getChildren.iterator()
  211. while (iterator.hasNext) {
  212. //remove the selection styling
  213. val imageTile: VBox = iterator.next().asInstanceOf[VBox]
  214. imageTile.setBorder(Border.EMPTY)
  215. }
  216. }
  217. private def setSelectionFormatting(index: Int): Unit = {
  218. setSelectionFormatting(this.parentTilePane.getChildren.get(index).asInstanceOf[ImageTile])
  219. }
  220. private def setSelectionFormatting(imageTile: ImageTile) = {
  221. val borderStroke = new BorderStroke(Color.BLUE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY,BorderStroke.THIN)
  222. imageTile.asInstanceOf[VBox].setBorder(new Border(borderStroke))
  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. }