summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Master/texmf-dist/scripts/tlcockpit/tlcockpit.jarbin11651966 -> 11667498 bytes
-rw-r--r--Master/texmf-dist/source/support/tlcockpit/src/main/scala/TLCockpit/ApplicationMain.scala480
-rw-r--r--Master/texmf-dist/source/support/tlcockpit/src/main/scala/TeXLive/TLUpdate.scala15
-rw-r--r--Master/texmf-dist/source/support/tlcockpit/src/main/scala/TeXLive/TlmgrProcess.scala12
4 files changed, 342 insertions, 165 deletions
diff --git a/Master/texmf-dist/scripts/tlcockpit/tlcockpit.jar b/Master/texmf-dist/scripts/tlcockpit/tlcockpit.jar
index ee194fe5d2e..a3d9ba726ae 100644
--- a/Master/texmf-dist/scripts/tlcockpit/tlcockpit.jar
+++ b/Master/texmf-dist/scripts/tlcockpit/tlcockpit.jar
Binary files differ
diff --git a/Master/texmf-dist/source/support/tlcockpit/src/main/scala/TLCockpit/ApplicationMain.scala b/Master/texmf-dist/source/support/tlcockpit/src/main/scala/TLCockpit/ApplicationMain.scala
index 5831e4828e4..65b4b0187df 100644
--- a/Master/texmf-dist/source/support/tlcockpit/src/main/scala/TLCockpit/ApplicationMain.scala
+++ b/Master/texmf-dist/source/support/tlcockpit/src/main/scala/TLCockpit/ApplicationMain.scala
@@ -6,19 +6,13 @@
package TLCockpit
-import javafx.collections.ObservableList
-import javafx.scene.control
-
import TLCockpit.ApplicationMain.getClass
-import TeXLive.{TLBackup, TLPackage, TlmgrProcess}
+import TeXLive.{TLBackup, TLPackage, TLUpdate, TlmgrProcess}
-import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.{Future, SyncVar}
import scala.concurrent.ExecutionContext.Implicits.global
-import scala.util.{Failure, Sorting, Success}
-import scalafx.beans.Observable
-import scalafx.beans.property.{ObjectProperty, ReadOnlyStringWrapper}
+import scala.util.{Failure, Success}
import scalafx.geometry.{HPos, Pos, VPos}
import scalafx.scene.control.Alert.AlertType
import scalafx.scene.image.{Image, ImageView}
@@ -34,11 +28,15 @@ import scalafx.scene.Scene
import scalafx.scene.layout._
import scalafx.scene.control._
import scalafx.event.ActionEvent
-import scalafx.collections.ObservableBuffer
import scala.sys.process._
-import scalafx.scene.text._
import scalafx.collections.ObservableBuffer
+import scalafx.collections.ObservableMap
+
+// TODO update listings when package is updated/installed/removed ...
+// TODO line by line reading/action of shell output
+// TODO TreeTableView indentation is lazy
+// TODO pkg info - access to doc files
object ApplicationMain extends JFXApp {
@@ -52,7 +50,9 @@ object ApplicationMain extends JFXApp {
val iconImage = new Image(getClass.getResourceAsStream("tlcockpit-48.jpg"))
val logoImage = new Image(getClass.getResourceAsStream("tlcockpit-128.jpg"))
- val pkgs: ArrayBuffer[TLPackage] = ArrayBuffer()
+ val pkgs = ObservableMap[String, TLPackage]()
+ val upds = ObservableMap[String, TLUpdate]()
+ val bkps = ObservableMap[String, Map[String,TLBackup]]() // pkgname -> (version -> TLBackup)*
val errorText: ObservableBuffer[String] = ObservableBuffer[String]()
val outputText: ObservableBuffer[String] = ObservableBuffer[String]()
@@ -77,11 +77,11 @@ object ApplicationMain extends JFXApp {
})
val update_all_menu: MenuItem = new MenuItem("Update all") {
- onAction = (ae) => callback_update_all()
+ onAction = (ae) => callback_update("--all")
disable = true
}
val update_self_menu: MenuItem = new MenuItem("Update self") {
- onAction = (ae) => callback_update_self()
+ onAction = (ae) => callback_update("--self")
disable = true
}
@@ -111,6 +111,7 @@ object ApplicationMain extends JFXApp {
cmdline.onKeyPressed = {
(ae: KeyEvent) => if (ae.code == KeyCode.Enter) callback_run_cmdline()
}
+ val outputLine = new SyncVar[String]
val tlmgr = new TlmgrProcess(
// (s:String) => outputfield.text = s,
(s: Array[String]) => {
@@ -126,7 +127,9 @@ object ApplicationMain extends JFXApp {
outerrpane.expanded = true
outerrtabs.selectionModel().select(1)
}
- })
+ },
+ (s: String) => outputLine.put(s)
+ )
def tlmgr_async_command(s: String, f: Array[String] => Unit): Unit = {
errorText.clear()
@@ -148,92 +151,6 @@ object ApplicationMain extends JFXApp {
}
}
- def update_update_menu_state(): Unit = {
- update_all_menu.disable = !pkgs.foldLeft(false)(
- (a, b) => a || (if (b.name.value == "texlive.infra") false else b.lrev.value.toInt > 0 && b.lrev.value.toInt < b.rrev.value.toInt)
- )
- // TODO we should change pkgs to a Map with keys are the names + repository (for multi repo support)
- // and the values are llrev/rrev or some combination
- update_self_menu.disable = !pkgs.foldLeft(false)(
- (a, b) => a || (if (b.name.value == "texlive.infra") b.lrev.value.toInt < b.rrev.value.toInt else false)
- )
- updateTable.refresh()
- packageTable.refresh()
- }
-
- def update_pkg_lists(): Unit = {
- tlmgr_async_command("info --data name,localrev,remoterev,shortdesc,size,installed", (s: Array[String]) => {
- pkgs.clear()
- s.map((line: String) => {
- val fields: Array[String] = line.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1)
- val sd = fields(3)
- val shortdesc = if (sd.isEmpty) "" else sd.substring(1).dropRight(1).replace("""\"""",""""""")
- val inst = if (fields(5) == "1") "Installed" else "Not installed"
- pkgs += new TLPackage(fields(0), fields(1), fields(2), shortdesc, fields(4), inst)
- })
- val pkgbuf: ArrayBuffer[TLPackage] = ArrayBuffer.empty[TLPackage]
- val binbuf = scala.collection.mutable.Map.empty[String, ArrayBuffer[TLPackage]]
- val updbuf: ArrayBuffer[TLPackage] = ArrayBuffer.empty[TLPackage]
- pkgs.foreach(pkg => {
- if (pkg.lrev.value > 0 && pkg.lrev.value < pkg.rrev.value) {
- // easy part: in the update tab show all packages in raw format
- // TODO if the main package is updated, too, use a child item?
- updbuf += pkg
- }
- // complicated part, determine whether it is a sub package or not!
- // we strip of initial texlive. prefixes to make sure we deal
- // with real packages
- if (pkg.name.value.stripPrefix("texlive.").contains(".")) {
- val foo: Array[String] = pkg.name.value.stripPrefix("texlive.infra").split('.')
- val pkgname = foo(0)
- val binname = foo(1)
- if (binbuf.keySet.contains(pkgname)) {
- binbuf(pkgname) += pkg
- } else {
- binbuf(pkgname) = ArrayBuffer[TLPackage](pkg)
- }
- } else {
- pkgbuf += pkg
- }
- })
- // now we have all normal packages in pkgbuf, and its sub-packages in binbuf
- // we need to create TreeItems
- val viewkids = pkgbuf.map { p => {
- val kids: Seq[TLPackage] = if (binbuf.keySet.contains(p.name.value)) {
- binbuf(p.name.value)
- } else {
- Seq()
- }
- // for ismixed we && all the installed status. If all are installed, we get true
- val allinstalled = (kids :+ p).foldRight[Boolean](true)((k,b) => k.installed.value == "Installed" && b)
- if (!allinstalled) {
- // replace installed status with "Mixed"
- new TreeItem[TLPackage](new TreeItem[TLPackage](
- new TLPackage(p.name.value, p.lrev.value.toString, p.rrev.value.toString, p.shortdesc.value, p.size.value.toString, "Mixed")
- )) {
- children = kids.map(new TreeItem[TLPackage](_))
- }
- } else {
- new TreeItem[TLPackage](p) {
- children = kids.map(new TreeItem[TLPackage](_))
- }
- }
- }}
- val updkids = updbuf.map { p => new TreeItem[TLPackage](p) }
- Platform.runLater {
- packageTable.root = new TreeItem[TLPackage](new TLPackage("root","0","0","","0","")) {
- expanded = true
- children = viewkids
- }
- updateTable.root = new TreeItem[TLPackage](new TLPackage("root","0","0","","0","")) {
- expanded = true
- children = updkids
- }
- update_update_menu_state()
- }
- })
- }
-
def callback_quit(): Unit = {
tlmgr.cleanup()
Platform.exit()
@@ -268,20 +185,29 @@ object ApplicationMain extends JFXApp {
outerrtabs.selectionModel().select(0)
outputText.append(s"Running $s")
val foo = Future {
+ // this is really a pain because with many lines being shot out
+ // from say fmtutil-sys --all, there are thousands of
+ // calls to Platform.runLater, which makes Java go to 100%
+ // but finally it succeeds.
+ // TODO use buffered update of output
s ! ProcessLogger(
- line => outputText.append(line),
- line => errorText.append(line)
+ line => Platform.runLater( outputText.append(line) ),
+ line => Platform.runLater( errorText.append(line) )
)
}
foo.onComplete {
case Success(ret) =>
- outputText.append("Completed")
- outputfield.scrollTop = Double.MaxValue
+ Platform.runLater {
+ outputText.append("Completed")
+ outputfield.scrollTop = Double.MaxValue
+ }
case Failure(t) =>
- errorText.append("An ERROR has occurred running $s: " + t.getMessage)
- errorfield.scrollTop = Double.MaxValue
- outerrpane.expanded = true
- outerrtabs.selectionModel().select(1)
+ Platform.runLater {
+ errorText.append("An ERROR has occurred running $s: " + t.getMessage)
+ errorfield.scrollTop = Double.MaxValue
+ outerrpane.expanded = true
+ outerrtabs.selectionModel().select(1)
+ }
}
}
@@ -295,55 +221,248 @@ object ApplicationMain extends JFXApp {
}.showAndWait()
}
- def callback_update_all(): Unit = {
- // TODO parse table and update line by line
- tlmgr_async_command("update --all", _ => { Platform.runLater { update_pkg_lists() } })
- }
-
- def callback_update_self(): Unit = {
- // TODO should we restart tlmgr here - it might be necessary!!!
- tlmgr_async_command("update --self", _ => { Platform.runLater { update_pkg_lists() } })
+ def callback_update(s: String): Unit = {
+ lineUpdateFunc = (l:String) => {
+ // println("line update: " + l + "=")
+ l match {
+ case u if u.startsWith("location-url") => None
+ case u if u.startsWith("total-bytes") => None
+ case u if u.startsWith("end-of-header") => None
+ case u if u.startsWith("end-of-updates") => None
+ case u if u == "OK" => None
+ case u if u.startsWith("tlmgr>") => None
+ case u =>
+ val foo = parse_one_update_line(l)
+ upds.remove(foo.name.value)
+ val op = pkgs(foo.name.value)
+ pkgs(foo.name.value) = new TLPackage(foo.name.value, op.rrev.value.toString, op.rrev.value.toString, foo.shortdesc.value, op.size.value.toString, "Installed")
+ Platform.runLater {
+ trigger_update("upds")
+ trigger_update("pkgs")
+ }
+ }
+ }
+ tlmgr_async_command(s"update $s", _ => {
+ Platform.runLater { lineUpdateFunc = { (s: String) => } }
+ })
}
def do_one_pkg(what: String, pkg: String): Unit = {
- tlmgr_async_command(s"$what $pkg", _ => { Platform.runLater { update_pkg_lists() } })
+ tlmgr_async_command(s"$what $pkg", _ => { Platform.runLater { update_pkgs_lists() } })
}
def callback_restore_pkg(str: String, rev: String): Unit = {
not_implemented_info()
}
- def callback_populate_backup_tab(): Unit = {
- // only run the restore command once
- if (backupTable.root.value.children.length == 0) {
-
- tlmgr_async_command("restore", lines => {
- // lines.drop(1).foreach(println(_))
- val foo = lines.drop(1).map { l =>
- val fields = l.split("[ ():]", -1).filter(_.nonEmpty)
- val pkgname = fields(0)
- val rests: Array[Array[String]] = fields.drop(1).sliding(4, 4).toArray
- if (rests.length == 1) {
- val p = rests(0)
- new TreeItem[TLBackup](new TLBackup(pkgname, p(0), p(1) + " " + p(2) + ":" + p(3)))
- } else {
- new TreeItem[TLBackup](new TLBackup(pkgname, "", "")) {
- children = rests.map(p => new TreeItem[TLBackup](
- new TLBackup(pkgname, p(0), p(1) + " " + p(2) + ":" + p(3))
- )).toSeq
+ bkps.onChange( (obs,chs) => {
+ var doit = chs match {
+ case ObservableMap.Add(k, v) => k.toString == "root"
+ case ObservableMap.Replace(k, va, vr) => k.toString == "root"
+ case ObservableMap.Remove(k, v) => k.toString == "root"
+ }
+ if (doit) {
+ // println("DEBUG bkps.onChange called new length = " + bkps.keys.toArray.length)
+ val newroot = new TreeItem[TLBackup](new TLBackup("root", "", "")) {
+ children = bkps
+ .filter(_._1 != "root")
+ .map(p => {
+ val pkgname: String = p._1
+ // we sort by negative of revision number, which give inverse sort
+ val versmap: Array[(String, TLBackup)] = p._2.toArray.sortBy(-_._2.rev.value.toInt)
+
+ val foo: Seq[TreeItem[TLBackup]] = versmap.tail.sortBy(-_._2.rev.value.toInt).map { q =>
+ new TreeItem[TLBackup](q._2)
+ }.toSeq
+ new TreeItem[TLBackup](versmap.head._2) {
+ children = foo
}
+ }).toArray.sortBy(_.value.value.name.value)
+ }
+ Platform.runLater {
+ backupTable.root = newroot
+ }
+ }
+ })
+ pkgs.onChange( (obs,chs) => {
+ var doit = chs match {
+ case ObservableMap.Add(k, v) => k.toString == "root"
+ case ObservableMap.Replace(k, va, vr) => k.toString == "root"
+ case ObservableMap.Remove(k, v) => k.toString == "root"
+ }
+ if (doit) {
+ val pkgbuf: ArrayBuffer[TLPackage] = ArrayBuffer.empty[TLPackage]
+ val binbuf = scala.collection.mutable.Map.empty[String, ArrayBuffer[TLPackage]]
+ pkgs.foreach(pkg => {
+ // complicated part, determine whether it is a sub package or not!
+ // we strip of initial texlive. prefixes to make sure we deal
+ // with real packages
+ if (pkg._1.stripPrefix("texlive.").contains(".")) {
+ val foo: Array[String] = pkg._1.stripPrefix("texlive.infra").split('.')
+ val pkgname = foo(0)
+ val binname = foo(1)
+ if (binbuf.keySet.contains(pkgname)) {
+ binbuf(pkgname) += pkg._2
+ } else {
+ binbuf(pkgname) = ArrayBuffer[TLPackage](pkg._2)
}
+ } else if (pkg._1 == "root") {
+ // ignore the dummy root element,
+ // only used for speeding up event handling
+ } else {
+ pkgbuf += pkg._2
}
- val newroot = new TreeItem[TLBackup](new TLBackup("root", "", "")) {
- children = foo
+ })
+ // now we have all normal packages in pkgbuf, and its sub-packages in binbuf
+ // we need to create TreeItems
+ val viewkids: ArrayBuffer[TreeItem[TLPackage]] = pkgbuf.map { p => {
+ val kids: Seq[TLPackage] = if (binbuf.keySet.contains(p.name.value)) {
+ binbuf(p.name.value)
+ } else {
+ Seq()
}
- Platform.runLater {
- backupTable.root = newroot
+ // for ismixed we && all the installed status. If all are installed, we get true
+ val allinstalled = (kids :+ p).foldRight[Boolean](true)((k, b) => k.installed.value == "Installed" && b)
+ if (!allinstalled) {
+ // replace installed status with "Mixed"
+ new TreeItem[TLPackage](new TreeItem[TLPackage](
+ new TLPackage(p.name.value, p.lrev.value.toString, p.rrev.value.toString, p.shortdesc.value, p.size.value.toString, "Mixed")
+ )) {
+ children = kids.map(new TreeItem[TLPackage](_))
+ }
+ } else {
+ new TreeItem[TLPackage](p) {
+ children = kids.map(new TreeItem[TLPackage](_))
+ }
}
- })
+ }
+ }
+ Platform.runLater {
+ packageTable.root = new TreeItem[TLPackage](new TLPackage("root", "0", "0", "", "0", "")) {
+ expanded = true
+ children = viewkids.sortBy(_.value.value.name.value)
+ }
+ }
+ }
+ })
+ upds.onChange( (obs, chs) => {
+ var doit = chs match {
+ case ObservableMap.Add(k, v) => k.toString == "root"
+ case ObservableMap.Replace(k, va, vr) => k.toString == "root"
+ case ObservableMap.Remove(k, v) => k.toString == "root"
+ }
+ if (doit) {
+ val infraAvailable = upds.keys.exists(_.startsWith("texlive.infra"))
+ val updatesAvailable = upds.keys.exists(p => !p.startsWith("texlive.infra"))
+ val newroot = new TreeItem[TLUpdate](new TLUpdate("root", "", "", "", "", "")) {
+ children = upds
+ .filter(_._1 != "root")
+ .map(p => new TreeItem[TLUpdate](p._2))
+ .toArray
+ .sortBy(_.value.value.name.value)
+ }
+ Platform.runLater {
+ update_self_menu.disable = !infraAvailable
+ update_all_menu.disable = !updatesAvailable
+ updateTable.root = newroot
+ }
+ }
+ })
+
+ def update_bkps_list(): Unit = {
+ tlmgr_async_command("restore", lines => {
+ // lines.drop(1).foreach(println(_))
+ val newbkps: Map[String, Map[String, TLBackup]] = lines.drop(1).map { (l: String) =>
+ val fields = l.split("[ ():]", -1).filter(_.nonEmpty)
+ val pkgname = fields(0)
+ val rests: Array[Array[String]] = fields.drop(1).sliding(4, 4).toArray
+ (pkgname, rests.map({ p => (p(1), new TLBackup(pkgname, p(0), p(1) + " " + p(2) + ":" + p(3)))}).toMap)
+ }.toMap
+ bkps.clear()
+ bkps ++= newbkps
+ trigger_update("bkps")
+ })
+ }
+ def update_pkgs_lists():Unit = {
+ tlmgr_async_command("info --data name,localrev,remoterev,shortdesc,size,installed", (s: Array[String]) => {
+ val newpkgs = s.map { (line: String) =>
+ val fields: Array[String] = line.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1)
+ val sd = fields(3)
+ val shortdesc = if (sd.isEmpty) "" else sd.substring(1).dropRight(1).replace("""\"""",""""""")
+ val inst = if (fields(5) == "1") "Installed" else "Not installed"
+ (fields(0), new TLPackage(fields(0), fields(1), fields(2), shortdesc, fields(4), inst))
+ }.toMap
+ pkgs.clear()
+ pkgs ++= newpkgs
+ trigger_update("pkgs")
+ })
+ }
+
+
+ def parse_one_update_line(l: String): TLUpdate = {
+ val fields = l.split("\t")
+ val pkgname = fields(0)
+ val status = fields(1) match {
+ case "d" => "Removed on server"
+ case "f" => "Forcibly removed"
+ case "u" => "Update available"
+ case "r" => "Local is newer"
+ case "a" => "New on server"
+ case "i" => "Not installed"
+ case "I" => "Reinstall"
}
+ val localrev = fields(2)
+ val serverrev = fields(3)
+ val size = humanReadableByteSize(fields(4).toLong)
+ val runtime = fields(5)
+ val esttot = fields(6)
+ val tag = fields(7)
+ val lctanv = fields(8)
+ val rctanv = fields(9)
+ val tlpkg: TLPackage = pkgs(pkgname)
+ val shortdesc = tlpkg.shortdesc.value
+ new TLUpdate(pkgname, status,
+ localrev + {
+ if (lctanv != "-") s" ($lctanv)" else ""
+ },
+ serverrev + {
+ if (rctanv != "-") s" ($rctanv)" else ""
+ },
+ shortdesc, size)
+ }
+
+ def update_upds_list(): Unit = {
+ tlmgr_async_command("update --list", lines => {
+ // val newupds = scala.collection.mutable.Map.empty[String,TLUpdate]
+ val newupds: Map[String, TLUpdate] = lines.filter { l =>
+ l match {
+ case u if u.startsWith("location-url") => false
+ case u if u.startsWith("total-bytes") => false
+ case u if u.startsWith("end-of-header") => false
+ case u if u.startsWith("end-of-updates") => false
+ case u => true
+ }
+ }.map { l =>
+ val foo = parse_one_update_line(l)
+ (foo.name.value, foo)
+ }.toMap
+ upds.clear()
+ upds ++= newupds
+ trigger_update("upds")
+ })
}
+ def trigger_update(s:String): Unit = {
+ // println("Triggering update of " + s)
+ if (s == "pkgs")
+ pkgs("root") = new TLPackage("root","0","0","","0","")
+ else if (s == "upds")
+ upds("root") = new TLUpdate("root", "", "", "", "", "")
+ else if (s == "bkps")
+ bkps("root") = Map[String,TLBackup](("0", new TLBackup("root","0","0")))
+ }
+
+
def callback_show_pkg_info(pkg: String): Unit = {
tlmgr_async_command(s"info $pkg", pkginfo => {
// need to call runLater to go back to main thread!
@@ -380,6 +499,19 @@ object ApplicationMain extends JFXApp {
})
}
+ /**
+ * @see https://stackoverflow.com/questions/3263892/format-file-size-as-mb-gb-etc
+ * @see https://en.wikipedia.org/wiki/Zettabyte
+ * @param fileSize Up to Exabytes
+ * @return
+ */
+ def humanReadableByteSize(fileSize: Long): String = {
+ if(fileSize <= 0) return "0 B"
+ // kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta
+ val units: Array[String] = Array("B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
+ val digitGroup: Int = (Math.log10(fileSize)/Math.log10(1024)).toInt
+ f"${fileSize/Math.pow(1024, digitGroup)}%3.1f ${units(digitGroup)}"
+ }
val mainMenu = new Menu("TLCockpit") {
items = List(
@@ -389,9 +521,12 @@ object ApplicationMain extends JFXApp {
new MenuItem("Update filename database ...") {
onAction = (ae) => callback_run_external("mktexlsr")
},
+ new MenuItem("Update context filename database ...") {
+ onAction = (ae) => callback_run_external("mtxrun --generate")
+ },
// calling fmtutil kills JavaFX when the first sub-process (format) is called
// I get loads of Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException
- // new MenuItem("Rebuild all formats ...") { onAction = (ae) => callback_run_external("fmtutil --sys --all") },
+ new MenuItem("Rebuild all formats ...") { onAction = (ae) => callback_run_external("fmtutil --sys --all") },
new MenuItem("Update font map database ...") {
onAction = (ae) => callback_run_external("updmap --sys")
},
@@ -500,44 +635,49 @@ object ApplicationMain extends JFXApp {
}
}
val updateTable = {
- val colName = new TreeTableColumn[TLPackage, String] {
+ val colName = new TreeTableColumn[TLUpdate, String] {
text = "Package"
cellValueFactory = { _.value.value.value.name }
prefWidth = 150
}
- val colDesc = new TreeTableColumn[TLPackage, String] {
+ val colStatus = new TreeTableColumn[TLUpdate, String] {
+ text = "Status"
+ cellValueFactory = { _.value.value.value.status }
+ prefWidth = 120
+ }
+ val colDesc = new TreeTableColumn[TLUpdate, String] {
text = "Description"
cellValueFactory = { _.value.value.value.shortdesc }
prefWidth = 300
}
- val colLRev = new TreeTableColumn[TLPackage, Int] {
+ val colLRev = new TreeTableColumn[TLUpdate, String] {
text = "Local rev"
cellValueFactory = { _.value.value.value.lrev }
prefWidth = 100
}
- val colRRev = new TreeTableColumn[TLPackage, Int] {
+ val colRRev = new TreeTableColumn[TLUpdate, String] {
text = "Remote rev"
cellValueFactory = { _.value.value.value.rrev }
prefWidth = 100
}
- val colSize = new TreeTableColumn[TLPackage, Int] {
+ val colSize = new TreeTableColumn[TLUpdate, String] {
text = "Size"
cellValueFactory = { _.value.value.value.size }
- prefWidth = 100
+ prefWidth = 70
}
- val table = new TreeTableView[TLPackage](
- new TreeItem[TLPackage](new TLPackage("root","0","0","","0","")) {
+ val table = new TreeTableView[TLUpdate](
+ new TreeItem[TLUpdate](new TLUpdate("root","","","","","")) {
expanded = false
}) {
- columns ++= List(colName, colDesc, colLRev, colRRev, colSize)
+ columns ++= List(colName, colStatus, colDesc, colLRev, colRRev, colSize)
}
- colDesc.prefWidth.bind(table.width - colName.width - colLRev.width - colRRev.width - colSize.width - 15)
+ colDesc.prefWidth.bind(table.width - colName.width - colLRev.width - colRRev.width - colSize.width - colStatus. width - 15)
table.prefHeight = 300
table.vgrow = Priority.Always
table.placeholder = new Label("No updates available")
table.showRoot = false
table.rowFactory = { _ =>
- val row = new TreeTableRow[TLPackage] {}
+ val row = new TreeTableRow[TLUpdate] {}
val ctm = new ContextMenu(
new MenuItem("Info") {
onAction = (ae) => callback_show_pkg_info(row.item.value.name.value)
@@ -552,7 +692,7 @@ object ApplicationMain extends JFXApp {
},
new MenuItem("Update") {
// onAction = (ae) => callback_run_text("update " + row.item.value.name.value)
- onAction = (ae) => do_one_pkg("update", row.item.value.name.value)
+ onAction = (ae) => callback_update(row.item.value.name.value)
}
)
row.contextMenu = ctm
@@ -609,9 +749,6 @@ object ApplicationMain extends JFXApp {
},
new MenuItem("Remove") {
onAction = (ae) => do_one_pkg("remove", row.item.value.name.value)
- },
- new MenuItem("Update") {
- onAction = (ae) => do_one_pkg("update", row.item.value.name.value)
}
)
row.contextMenu = ctm
@@ -665,14 +802,14 @@ object ApplicationMain extends JFXApp {
vgrow = Priority.Always
tabs = Seq(
new Tab {
- text = "Updates"
+ text = "Packages"
closable = false
- content = updateTable
+ content = packageTable
},
new Tab {
- text = "Packages"
+ text = "Updates"
closable = false
- content = packageTable
+ content = updateTable
},
new Tab {
text = "Backups"
@@ -684,8 +821,12 @@ object ApplicationMain extends JFXApp {
pkgstabs.selectionModel().selectedItem.onChange(
(a,b,c) => {
if (a.value.text() == "Backups") {
- callback_populate_backup_tab()
- // println("Entering Backup Tab")
+ if (backupTable.root.value.children.length == 0)
+ update_bkps_list()
+ } else if (a.value.text() == "Updates") {
+ // only update if not done already
+ if (updateTable.root.value.children.length == 0)
+ update_upds_list()
}
}
)
@@ -738,9 +879,22 @@ object ApplicationMain extends JFXApp {
}
*/
+ var lineUpdateFunc = { (l: String) => } // println(s"DEBUG: got ==$l== from tlmgr") }
+
+ val bar = Future {
+ while (true) {
+ val s = outputLine.take
+ try {
+ lineUpdateFunc(s)
+ } catch {
+ case e: Exception => println("SOMETHING BAD IN lineUpdateFUnc happened: " + e + "\n")
+ }
+ }
+ }
tlmgr.start_process()
tlmgr.get_output_till_prompt()
- update_pkg_lists() // this is async done
+ // update_pkg_lists_to_be_renamed() // this is async done
+ update_pkgs_lists()
} // object ApplicationMain
diff --git a/Master/texmf-dist/source/support/tlcockpit/src/main/scala/TeXLive/TLUpdate.scala b/Master/texmf-dist/source/support/tlcockpit/src/main/scala/TeXLive/TLUpdate.scala
new file mode 100644
index 00000000000..427ad722ceb
--- /dev/null
+++ b/Master/texmf-dist/source/support/tlcockpit/src/main/scala/TeXLive/TLUpdate.scala
@@ -0,0 +1,15 @@
+package TeXLive
+
+import scalafx.beans.property.{ObjectProperty, StringProperty}
+
+// Note!!! we have to use ObjectProperty[Int] here instead of IntegerProperty
+// since IntegerProperty does NOT implement Observable[Int,Int]
+// see https://github.com/scalafx/scalafx/issues/243
+case class TLUpdate(name: StringProperty, status: StringProperty, lrev: StringProperty, rrev: StringProperty,
+ shortdesc: StringProperty, size: StringProperty) {
+ def this(_name: String, _status: String, _lrev: String, _rrev: String, _shortdesc: String, _size: String) =
+ this(
+ StringProperty(_name), StringProperty(_status), StringProperty(_lrev), StringProperty(_rrev),
+ StringProperty(_shortdesc), StringProperty(_size)
+ )
+} \ No newline at end of file
diff --git a/Master/texmf-dist/source/support/tlcockpit/src/main/scala/TeXLive/TlmgrProcess.scala b/Master/texmf-dist/source/support/tlcockpit/src/main/scala/TeXLive/TlmgrProcess.scala
index 360ad9bc433..c65058e169a 100644
--- a/Master/texmf-dist/source/support/tlcockpit/src/main/scala/TeXLive/TlmgrProcess.scala
+++ b/Master/texmf-dist/source/support/tlcockpit/src/main/scala/TeXLive/TlmgrProcess.scala
@@ -8,7 +8,7 @@ import scala.collection.mutable.ArrayBuffer
import scala.concurrent.SyncVar
import scala.sys.process.{Process, ProcessBuilder, ProcessIO}
-class TlmgrProcess(updout: Array[String] => Unit, upderr: String => Unit) {
+class TlmgrProcess(updout: Array[String] => Unit, upderr: String => Unit, updline: String => Unit) {
val inputString = new SyncVar[String] // used for the tlmgr process input
val outputString = new SyncVar[String] // used for the tlmgr process output
val errorBuffer: StringBuffer = new StringBuffer() // buffer used for both tmgr process error console AND logging
@@ -26,12 +26,18 @@ class TlmgrProcess(updout: Array[String] => Unit, upderr: String => Unit) {
val maxWaitTime = 5000
var waited = 0
+ var sendNL = 0
while (isBusy) {
// do busy waiting here in case some other tlmgr command is running
// println("Debug: waiting for tlmgr being ready, want to send " + input)
Thread.sleep(300)
waited += 300
- if (waited > maxWaitTime) {
+
+ if (waited > maxWaitTime && sendNL < 3) {
+ outputString.put("protocol")
+ sendNL += 1
+ }
+ if (waited > maxWaitTime && sendNL >= 3) {
throw new Exception("tlmgr busy, waited too long, aborting calling: " + input)
}
}
@@ -129,11 +135,13 @@ class TlmgrProcess(updout: Array[String] => Unit, upderr: String => Unit) {
while (true) {
line = reader.readLine
if (line == null) {
+ // println("DDD: got null line")
// println("Did read NULL from stdin giving up")
// error = true
} else {
// println("did read " + line + " from process")
outputString.put(line)
+ updline(line)
}
}
stdOut.close()