Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ scalaVersion := "2.9.2"

retrieveManaged := true

javaSource in Compile <<= baseDirectory(_ / "src" / "main")
javaSource in Compile <<= baseDirectory(_ / "src" / "main")

scalaSource in Compile <<= baseDirectory(_ / "src" / "main")

scalaSource in Test <<= baseDirectory(_ / "src" / "test")

Expand All @@ -13,8 +15,8 @@ scalacOptions ++= Seq("-deprecation", "-unchecked", "-Xfatal-warnings",

libraryDependencies ++= Seq(
"com.google.guava" % "guava" % "18.0",
"org.nlogo" % "NetLogo" % "5.1.0" from "http://ccl.northwestern.edu/netlogo/5.1.0/NetLogo.jar",
"org.nlogo" % "NetLogo-tests" % "5.1.0" % "test" from "http://ccl.northwestern.edu/netlogo/5.1.0/NetLogo-tests.jar",
"org.nlogo" % "NetLogo" % "5.2.0-LS1" from "http://ccl.northwestern.edu/devel/5.2.0-LS1/NetLogo.jar",
"org.nlogo" % "NetLogo-tests" % "5.2.0-LS1" % "test" from "http://ccl.northwestern.edu/devel/5.2.0-LS1/NetLogo-tests.jar",
"org.scalatest" %% "scalatest" % "1.8" % "test",
"org.picocontainer" % "picocontainer" % "2.13.6" % "test",
"asm" % "asm-all" % "3.3.1" % "test"
Expand Down
81 changes: 81 additions & 0 deletions src/main/BackingModelManager.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import gui.{ModelProceduresTab, LevelSpaceMenu}

import java.util.{ Map => JMap }

import org.nlogo.app.{ProceduresTab, App}
import org.nlogo.workspace.AbstractWorkspace

import scala.collection.JavaConversions._
import scala.collection.parallel.mutable.ParHashMap

trait LSModelManager extends gui.ModelManager {
def updateChildModels(map: JMap[java.lang.Integer, ChildModel]): Unit = {}
def guiComponent: LevelSpaceMenu = null
}

class BackingModelManager extends LSModelManager {
override val guiComponent = new LevelSpaceMenu(App.app.tabs, this)
val backingModels = ParHashMap.empty[String, (ChildModel, ModelProceduresTab)]
var openModels = Map.empty[String, ChildModel]

override def updateChildModels(indexedModels: JMap[java.lang.Integer, ChildModel]): Unit = {
val models = indexedModels.values

// toSeq.distinct preserves ordering, whereas toSet does not
val modelPaths = models.map(_.workspace().getModelPath).toSeq.distinct

val closedModelsPaths = (openModels.values.toSet &~ models.toSet).map(_.workspace().getModelPath)
val newlyOpenedPaths = (models.toSet &~ openModels.values.toSet).map(_.workspace().getModelPath)
openModels = (modelPaths zip models).toMap
(closedModelsPaths intersect openModelPaths).foreach(replaceTabAtPath)
(newlyOpenedPaths intersect openModelPaths).foreach(replaceTabAtPath)
guiComponent.addMenuItemsForOpenModels(modelPaths)
}

private def replaceTabAtPath(filePath: String) =
guiComponent.replaceTab(backingModels(filePath)._2)

def openModelPaths = backingModels.seq.keySet

def existingTab(filePath: String): Option[ProceduresTab] =
if (filePath == App.app.workspace.getModelPath)
Some(App.app.tabs.proceduresTab)
else
backingModels.get(filePath).map(_._2)

def removeTab(tab: ModelProceduresTab): Unit = {
if (! openModelPaths(tab.filePath))
backingModels.get(tab.filePath).foreach(_._1.kill())
backingModels -= tab.filePath
}

def registerTab(filePath: String, model: ChildModel)
(f: AbstractWorkspace => ModelProceduresTab): Option[ModelProceduresTab] = {
if (backingModels.get(filePath).isDefined) {
None
} else {
val tab = f(model.workspace())
backingModels += filePath ->(model, tab)
Some(tab)
}
}

def registerTab(filePath: String)
(f: AbstractWorkspace => ModelProceduresTab): Option[ModelProceduresTab] = {
if (backingModels.get(filePath).isDefined) {
None
} else {
val newModel =
openModels.getOrElse(filePath, new HeadlessChildModel(App.app.workspace.world(), filePath, -1))
registerTab(filePath, newModel)(f)
}
}
}

// this is an example of the
class HeadlessBackingModelManager extends LSModelManager {
def removeTab(tab: ModelProceduresTab): Unit = {}
def existingTab(filePath: String): Option[ModelProceduresTab] = None
def registerTab(filePath: String)
(f: AbstractWorkspace => ModelProceduresTab): Option[ModelProceduresTab] = None
}
2 changes: 1 addition & 1 deletion src/main/ChildModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ public <T> T runNlogoSafely(final Callable<T> callable) throws HaltException, Ex

public <T> T runUISafely(final Callable<T> callable) throws ExtensionException, HaltException {
// waitFor is unsafe on the event queue, so if we're on the event queue, just run directly.
if (EventQueue.isDispatchThread()) {
if (SwingUtilities.isEventDispatchThread()) {
try {
return callable.call();
} catch (ExtensionException e) {
Expand Down
100 changes: 0 additions & 100 deletions src/main/GUIChildModel.java

This file was deleted.

84 changes: 84 additions & 0 deletions src/main/GUIChildModel.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import java.awt._
import java.awt.event.{ WindowAdapter, WindowEvent }
import java.util.concurrent.Callable
import javax.swing.{ FocusManager, JFrame, JMenuBar, JOptionPane, WindowConstants }
import gui.{ZoomableInterfaceComponent, GUIPanel}
import org.nlogo.api._
import org.nlogo.app.App
import org.nlogo.lite.{LiteWorkspace, InterfaceComponent}
import org.nlogo.nvm.HaltException
import org.nlogo.window.Events.ZoomedEvent
import org.nlogo.window.Widget.LoadHelper
import org.nlogo.window._
import org.nlogo.workspace.AbstractWorkspace

class GUIChildModel @throws(classOf[InterruptedException]) @throws(classOf[ExtensionException]) @throws(classOf[HaltException]) (parentWorld: World, path: String, levelsSpaceNumber: Int)
extends ChildModel(parentWorld, levelsSpaceNumber) {

final val frame: JFrame = new JFrame

var panel: GUIPanel = null
val component = runUISafely(RunGUIChildModel)

init()

object RunGUIChildModel extends Callable[InterfaceComponent] {
@throws(classOf[Exception])
def call: InterfaceComponent = {
val component: InterfaceComponent = new ZoomableInterfaceComponent(frame)
panel = new GUIPanel(component)
frame.add(panel)
val currentlyFocused: Window =
Option(KeyboardFocusManager.getCurrentKeyboardFocusManager.getActiveWindow).getOrElse(App.app.frame)
frame.setLocationRelativeTo(currentlyFocused)
frame.setLocationByPlatform(true)
frame.setVisible(true)
currentlyFocused.toFront()
component.open(path)
val c: Array[Component] = component.workspace.viewWidget.controlStrip.getComponents
c.foreach {
case ssp: SpeedSliderPanel =>
ssp.setVisible(false)
ssp.setValue(0)
case _ =>
}
frame.pack()
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE)
frame.addWindowListener(new GUIWindowAdapter)
val newMenuBar = new JMenuBar()
val zoomMenuClass = Class.forName("org.nlogo.app.ZoomMenu")
newMenuBar.add(zoomMenuClass.newInstance().asInstanceOf[org.nlogo.swing.Menu])
frame.setJMenuBar(newMenuBar)
component
}
}

class GUIWindowAdapter extends WindowAdapter {
override def windowClosing(windowEvent: WindowEvent): Unit = {
val options: Array[AnyRef] = Array("Close Model", "Run in Background", "Cancel")
val n: Int = JOptionPane.showOptionDialog(frame, "Close the model, run it in the background, or do nothing?", null, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options(2))
n match {
case 0 =>
try {
LevelsSpace.closeModel(levelsSpaceNumber)
} catch {
case e: ExtensionException => throw new RuntimeException(e)
case e: HaltException =>
}
case 1 => hide()
case 2 =>
}
}
}

def setSpeed(d: Double): Unit = {
val c: Array[Component] = component.workspace.viewWidget.controlStrip.getComponents
for (co <- c) {
if (co.isInstanceOf[SpeedSliderPanel]) {
(co.asInstanceOf[SpeedSliderPanel]).setValue(d.toInt)
}
}
}

def workspace: AbstractWorkspace = component.workspace
}
44 changes: 0 additions & 44 deletions src/main/GUIPanel.java

This file was deleted.

Loading