diff --git a/engine/src/main/java/com/sothr/imagetools/engine/AppConfig.java b/engine/src/main/java/com/sothr/imagetools/engine/AppConfig.java index 94ff970..ae580ba 100644 --- a/engine/src/main/java/com/sothr/imagetools/engine/AppConfig.java +++ b/engine/src/main/java/com/sothr/imagetools/engine/AppConfig.java @@ -94,6 +94,7 @@ public class AppConfig { String message = fromFile ? "From File" : "From Defaults"; logger.info(String.format("Configured Logger %s", message)); logger.info(String.format("Detected Version: %s of Image Tools", PropertiesService.getVersion().toString())); + logger.info(String.format("Running on %s, %s, %s",PropertiesService.OS(), PropertiesService.OS_VERSION(),PropertiesService.OS_ARCH())); } //Only configure logging from the default file once diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertiesService.scala b/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertiesService.scala index e733248..d4372b5 100644 --- a/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertiesService.scala +++ b/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertiesService.scala @@ -38,6 +38,11 @@ object PropertiesService extends Logging { var pHashWeight = 0.0f var usePhash = false + //OS information + val OS = System.getProperty("os.name", "UNKNOWN") + val OS_VERSION = System.getProperty("os.version", "UNKNOWN") + val OS_ARCH = System.getProperty("os.arch", "UNKNOWN") + /* * Load the properties file from the specified location */ diff --git a/gui/src/main/resources/fxml/mainapp/MainApp.fxml b/gui/src/main/resources/fxml/mainapp/MainApp.fxml index 2d48599..f7ef02c 100644 --- a/gui/src/main/resources/fxml/mainapp/MainApp.fxml +++ b/gui/src/main/resources/fxml/mainapp/MainApp.fxml @@ -2,6 +2,7 @@ + @@ -128,9 +129,9 @@ - + - + diff --git a/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTile.scala b/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTile.scala index f64061d..ae8a02d 100644 --- a/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTile.scala +++ b/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTile.scala @@ -1,6 +1,7 @@ package com.sothr.imagetools.ui.component -import java.io.FileInputStream +import java.awt.Desktop +import java.io.{File, FileInputStream} import javafx.event.{EventType, EventHandler} import javafx.geometry.{Orientation, Insets, Pos} import javafx.scene.control.{Separator, Tooltip, Label} @@ -8,6 +9,7 @@ import javafx.scene.image.{ImageView} import javafx.scene.input.{PickResult, ContextMenuEvent, MouseEvent} import javafx.scene.layout.VBox +import com.sothr.imagetools.ui.util.FileUtil import grizzled.slf4j.Logging import resource._ @@ -37,6 +39,9 @@ class ImageTile(thumbnailWidth: Integer, if (event.isShiftDown) { //multiple selection imageTilePane.addImageSelected(thisTile) + //remove individual images with control + } else if (event.isControlDown) { + imageTilePane.removeImageSelected(thisTile) } else { if (event.isPrimaryButtonDown) { @@ -45,13 +50,14 @@ class ImageTile(thumbnailWidth: Integer, if (event.getClickCount == 2) { // Look into http://stackoverflow.com/questions/228477/how-do-i-programmatically-determine-operating-system-in-java // for proper multi-platform opening support - //Desktop.getDesktop.open(new File(image.getImagePath)) + FileUtil.openInEditor(new File(image.getImagePath)) } else { } } else if (event.isSecondaryButtonDown) { //right click context menu debug("Requesting Context Menu") + imageTilePane.addImageSelected(thisTile) val contextMenuEvent = new ContextMenuEvent( thisTile, thisTile, diff --git a/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTilePane.scala b/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTilePane.scala index b4e01ae..41f1e18 100644 --- a/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTilePane.scala +++ b/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTilePane.scala @@ -88,6 +88,15 @@ class ImageTilePane extends TilePane with Logging { def addImageSelected(imageTile: ImageTile) = { this.selectionModel.select(this.getChildren.indexOf(imageTile)) } + + def removeImageSelected(imageTile: ImageTile) = { + this.selectionModel.clearSelection(this.getChildren.indexOf(imageTile)) + } + + def clearSelection() = { + this.selectionModel.clearSelection() + } + } /** @@ -153,7 +162,11 @@ class ImageTilePaneSelectionModel[ImageTile](parentTilePane: ImageTilePane) exte } override def clearSelection(index: Int): Unit = { - this.selectedIndexes.remove(index) + val i = this.selectedIndexes.indexOf(index) + if (i >= 0) { + clearSelectionFormatting(index) + this.selectedIndexes.remove(i) + } } override def clearSelection(): Unit = { @@ -178,8 +191,11 @@ class ImageTilePaneSelectionModel[ImageTile](parentTilePane: ImageTilePane) exte } override def select(index: Int): Unit = { - setSelectionFormatting(index) - this.selectedIndexes.add(index) + //can only select once + if (! this.selectedIndexes.contains(index)) { + setSelectionFormatting(index) + this.selectedIndexes.add(index) + } } override def select(obj: ImageTile): Unit = { @@ -199,6 +215,11 @@ class ImageTilePaneSelectionModel[ImageTile](parentTilePane: ImageTilePane) exte this.selectedIndexes.contains(index) } + private def clearSelectionFormatting(index: Int) = { + val tile = this.parentTilePane.getChildren.get(index).asInstanceOf[VBox] + tile.setBorder(Border.EMPTY) + } + private def clearSelectionFormatting() = { val iterator = this.parentTilePane.getChildren.iterator() while (iterator.hasNext) { @@ -212,7 +233,7 @@ class ImageTilePaneSelectionModel[ImageTile](parentTilePane: ImageTilePane) exte setSelectionFormatting(this.parentTilePane.getChildren.get(index).asInstanceOf[ImageTile]) } - private def setSelectionFormatting(imageTile: ImageTile): Unit = { + private def setSelectionFormatting(imageTile: ImageTile) = { val borderStroke = new BorderStroke(Color.BLUE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY,BorderStroke.THIN) imageTile.asInstanceOf[VBox].setBorder(new Border(borderStroke)) } diff --git a/gui/src/main/scala/com/sothr/imagetools/ui/controller/AppController.scala b/gui/src/main/scala/com/sothr/imagetools/ui/controller/AppController.scala index 4dd7263..bdedbe7 100644 --- a/gui/src/main/scala/com/sothr/imagetools/ui/controller/AppController.scala +++ b/gui/src/main/scala/com/sothr/imagetools/ui/controller/AppController.scala @@ -80,6 +80,9 @@ class AppController extends Logging { PropertiesService.set("app.ui.thumbsPerPage", "100") } + //setup the paginator + //font size doesn't increase the size of the buttons + //paginator.setStyle("-fx-font-size:13;") // configure the page factory paginator.setPageFactory(new Callback[Integer, Node]() { override def call(pageIndex: Integer): Node = { @@ -188,7 +191,8 @@ class AppController extends Logging { resetPaginator() getImageTilePane.getChildren.setAll(new java.util.ArrayList[Node]()) val f: Future[List[Image]] = Future { - engine.getImagesForDirectory(currentDirectory, recursive = doRecursiveProcessing.isSelected) + val images = engine.getImagesForDirectory(currentDirectory, recursive = doRecursiveProcessing.isSelected) + images.sortWith((x,y) => x.imagePath < y.imagePath) } f onComplete { @@ -212,22 +216,23 @@ class AppController extends Logging { resetPaginator() imageTilePane.getChildren.setAll(new java.util.ArrayList[Node]()) - val f: Future[List[SimilarImages]] = Future { - engine.getSimilarImagesForDirectory(currentDirectory, recursive = doRecursiveProcessing.isSelected) + val f: Future[List[Image]] = Future { + val similarImages = engine.getSimilarImagesForDirectory(currentDirectory, recursive = doRecursiveProcessing.isSelected) + val tempImages = new mutable.MutableList[Image]() + for (similarImage <- similarImages) { + debug(s"Adding similar images ${similarImage.rootImage.toString} to app") + tempImages += similarImage.rootImage + similarImage.similarImages.foreach(image => tempImages += image) + } + tempImages.toList.sortWith((x,y) => x.imagePath < y.imagePath) } f onComplete { - case Success(similarImages) => - info(s"Displaying ${similarImages.length} similar images") + case Success(images) => + info(s"Displaying ${images.length} similar images") Platform.runLater(new Runnable() { override def run() { - val tempImages = new mutable.MutableList[Image]() - for (similarImage <- similarImages) { - debug(s"Adding similar images ${similarImage.rootImage.toString} to app") - tempImages += similarImage.rootImage - similarImage.similarImages.foreach(image => tempImages += image) - } - setPagesContent(tempImages.toList) + setPagesContent(images) showPage(0) } }) @@ -246,7 +251,7 @@ class AppController extends Logging { } def setPagesContent(images: List[Image]) = { - this.currentImages = images.sortWith((x,y) => x.imagePath < y.imagePath) + this.currentImages = images //set the appropriate size for the pagination val itemsPerPage = PropertiesService.get("app.ui.thumbsPerPage", "100").toInt val pageNum = Math.ceil(this.currentImages.size.toFloat / itemsPerPage).toInt @@ -258,6 +263,8 @@ class AppController extends Logging { val itemsPerPage = PropertiesService.get("app.ui.thumbsPerPage", "100").toInt val startIndex = pageIndex * itemsPerPage val endIndex = if ((startIndex + itemsPerPage) > this.currentImages.size) this.currentImages.length else startIndex + itemsPerPage + //clear any selections + getImageTilePane.asInstanceOf[ImageTilePane].clearSelection() //clear and populate the scrollpane getImageTilePane.getChildren.setAll(new java.util.ArrayList[Node]()) val images = this.currentImages.slice(startIndex, endIndex) diff --git a/gui/src/main/scala/com/sothr/imagetools/ui/util/FileUtil.scala b/gui/src/main/scala/com/sothr/imagetools/ui/util/FileUtil.scala new file mode 100644 index 0000000..317b169 --- /dev/null +++ b/gui/src/main/scala/com/sothr/imagetools/ui/util/FileUtil.scala @@ -0,0 +1,21 @@ +package com.sothr.imagetools.ui.util + +import java.awt.Desktop +import java.io.File + +import com.sothr.imagetools.engine.util.PropertiesService +import grizzled.slf4j.Logging + +/** + * Created by Drew Short on 8/31/2014. + */ +object FileUtil extends Logging { + + def openInEditor(file: File) = { + PropertiesService.OS.toLowerCase match { + // Open file on windows + case os if os.startsWith("windows") => Desktop.getDesktop.open(file) + case default => error(s"Do not know how to open editor for OS: ${PropertiesService.OS}, ${PropertiesService.OS_VERSION}, ${PropertiesService.OS_ARCH}") + } + } +}