Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
N. Justus committed Feb 17, 2017
2 parents ba47b44 + 4585c1e commit bec5b3f
Show file tree
Hide file tree
Showing 13 changed files with 171 additions and 25 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
======= Version 0.7.1 ==
==> Features
- adds menu for recent files (currently for opened files only)
- adds innternationalization for english & german
- adds zoom/scale functions and bottom toolbar
- CLI Interface for opening MoVE through command line
- new unified input dialogs
- adds icons in (context-) menus
- new icons for Polygon & Path tools

==> Bufixes
- parse class specializations (packages, classes, partials,..)

==> Code improvements
- update to scala 2.11.8
- use ReactFX and streams for event processing
- move package `move.controller.implicits` to `move.implicits`
- create drawing strategies in `controllers.drawing`

======= Version 0.7.0 ==
==> New major Features
- warn if DynamicSelect & Conditional-Values are used in loaded files
Expand Down
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ lazy val root = (project in file(".")).
version := "0.7.0",
scalaVersion := "2.11.8",
javacOptions ++= Seq("-source", "1.8")
)
).
dependsOn(RootProject(file("../recently")))

mainClass in Compile := Some("de.thm.move.MoveApp")
assemblyJarName in assembly := s"${name.value}-${version.value}.jar"
Expand Down
8 changes: 8 additions & 0 deletions src/main/resources/fxml/move.fxml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@
<graphic>
<Label styleClass="toolbar-button" text="%fa.file.open" />
</graphic></MenuItem>


<Menu fx:id="recentFilesMenu" text="%file.recent-files">
<items>
</items>
</Menu>


<MenuItem fx:id="saveMenuItem" mnemonicParsing="false" onAction="#onSaveClicked" text="%file.save">
<graphic>
<Label styleClass="toolbar-button" text="%fa.file.save" />
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/i18n/messages_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cancel-btn=Abbrechen
file=Datei
file.new=Neue Datei
file.open=Datei öffnen
file.recent-files=Zuletzt verwendet
file.save=Speichern
file.save-as=Speichern unter
file.export-svg=Als SVG exportieren
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/i18n/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cancel-btn=Cancel
file=File
file.new=New file
file.open=Open file
file.recent-files=Open recent
file.save=Save
file.save-as=Save as
file.export-svg=Export as SVG
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/de/thm/move/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ import de.thm.move.util.CustomResourceBundle
object Global {

private val configDirectoryName = ".move"
private val homeDirPath = Paths.get(System.getProperty("user.home"))
private val configDirPath = homeDirPath.resolve(configDirectoryName)
private val configDirPath = Paths.get(System.getProperty("user.home"), configDirectoryName)

/** Check if path exist; if not create it */
private def withCheckConfigDirectory[A](fn: Path => A): A = {
Expand Down Expand Up @@ -54,6 +53,7 @@ object Global {

lazy val shortcuts = new ShortCutHandler(getConfigFile("shortcuts.conf"))
lazy val config: Config = new ConfigLoader(getConfigFile("move.conf"))
lazy val recentFilesPath = configDirPath.resolve("recent-files.json")

lazy val historySize = Global.config.getInt("history.cache-size").getOrElse(50)
lazy val history = new History(historySize)
Expand Down
19 changes: 17 additions & 2 deletions src/main/scala/de/thm/move/controllers/MoveCtrl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@

package de.thm.move.controllers

import java.io.InputStream
import java.net.URL
import java.nio.file.{Path, Paths}
import java.nio.file.{Files, Path, Paths, StandardOpenOption}
import java.util.ResourceBundle
import javafx.application.Platform
import javafx.collections.ListChangeListener.Change
Expand All @@ -31,7 +32,6 @@ import de.thm.move.implicits.FxHandlerImplicits
import de.thm.move.implicits.FxHandlerImplicits._
import de.thm.move.implicits.MonadImplicits._
import de.thm.move.implicits.LambdaImplicits._

import de.thm.move.models.FillPattern._
import de.thm.move.models.LinePattern._
import de.thm.move.models.SelectedShape.SelectedShape
Expand All @@ -43,6 +43,8 @@ import de.thm.move.views.anchors.Anchor
import de.thm.move.views.dialogs.Dialogs
import de.thm.move.views.panes.{DrawPanel, SnapGrid}
import de.thm.move.views.shapes.{ResizableShape, ResizableText}
import de.thm.recent.{MRecent, Recent}
import de.thm.recent.JsProtocol._

import scala.None
import scala.collection.JavaConversions._
Expand All @@ -65,6 +67,8 @@ class MoveCtrl extends Initializable {
@FXML
var openMenuItem: MenuItem = _
@FXML
var recentFilesMenu: Menu =_
@FXML
var chPaperSizeMenuItem: MenuItem = _
@FXML
var closeMenuItem: MenuItem = _
Expand Down Expand Up @@ -128,6 +132,13 @@ class MoveCtrl extends Initializable {

private val moveHandler = selectionCtrl.getMoveHandler

private val recentHandler = {
val recent =
if(Files.exists(recentFilesPath)) MRecent(Recent.fromInputStream[Path](Files.newInputStream(recentFilesPath)))
else MRecent(Recent.fromList(Seq[Path]()))
new RecentlyFilesHandler(recent, openFile)
}

private val shapeBtnsToSelectedShapes = Map(
"rectangle_btn" -> SelectedShape.Rectangle,
"circle_btn" -> SelectedShape.Circle,
Expand Down Expand Up @@ -198,6 +209,8 @@ class MoveCtrl extends Initializable {
"show-grid" -> showGridItem,
"enable-snapping" -> enableGridItem)

recentFilesMenu.getItems.addAll(recentHandler.getMenuItems:_*)

embeddedTextMenuController.setSelectedShapeCtrl(selectionCtrl)
embeddedColorToolbarController.postInitialize(selectionCtrl)
embeddedBottomToolbarController.postInitialize(drawStub)
Expand Down Expand Up @@ -333,6 +346,7 @@ class MoveCtrl extends Initializable {

def shutdownMove(): Unit = {
embeddedColorToolbarController.shutdown()
recentHandler.writeTo(recentFilesPath)
}

def shapeInputHandler(ev:InputEvent): Unit = {
Expand Down Expand Up @@ -386,6 +400,7 @@ class MoveCtrl extends Initializable {
fileInfos match {
case Success((file, system, shapes)) =>
displayUsedFile(file)
recentHandler.incrementPriorityOf(file)
drawPanel.setSize(system)
if (drawPanelCtrl.getElements.nonEmpty) {
drawPanelCtrl.removeAll()
Expand Down
36 changes: 36 additions & 0 deletions src/main/scala/de/thm/move/controllers/RecentlyFilesHandler.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package de.thm.move.controllers

import java.nio.file.{Files, Path, StandardOpenOption}
import javafx.event.ActionEvent
import javafx.scene.control.MenuItem

import de.thm.move.Global
import de.thm.move.implicits.FxHandlerImplicits._
import de.thm.move.util.JFxUtils
import de.thm.recent._
import spray.json.JsonFormat

class RecentlyFilesHandler(recent:Recent[Path], pathClicked: Path => Unit) {

private def menuItem(path:Path): MenuItem = {
val item = new MenuItem(path.toString)
JFxUtils.addFontIcon(item, "\uf1c9")
item.setOnAction { _:ActionEvent =>
incrementPriorityOf(path)
println(recent.recentValuesByPriority)
pathClicked(path)
}
item
}

def incrementPriorityOf(path:Path): Unit =
recent.incrementPriority(path)

def getMenuItems:Seq[MenuItem] =
recent.recentElementsByPriority.map(menuItem)

def writeTo(outputFile:Path)(implicit pathFormat:JsonFormat[Path]): Unit = {
val jsonString = recent.toJson
Files.write(outputFile, jsonString.getBytes(Global.encoding))
}
}
12 changes: 6 additions & 6 deletions src/main/scala/de/thm/move/loader/parser/ModelicaParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ class ModelicaParser extends JavaTokenParsers
toAst(for {
gi <- getGraphicItem(map)
ext <- getPropertyValue(map, extent)(extension)
base64Opt = map.get(base64).map(identWithoutHyphens)
imgUriOpt = map.get(imgUri).map(identWithoutHyphens)
base64Opt = map.get(base64).map(stringLiteral)
imgUriOpt = map.get(imgUri).map(stringLiteral)
} yield {
base64Opt.map(ImageBase64(gi, ext, _)).orElse(
imgUriOpt.map(ImageURI(gi, ext, _))).getOrElse(
Expand All @@ -188,9 +188,9 @@ class ModelicaParser extends JavaTokenParsers
toAst( for {
gi <- getGraphicItem(map)
ext <- getPropertyValue(map, extent)(extension)
text = map.get(textString).map(identWithoutHyphens).getOrElse(missingKeyError(textString))
text = map.get(textString).map(stringLiteral).getOrElse(missingKeyError(textString))
fs <- getPropertyValue(map, fontSize, validValue(defaultFontSize))(withVariableValues(decimalNo, fontSize))
font = map.get(fontName).map(identWithoutHyphens).getOrElse(defaultFont)
font = map.get(fontName).map(stringLiteral).getOrElse(defaultFont)
fontStyle <- getPropertyValue(map, textStyle, validValue(defaultFontStyle))(withVariableValues(emptySeqString, textStyle))
cl <- getPropertyValue(map, textColor, validValue(defaultCol))(withVariableValues(color, textColor))
halignment <- getPropertyValue(map, hAlignment, validValue(defaultHAlignment))(withVariableValues(ident, hAlignment))
Expand All @@ -214,10 +214,10 @@ class ModelicaParser extends JavaTokenParsers
lp <- getPropertyValue(map, linePatt, validValue(defaultLinePatt))(withVariableValues(ident, linePatt))
} yield FilledShape(cl,fp,lc,thick,lp)

def identWithoutHyphens(str:String):String = {
def stringLiteral(str:String):String = {
val regex = "\"(.*)\"".r
str match {
case regex(inner) => inner
case regex(inner) => transformEscapeChars(inner)
case _ => throw new ParsingError(s"$str doesn't match $regex")
}
}
Expand Down
21 changes: 21 additions & 0 deletions src/main/scala/de/thm/move/loader/parser/PropertyParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,27 @@ trait PropertyParser {
val classSpecialization:Parser[String] = (
"class" | "model" | "record" | "type" | "block" | "function" | "connector" | "package"
)

def transformEscapeChars(str:String): String = {
@annotation.tailrec
def run(str:String, newS:String): String = {
if(str.isEmpty()) newS
else {
str.charAt(0) match {
case '\\' => str.charAt(1) match {
case 'n' => run(str.substring(2), newS+"\n")
case 't' => run(str.substring(2), newS+"\t")
case 'r' => run(str.substring(2), newS+"\r")
case 'b' => run(str.substring(2), newS+"\b")
case c:Char => run(str.substring(1), newS+'\\'+c)
}
case c:Char => run(str.substring(1), newS+c)
}
}
}

run(str, "")
}
}

object PropertyParser {
Expand Down
11 changes: 9 additions & 2 deletions src/main/scala/de/thm/move/util/JFxUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ package de.thm.move.util
import javafx.beans.property.ObjectProperty
import javafx.event.{Event, EventHandler}
import javafx.scene.Node
import javafx.scene.control.ChoiceBox
import javafx.scene.input.{KeyEvent, KeyCode}
import javafx.scene.control.{ChoiceBox, Label, MenuItem}
import javafx.scene.input.{KeyCode, KeyEvent}

import de.thm.move.implicits.FxHandlerImplicits._
import de.thm.move.views.anchors.Anchor
Expand Down Expand Up @@ -73,4 +73,11 @@ object JFxUtils {

/** A predicate for filtering a KeyEvent-Stream by the pressed KeyCode. */
val byKeyCode: KeyCode => KeyEvent => Boolean = code => kv => kv.getCode == code

def addFontIcon(elem:MenuItem, iconIdent:String): MenuItem = {
val lbl = new Label(iconIdent)
lbl.getStyleClass().add("toolbar-button")
elem.setGraphic(lbl)
elem
}
}
18 changes: 6 additions & 12 deletions src/main/scala/de/thm/move/views/ShapeContextMenu.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,17 @@ package de.thm.move.views

import javafx.scene.control.{ContextMenu, MenuItem, Label}
import de.thm.move.Global._
import de.thm.move.util.JFxUtils

class ShapeContextMenu extends ContextMenu {
private def withFontIcon(elem:MenuItem, iconIdent:String): MenuItem = {
val lbl = new Label(iconIdent)
lbl.getStyleClass().add("toolbar-button")
elem.setGraphic(lbl)
elem
}

val inForegroundItem = new MenuItem(fontBundle.getString("context.foreground"))
val inBackgroundItem = new MenuItem(fontBundle.getString("context.background"))
val duplicateElementItem = withFontIcon(new MenuItem(fontBundle.getString("context.duplicate")), "\uf24d")
val duplicateElementItem = JFxUtils.addFontIcon(new MenuItem(fontBundle.getString("context.duplicate")), "\uf24d")
val resetRotationElementItem = new MenuItem(fontBundle.getString("context.reset-rotation"))
val rotate90ClockwiseItem = withFontIcon(new MenuItem(fontBundle.getString("context.rotate-90-clockwise")), "\uf01e")
val rotate90CounterClockwiseItem = withFontIcon(new MenuItem(fontBundle.getString("context.rotate-90-counter-clockwise")), "\uf0e2")
val rotate45ClockwiseItem = withFontIcon(new MenuItem(fontBundle.getString("context.rotate-45-clockwise")), "\uf01e")
val rotate45CounterClockwiseItem = withFontIcon(new MenuItem(fontBundle.getString("context.rotate-45-counter-clockwise")), "\uf0e2")
val rotate90ClockwiseItem = JFxUtils.addFontIcon(new MenuItem(fontBundle.getString("context.rotate-90-clockwise")), "\uf01e")
val rotate90CounterClockwiseItem = JFxUtils.addFontIcon(new MenuItem(fontBundle.getString("context.rotate-90-counter-clockwise")), "\uf0e2")
val rotate45ClockwiseItem = JFxUtils.addFontIcon(new MenuItem(fontBundle.getString("context.rotate-45-clockwise")), "\uf01e")
val rotate45CounterClockwiseItem = JFxUtils.addFontIcon(new MenuItem(fontBundle.getString("context.rotate-45-counter-clockwise")), "\uf0e2")

getItems.addAll(
inForegroundItem,
Expand Down
43 changes: 43 additions & 0 deletions src/test/scala/de/thm/move/loader/parser/StringParserTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright (C) 2016 Nicola Justus <[email protected]>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

package de.thm.move.loader.parser

import java.io.{ByteArrayInputStream, InputStreamReader}
import java.nio.charset.StandardCharsets

import scala.util._
import de.thm.move.MoveSpec
import de.thm.move.loader.parser.PropertyParser._
import de.thm.move.loader.parser.ast._

class StringParserTest extends MoveSpec {
val parser = new ModelicaParser()
def parseString(str:String): String = {
parser.stringLiteral(str)
}

"The parser for Modelica strings" should "parse simple strings" in {
val s = "this is a super awesome test"
true shouldBe true
}

"PropertyParser#transformEscapeChars" should
"transform literal escape characters to ansi escape characters" in {
val s = "this\\t\\tis a\\n test\\rmöb\\b"
parser.transformEscapeChars(s) shouldBe "this\t\tis a\n test\rmöb\b"

val s2 = "\\n\\n\\t"
parser.transformEscapeChars(s2) shouldBe "\n\n\t"
}

it should "return the same string for strings without escape characters" in {
val s = "this is awesome"
parser.transformEscapeChars(s) shouldBe s
}
}

0 comments on commit bec5b3f

Please sign in to comment.