sourcecamembert::Sys.fan

//
// Copyright (c) 2012, Brian Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   22 Apr 12  Brian Frank  Creation
//

using gfx
using concurrent
using wisp
using netColarUtils

**
** Sys manages references to system services
**
const class Sys : Service
{
  ** Logger
  static const Log log := Log.get("camembert")

  const ProjectRegistry prjReg

  ** The main options file
  const File optionsFile

  ** Shortcuts config
  const Shortcuts shortcuts

  ** Configuration options
  const Options options

  ** Theme
  const AtomicRef _theme := AtomicRef()

  const Template[] templates

  const LicenseTpl[] licenses

  ** Application level commands
  const Commands commands

  const WispService docServer

  const Unsafe pm

  ** Top-level frame (only in UI thread)
  Frame frame() { Actor.locals["frame"] ?: throw Err("Not on UI thread") }

  ProcessManager processManager() { (ProcessManager) pm.val }

  new make(|This|? f)
  {
    if(f!=null) f(this)
    options = Options.load(optionsFile)
    shortcuts =  Shortcuts.load(optionsFile.parent)
    _theme.val = Theme.load(`${optionsFile.parent}/themes/${options.theme}.props`.toFile)
    commands = Commands(this)
    prjReg = ProjectRegistry(options.srcDirs, optionsFile.parent)
    wPort := NetUtils.findAvailPort(8787)
    docServer = WispService { port = wPort; root = DocWebMod() }.start
    pm = Unsafe(ProcessManager())

    // read the templates
    tpl := Template[,]
    (optionsFile.parent + `templates/`).listFiles.each
    {
      tpl.add(JsonUtils.load(it.in, Template#))
    }
    templates = tpl.sort |a, b| {a.name <=> b.name}

    // read the licenses
    lic := LicenseTpl[,]
    (optionsFile.parent + `licenses/`).listFiles.each
    {
      lic.add(JsonUtils.load(it.in, LicenseTpl#))
    }
    licenses = lic.sort |a, b| {a.name <=> b.name}

  }

  Theme theme()
  {
    return _theme.val
  }

  override Void onStart()
  {
    PluginManager.cur.onConfigLoaded(this)
  }

  override Void onStop()
  {
    PluginManager.cur.onShutdown()

    // TODO: gotta be generalized or moved to Fantom pugin too
    if(docServer.isRunning)
    {
      docServer.stop
      docServer.uninstall
    }
    prjReg.pool.stop

    Actor.sleep(1sec)

    PluginManager.cur.onShutdown(true)
    prjReg.pool.kill

    Sys.log.info("Sys.onStop completed.")
  }

  ** Reload the *whole* config including all plugins
  static Void reloadConfig()
  {
    frame := Sys.cur.frame
    frame.spaces.each {Sys.cur.frame.closeSpace(it)}

    optionsFile := Sys.cur.optionsFile
    // Note : calling manually onStop to make sure it fully stops before we restart
    // because sys.stop calls it asynchronously
    Sys.cur.onStop

    Sys.cur.uninstall

    Sys sys := Sys
    {
      it.optionsFile = optionsFile
    }
    sys.start

    // Update theme menu
    m := (frame.menuBar as MenuBar)
    m.buildThemesMenu
    m.relayout; m.repaint

    // rescan projects after a sys chnage
    ProjectRegistry.scan

    // also update menus
    PluginManager.cur.onFrameReady(Sys.cur.frame, false)
  }

  ** All known plugins
  Str:Plugin plugins() {PluginManager.cur.plugins}

  ** Retrieve a given plugin instance by it's name
  Plugin? plugin(Str name)
  {
    return PluginManager.cur.plugins[name]
  }

  static Sys cur()
  {
    return (Sys) Service.find(Sys#)
  }

  Uri[] srcRoots() {options.srcDirs}

  static File confDir()
  {
    File.os((Env.cur.workDir + `etc/camembert/camembert.props`).readProps["configDir"])
  }
}