!7

sS background = #1101302; // #1009931;
sS backgroundMode = 'fit;
sS mainIconID = #1101346, moduleDefaultIconID = #1101337;
sS laf;
static double minLoadScreenShowingTime = 3.0;
static JDesktopPane desktop;

// TODO: merge with systemQ
static ReliableSingleThread rst = new(f updateModules);

static volatile long updateCycles;
static int systemErrorsToKeep = 10;
static L systemErrors;
static int systemErrors_pointer;
static new Flag stopRequested;
static SimpleLiveValue<S> systemStatus = stringLiveValue("loading");
static Map<O, GhostModule> ghostModules = weakHashMap(); // key is main class (if dynamic module) or instance (if static module)
static Q systemQ;
static AccessControlContext globalACC;
static volatile Module activeModule;
static Set<Module> onActiveModuleChange = synchroHashSet();
static JTextField tfTopInput; // the input field in the OS menu bar

p {
  //if (isDevVersion()) laf = 'webLAF;
  
  if (!hasClass("x30_pkg.x30_util")) {
    if (!zipFileContains_falseOnError(pathToJavaxJar(), "x30_pkg/x30_util.class"))
      upgradeJavaXAndRestart();
    else
      restart();
  }
  
  _handleException_addHandler(voidfunc(Throwable e) {
    try {
      synchronized(systemErrors) {
        listSet(systemErrors, systemErrors_pointer, e);
        systemErrors_pointer = (systemErrors_pointer+1) % systemErrorsToKeep;
      }
    } catch e2 {
      printStackTrace(e2);
    }
    infoBox("Error: " + exceptionToStringShort(e));
    LastErrors lastErrors = first(staticModulesOfType(LastErrors));
    if (lastErrors != null) lastErrors.addError(e);
  });
  
  timeJumpDetector();

  monitorThreadAllocatedMemory();
  
  print("Data dir: " + javaxDataDir());
  globalACC = acc_current();
  
  if (swic(activateFramesOf(programIDPlusHome()), "OK")) cleanKill();
  framesBot();
  
  //substance();
  final SimpleLiveValue<S> lvDetails = stringLiveValue();
  lvDetails.onChange(r { print(lvDetails!) });
  stefansOS_loadingAnimation_fullScreen = startInFullScreen() && !isWindows() /* bug */;
  final Component loadingAnim = stefansOS_loadingAnimation(r {
    stopRequested.raise()
  }, lvDetails);
  
  try {
    long start = sysNow();
    systemQ = startQ("System Q");
    useDBOf(#1015871);
    lvDetails.set("Loading database");
    db();
    lvDetails.set("Loading background");
    background = or2(trim(loadTextFile(javaxDataDir("os-background.txt"))), background);
    if (eq(backgroundMode, 'fit))
      desktop = jDesktopPaneWithFitPicture(background);
    else
      desktop = jDesktopPaneWithSkyPicture/*_autoUnload*/(background, Color.black);
    setMainDesktopPane(desktop);
    autoFixDesktopPane(desktop);
    long ms = toMS(minLoadScreenShowingTime);
    long remaining = start+ms-sysNow();
    if (remaining <= 0)
      lvDetails.set("Done loading");
    else {
      S seconds = formatDouble(toSeconds(remaining), 1);
      lvDetails.set("Done loading, just showing you the nice animation for " + seconds + " more second" + (eq(seconds, "1") ? "" : "s"));
    }
    if (loadingAnim != null)
      waitUntilSysTimeOrFlag(start+ms, stopRequested);
  } catch e {
    _handleException(e);
    stopRequested.raise();
  }
  
  try {
    bool flag = stopRequested.isUp(); // get before closing animation
    if (flag)
      ret with showFrame("Stop screen",
        centerAndSouthWithMargins(
          jcenteredlabel("Troubleshooting options will go here."),
          jcenteredbuttons(
            "Start Stefan's OS normally", r restart,
            "Switch to " + otherVersionName(), r startOtherVersion,
            "Delete session", disableButtonWhileCalcing(func -> bool {
              if (!fileExists(conceptsFile()))
                ret false with infoBox("Session already empty.");
              if (!confirmOKCancel("Really delete your current session?")) false;
              renameFileToSomeBackupName(conceptsFile());
              infoBox("Session deleted. Starting again.");
              disableAllButtonsInWindow(heldInstance(JButton));
              sleepSeconds(3);
              restart();
              false;
            }))));
  
    //autoRestart();
    serverAutoRestartMD5(); // just a short ping
    
    setLookAndFeel();
    
    showDesktop();
  } finally {
    disposeWindow(loadingAnim);
  }
    
  initAfterDBLoad();
  hideConsole();
  manualConsole();
  clearConsole();
  if (headless()) sleep();
}

sbool startInFullScreen() {
  ret eq("1", trim(loadTextFile(javaxDataDir("start-os-in-full-screen.txt"))));
}

svoid setStartInFullScreen(bool b) {
  saveTextFile(javaxDataDir("start-os-in-full-screen.txt"), b ? "1" : "0");
}
sbool showDesktop_first = true;

svoid showDesktop {
  if (headless()) ret;
  bool done = false;
  if (showDesktop_first && startInFullScreen()) pcall {
    showFullScreen(desktop);
    done = true;
  }

  if (!done) {
    showMaximizedFrame(desktop);
    titlePopupMenu_top(desktop, voidfunc(JPopupMenu menu) {
      addMenuItems(menu,
        "Update One Cycle", rst,
        "New Session", rThreadPcallMessageBox(r deleteAllModules),
        "Restore Initial Modules", r initialModules,
      );
    });
  }
  
  fillMenuBar();
  doNothingOnClose(getFrame(desktop));
  frameIcon(mainIconID, desktop);
  showDesktop_first = false;
  cleanExitOnFrameClose_ifStillInSameFrame(desktop);
}

sbool inFullScreen() {
  ret isFullScreen(desktop);
}

svoid fullScreenOff() {
  if (inFullScreen()) fullScreenOnOff();
}

svoid fullScreenOnOff {
  // Don't screw up window positions while adjusting from/to fullscreen
  autoFixDesktopPane_exclude.add(desktop);
  temp tempAfterwards(r { autoFixDesktopPane_exclude.remove(desktop) });
  
  Window w = getWindow(desktop);
  if (!inFullScreen()) {
    showFullScreen(frameTitle(desktop), desktop);
    pcall { frameIcon(mainIconID, desktop); }
    cleanExitOnFrameClose_ifStillInSameFrame(desktop);
    fillMenuBar();
    setStartInFullScreen(true);
  } else {
    removeFromParent(desktop);
    showDesktop();
    setStartInFullScreen(false);
  }
  
  // Allow closing empty "zombie" windows (why do we have these?)
  final JFrame f = getFrame(desktop);
  onFrameClosing(f, r { if (isEmptyFrame(f)) {
    removeListener();
    disposeWindow(f);
  }});
  
  disposeWindow(w);
  fixDesktopPane(desktop);
}

svoid fillMenuBar() swing {
  // if you use Processing
  JPopupMenu.setDefaultLightWeightPopupEnabled(false);
  
  JMenuBar menuBar = addMenuBar(desktop);
  menuBar.setLayout(new javax.swing.plaf.basic.DefaultMenuLayout(menuBar, BoxLayout.X_AXIS));
  set jmenuItem_newThreads;
  S version = ai_versionFromName(programName());
  final bool dev = isDevVersion();
  S name = "Stefan's OS " + version;
  S home = userHomeIfNotActual();
  S actual = actualUserHome();
  S prefix = addFileSep(actualUserHome());
  print("home: " + home + ", actual: " + actual);
  if (home != null) name += " [" + dropPrefix(prefix, home) + "]";
  addMenu(desktop, trim(name),
    "Screenshot", r stefansOS_screenshot,
    "Full screen on/off", r fullScreenOnOff,
    "Switch to " + otherVersionName(), r {
      innerCleanUp();
      startOtherVersion();
      cleanKill();
    },
    "---",
    "Restart Stefan's OS", r { showConsole(); restart(); },
    "Exit", r cleanKill
  );
  
  addMenu(desktop, "Modules",
    "Other Module...", r stefansOS_addDynamicModuleDialog,
    "---",
    "Welcome Screen", r { makeOrShowModule("#1016067/WelcomeScreen") },
    //"Hello (input field for everything)", r { makeOrShowModule("Hello") },
    "Task Bar", r { makeOrShowModule("#1016123/TaskBar") },
    jmenu("Internal Types", map(myNonAbstractClassesImplementing(Module), func(final Class c) -> JMenuItem {
      jMenuItem(shortClassName(c), rThread { makeOrShowStaticModuleOfType(c) })
    })),
    "Module Classes", r {  makeOrShowStaticModuleOfType(ModuleClasses) },
  );
  
  addMenu(desktop, "More",
    "Start External JavaX Program...", r stefansOS_startExternalJavaXProgram,
    "---",
    "Find memory leaks", r stefansOS_findMemoryLeaks);
    
  // Add the fancy stuff
  
  swing {
    JMenuBar menuBar = cast call(getFrame(desktop), 'getJMenuBar);
    if (!containsChildOfType(menuBar, JLabel.class)) {
      //menuBar.add(Box.createHorizontalGlue());
      menuBar.add(Box.createHorizontalStrut(20));
      tfTopInput = jcenteredtextfield(uniq(TopInput).text);
      onChange(tfTopInput, r {
        cset(uniq(TopInput), text := getText(tfTopInput))
      });
      onEnter(tfTopInput, r {
        katze_userTyped(getText(tfTopInput))
      });
      //menuBar.add(jMinWidth(100, tfTopInput));
      menuBar.add(tfTopInput);
      //menuBar.add(jhgrid(null, tfTopInput));
      //menuBar.add(jhgrid(tfTopInput, null));
      menuBar.add(Box.createHorizontalStrut(20));
      menuBar.add(jLiveValueLabel(clockTimeLiveValue()));
      menuBar.add(Box.createHorizontalStrut(6));
    }
  }
}

static AutoCloseable tempBusyAnimation(fS text) {
  if (headless()) null;
  final JComponent anim = jBackground(Color.white, jAnimation(#1101316, text));
  swing {
    addInternalFrame_dontSelect.set(true);
    addInternalFrame_layer.set(JLayeredPane.POPUP_LAYER);
    addInternalFrame(desktop, text, null, anim);
    packInternalFrameInTopRightCorner(anim);
    JInternalFrame f = getInternalFrame(anim);
    print("Have busy animation: " + f + " in " + getParent(f));
  }
  ret tempDisposeInternalFrame(anim);
}

svoid initAfterDBLoad {
  doEvery(30000, r {
    applyStandardSwingFixes();
    cleanDefunctACCsInAllThreads();
  });
  doEvery(60000, r clearSnippetTitleCache);
  set cleanUp_interruptThreads; 
  
  // for Java Chrome
  classForNameOpt("java.util.prefs.FileSystemPreferences");
  
  temp tempBusyAnimation("Restoring Session");
  
  initialModules();
  
  UpdateCycles uc = conceptWhere(UpdateCycles);
  if (uc != null) updateCycles = uc.value;
  
  for (Module m : onModules()) startModule(m);
  
  addConceptIndex(simpleConceptIndex(rst));
  rst.trigger();

  systemStatus.set("running");
}

svoid initialModules {
  if (empty(onModules()))
    showModule(new DynamicModule(#1016067, 'main$WelcomeScreen));
  //makeOrShowStaticModuleOfType(ModuleClasses);
  //makeOrShowStaticModuleOfType(Hello);
  if (headless()) makeOrShowModule("#1016576/ConnectToServer");
}

svoid triggerUpdate { rst.trigger(); }

svoid updateModules {
  ++updateCycles;
  for (Module m : onModules())
    updateModule(m);
}

svoid updateModule(Module m) {
  if (m == null) ret;
  temp m.enter();
  pcall { m.update(); }
}

svoid updateModules(final Collection<Module> l) {
  systemQ.add(r {
    for (Module m : unnull(l)) updateModule(m);
  });
}

svoid cleanMeUp {
  autoConsole();
  showConsole();
  systemStatus.set("shutting down");
  temp tempBusyAnimation("Shutting Down");
  for (Module m) if (m.vis != null) pcall {
    m.unvisualize();
  }
  
  for (Module m) cleanUp(m);
  cleanedUp = true;
}

static L<Module> onModules() { ret conceptsWhere(Module, on := true); }

sbool hasModuleWithFields(Class<? extends Module> c, O... params) {
  ret hasConcept(c, concatArrays(new O[] {on := true}, params));
}

svoid startModule(Module m) { ping(); pcall {
  //addIfNotThere(modules, m);
  temp m.enter();
  if (m.started) ret;
  m.started = true;
  print("Starting module " + m.moduleName());
  try {
    m.start();
  } catch e {
    m.setError(e);
    _handleException(e);
  }
  rst.trigger();
  if (m.visible) showModule(m);
}}

static Module showModule(final Module m) {
  if (m == null) ret m;
  startModule(m);
  if (headless()) ret m;
  temp m.enter();
  if (m.vis != null) {
    if (!isLoading())
      activateInternalFrame(m.vis);
    ret m;
  }
  cset(m, visible := true);
  visualizeModule(m);
  if (m.vis != null) swing {
    Rect r = m.frameRect;
    /*if (r == null) r = randomRect(desktop.getWidth(), desktop.getHeight(), 10, 150, 100);
    if (r == null) r = Rect(10, 10, 200, 100);
    print("Showing frame at " + r);*/
    S frameTitle = m.moduleName();
    final JInternalFrame f;
    {
      temp tempSetThreadLocal(addInternalFrame_dontSelect, isLoading());
      f = showInternalFrame(desktop, frameTitle, r, m.vis);
    }
    if (r == null) centerPackInternalFrame(f);
    f.setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE);
    
    final WeakReference<JInternalFrame> fRef = new(f);
    final WeakReference<Module> mRef = new(m);
    internalFrameTitlePopupMenuItem(f, "Module source", r-thread { pcall {
      S src = mRef->sourceCode();
      if (src != null) showText(internalFrameTitle(fRef!) + " [Source]", src); else infoBox("No source code found");
    }});
    onInternalFrameIconified(f, r { hideModule(m) });
    onInternalFrameClosing(f, r { deleteModule(m) });
    onInternalFrameActivated(f, r { setActiveModule(m) });
    onInternalFrameDeactivated(f, r { if (activeModule == m) setActiveModule(null) });
    internalFrameIcon(f, or2(m.iconID, moduleDefaultIconID));
    m.enhanceFrame(f);
  }
  ret m;
}

svoid showModules(L<? extends Module> l) {
  for (Module m : unnull(l)) showModule(m);
}

sbool deleteModule(Module m) {
  pcall {
    bool warn = isTrue(callOpt(resolveModule(m), 'warnOnDelete));
    if (warn && !confirmOKCancel("Really delete module " + m + "?"))
      false;
  }
  onActiveModuleChange.remove(m);
  O m2 = unwrapDynamicModule(m);
  ghostModules.put(m == m2 ? m : m2.getClass(), nu(GhostModule,
    name := m.moduleName(),
    created := m.created,
    deleted := now(),
    instance := new WeakReference(m2)));
  pcall { m.unvisualize(); }
  pcall {
    logStructure(deletedModulesLogFile(), m);
  }
  removeConcept(m);
  triggerUpdate();
  true;
}

svoid visualizeModule(Module m) {
  pcall {
    if (m.vis == null) m.vis = m.visualize();
    if (m.vis == null) m.vis = defaultVisualize(m);
  }
}

svoid hideModule(final Module m) {
  if (m == null) ret;
  temp m.enter();
  cset(m, visible := false);
  pcall { m.unvisualize(); }
}

svoid revisualizeModule(Module m) {
  pcall {
    if (m == null) ret;
    temp m.enter();
    JInternalFrame frame = getInternalFrame(m.vis);
    if (frame == null) ret;
    m.unvisualize1b();
    m.unvisualize2();
    visualizeModule(m);
    setInternalFrameContents(frame, m.vis);
  }
}

abstract concept Module {
  transient JComponent vis;
  transient bool started;
  transient Lock lock = lock();

  bool on = true, visible;
  Rect frameRect;
  S iconID;
  PersistableThrowable error;
  
  //transient Set timers;
  
  JComponent visualize() { null; }
  void unvisualize() {
    pcall { unvisualize1(); }
    pcall { unvisualize2(); }
  }
  void enhanceFrame(JInternalFrame frame) {}
  void start() {}
  void unvisualize1() {
    disposeInternalFrame(getInternalFrame(vis));
    unvisualize1b();
  }
  
  void unvisualize1b() {
    grabFrameRect();
    vis = null;
  }
  void unvisualize2() {}
  void update() {}
  
  void grabFrameRect() {
    JInternalFrame f = getInternalFrame(vis);
    if (f != null)
      cset(this, frameRect := toRect(getBounds(f)));
  }
  
  Rect getFrameRect() {
    grabFrameRect();
    ret frameRect;
  }
  
  void cleanMeUp_started() { started = false; }

  void delete() {
    unvisualize();
    cleanUp(this);
    super.delete();
  }

  S sourceCode() {  
    ret javaxSourceOfMyClass1(shortClassName(this));
  }
  
  // for all modules
  void triggerUpdate { rst.trigger(); }
  
  void setModuleIcon(S iconID) {
    if (eq(iconID, this.iconID)) ret;
    this.iconID = iconID;
    internalFrameIcon(vis, iconID);
  }
  
  O resolve() { ret this; }
  O getError() { ret error; }
  S moduleID() { ret str(id); }
  
  void setError(Throwable e) {
    cset(this, error := persistableThrowable(e));
  }
  
  AutoCloseable enter() { null; }
  
  JComponent vis() { ret vis; }
  
  static bool needsParameters() { false; }
  
  S moduleName() {
    ret humanizeFormLabel(shortClassName(this));
  }
} // END CONCEPT MODULE

static JComponent defaultVisualize(Module m) {
  ret jCenteredMultiLineLabel(renderConcept(m)); 
}

static <A extends Module> A findModule(Class<A> c) {
  ret findConcept(c, on := true);
}

// returns module ID
static S findDynModuleOfType(S type) {
  DynamicModule m = findConcept(DynamicModule, on := true, _className := "main$" + type);
  ret m == null ? null : m.moduleID();
}

sbool moduleTypeIs(Module m, S type) {
  if (m == null) false;
  ret eq(moduleResolvedClassName(m), "main$" + type);
}

sS moduleResolvedClassName(Module m) {
  if (m == null) null;
  if (m instanceof DynamicModule)
    ret m/DynamicModule._className;
  ret className(m);
}

// returns module ID
sS findClosestModuleTo(O searcher, S type) {
  JInternalFrame f = getInternalFrame(dm_getVisualization(searcher));
  if (f == null) null;
  Pt p = centerOfRect(toRect(getBounds(f)));
  new Lowest<S> best;
  for (Module m : onModules()) {
    JInternalFrame f2 = getInternalFrame(m.vis);
    if (f2 == null || f2 == f) continue;
    if (type != null && !moduleTypeIs(m, type)) continue;
    Rect r2 = toRect(getBounds(f2));
    best.put(m.moduleID(), rectPointDistance(r2, p));
  }
  ret best!;
}

static <A extends Module> L<A> staticModulesOfType(Class<A> type) {
  ret conceptsWhere(type, on := true);
}

static <A extends Module> L<A> staticModulesOfExactType(Class<A> type) {
  ret filterByExactType(type, staticModulesOfType(type));
}

static L listModules() {
  ret map unwrapDynamicModule(onModules());
}

static L visibleModules() {
  ret map unwrapDynamicModule(objectsWhere(onModules(), visible := true));
}

static O unwrapDynamicModule(Module m) {
  ret m instanceof DynamicModule ? or(m/DynamicModule.o, m) : m;
}

sbool moduleStillThere(O o) {
  Module m = o instanceof Module ? o/Module : (Module) get(o, '_host);
  ret isConceptRegistered(mainConcepts, m);
}

static O getDynModuleByID(S moduleID) {
  if (moduleID == null) null;
  ret resolveModule(getConcept(Module, parseLong(moduleID)));
}
  
static Module getModuleByID(S moduleID) {
  if (moduleID == null) null;
  ret getConcept(Module, parseLong(moduleID));
}
  
sS getInterestingString {
  InterestingString m = findModule(InterestingString);
  ret m == null ? null /*getText(tfTopInput)*/ : m.theString;
}

sS modulesSessionGrab() {
  grabFrameRects();
  ret struct(ll(programID(), localDateWithMilliseconds())) + "\n"
    + mainConcepts.xfullgrab();
}

svoid autoSaveModulesSession() {
  infoBox("Auto-saving session.");
  S grab;
  logQuoted(javaxBackupDir(fsI(programID()) + "/auto-saved-sessions.txt"), grab = modulesSessionGrab());
  infoBox("Auto-save done (" + l(grab) + " chars)");
}

svoid deleteAllModules() {
  autoSaveModulesSession();
  deleteConcepts(Module);
  initialModules();
}

svoid restoreModulesSession(S text) {
  autoSaveModulesSession();
  systemStatus.set("shutting down");
  infoBox("Releasing session");
  cleanMeUp();
  cleanUp(mainConcepts);
  mainConcepts = null;
  //sleepSeconds(1);
  infoBox("Loading session");
  systemStatus.set("loading");
  mainConcepts = new Concepts().load(dropFirstLine(text));
  initAfterDBLoad();
  infoBox("Session restore done");
}

svoid grabFrameRects {
  for (Module m : onModules()) m.grabFrameRect();
}

sclass DynamicModule extends Module {
  S moduleID, _className; // "className" is taken by DynamicObject; _className == null for old-style dyn module
  S oStruct; // serialized dynamic object
  sbool reload_replaceFrame = true;
  
  transient Class c;
  transient O o;

  *() {}
  *(S *moduleID, S *_className) {}
  *(S *moduleID, S *_className, Class *c) {}
  
  static bool needsParameters() { true; }
    
  AutoCloseable enter() {
    ret castForTemp(callOpt(o, 'enter));
  }

  JComponent visualize() {
    temp enter();
    ret (JComponent) callOpt(o, 'visualize);
  }
  
  void enhanceFrame(final JInternalFrame f) {
    internalFrameTitlePopupMenuItem(f, "Reload", rThread(r reload));
    {
      temp enter();
      pcallOpt(o, 'enhanceFrame, f);
    }
    internalFrameTitle(f, moduleName());
  }
  
  S moduleName() {
    S name = (S) callOpt(o, 'moduleName);
    if (nempty(name)) ret name;
    S title = snippetTitle_cached(moduleID);
    //ret dropSuffixICTrimOneOf(title, "[Dyn Module]", "[Dyn Module, OK]", "[Dyn Module, shortened]");
    ret dropTrailingSquareBracketStuff(title);
  }
  
  void start() {
    if (moduleID == null) ret;
    if (c == null) c = hotwireModule(moduleID);
    if (oStruct != null) pcall {
      o = unstructureInRealm(oStruct, c);
    }
    if (o == null)
      if (_className == null)
        o = c;
      else
        o = nu(_getClass(c, _className));
    setOptAll(o, _host := this, lock := lock);
    temp enter();
    if (o instanceof Class)
      callMain(o);
    else
      callOpt(o, 'start);
  }
  
  void unvisualize2() { callOpt(o, 'unvisualize); }
  
  void update() { callOpt(o, 'update); }
  
  void cleanMeUp() {
    oStruct = null; pcall { if (o != null && !o instanceof Class) oStruct = struct(o); }
    cleanUpObjectAndItsMainClass(o);
    o = null;
    c = null;
  }
  
  void reload() {
    JInternalFrame frame = getInternalFrame(vis);
    unvisualize1b();
    unvisualize2();
    
    if (o != null)
      ghostModules.put(o.getClass(), nu(GhostModule,
        name := moduleName(),
        created := created,
        deleted := now(),
        instance := new WeakReference(o)));

    cleanUp(this); // also sets started to false
    
    if (frame != null)
      setInternalFrameContents(frame, jcenteredlabel("Reloading..."));
    visible = false;
    startModule(this);
    if (frame != null) {
      if (reload_replaceFrame) { // avoids some bugs
        cset(this, frameRect := toRect(getBounds(frame)));
        disposeInternalFrame(frame);
        showModule(this);
      } else {
        cset(this, visible := true);
        visualizeModule(this);
        //print("New content: " + vis);
        setInternalFrameContents(frame, vis);
      }
    }
    rst.trigger();
  }
  
  S sourceCode() {  
    ret loadSnippet(moduleID);
  }
  
  O resolve() { ret o; }
  O getError() {
    if (o != null) {
      O error = callOpt(o, 'getError);
      if (error != null) ret error;
    }
    ret super.getError();
  }
  
  toString {
    ret "DynModule " + moduleID + "/" + shortenClassName(_className);
  }
  
  void setError(Throwable e) {
    if (o != null && isTrue(callOpt(o, 'setError, e))) ret;
    super.setError(e);
  }

}

/*static L resolvedModules() {
  new L l;
  for (Module m : onModules())
    l.add(m.resolve());
  ret l;
}*/

static S moduleID(Module m) {
  ret m == null ? null : m.moduleID();
}

static O resolveModule(Module m) {
  ret m == null ? null : m.resolve();
}

static S makeModule(Class<? extends Module> moduleClass) {
  ret makeModule(shortClassName(moduleClass));
}

static S makeModule(S moduleLibID) {
  ret makeOrShowModule(moduleLibID, false);
}

static S makeOrShowModule(S moduleLibID) {
  ret makeOrShowModule(moduleLibID, true);
}

static S makeOrShowModule(S moduleLibID, bool orShow) {
  // makes dynamic & static modules
  
  if (isIdentifier(moduleLibID))
    ret moduleID(makeOrShowStaticModuleOfType(moduleLibID, orShow));
  
  L<S> l = splitAtSlash(moduleLibID);
  if (!isSnippetID(first(l)))
    fail("Unknown module lib ID: " + moduleLibID);
    
  S snippetID = first(l), className = second(l);
  className = className == null ? null : "main$" + className;
  DynamicModule m = findConcept(DynamicModule, on := true, moduleID := snippetID, _className := className);
  if (m == null) {
    Class c = hotwireModule(snippetID);
    m = DynamicModule(snippetID, className, c);
  }
  if (orShow) showModule(m); else startModule(m);
  ret moduleID(m);
}

static S makeNewModule(S moduleLibID, bool show) {
  print("makeNewModule " + moduleLibID);
  
  if (isIdentifier(moduleLibID))
    ret moduleID(makeNewStaticModuleOfType(moduleLibID, show));
  
  L<S> l = splitAtSlash(moduleLibID);
  if (!isSnippetID(first(l)))
    fail("Unknown module lib ID: " + moduleLibID);
    
  S snippetID = first(l), className = second(l);
  className = className == null ? null : "main$" + className;
  Class c = hotwireModule(snippetID);
  Module m = DynamicModule(snippetID, className, c);
  if (show) showModule(m); else startModule(m);
  ret moduleID(m);
}

static Module makeOrShowStaticModuleOfType(S s) {
  ret makeOrShowStaticModuleOfType(s, true);
}

static Module makeOrShowStaticModuleOfType(S s, bool orShow) {
  ret makeOrShowStaticModuleOfType(classForName("main$" + s), orShow);
}
  
static Module makeOrShowStaticModuleOfType(Class<? extends Module> c) {
  ret makeOrShowStaticModuleOfType(c, true);
}

static Module makeOrShowStaticModuleOfType(Class<? extends Module> c, bool orShow) {
  final L<? extends Module> l = staticModulesOfExactType(c);
  Module m = empty(l) ? nu(c) : first(l);
  if (orShow) showModule(m); else startModule(m);
  ret m;
}

static Module makeNewStaticModuleOfType(S type, bool show) {
  Class c = classForName("main$" + type);
  Module m = nu(c);
  if (show) showModule(m); else startModule(m);
  ret m;
}

!include once #1016217 // Sticky Libs

static Class hotwireModule(S snippetID) {
  hotwire_autoStickyLibs();
  ret hotwire(snippetID); // give each their own local_log
}

// Make modules print with their module ID

set flag hotwire_copyOver_extend.
svoid hotwire_copyOver_extend(Class c) {
  S progID = getProgramID(c);
  if (nempty(progID) && fieldType(c, 'print_log) == Appendable.class)
    setOpt(c, 'print_log, _SubModulePrint("[" + progID + "] "));
}

sbool isLoading() {
  ret eq(systemStatus!, "loading");
}

svoid doInGlobalContext(final Runnable r) {
  final new Flag flag;
  systemQ.add(r {
    callF(r);
    flag.raise();
  });
  flag.waitUntilUp();
}

!include once #1015842 // SavedSessions
!include once #1015885 // Standard Modules
!include once #1015959 // More Standard Modules

please include function renderConcept.
please include function restart.

// do it here, x30 has a bug on old Windows
svoid restart {
  print("\nClean-restarting myself.");
  thread "Clean Restart" {
    call(javax(), 'preKill);
    nohupJavax(smartJoin((S[]) get(javax(), 'fullArgs)), fullVMArguments());
    System.exit(0);
  }
}

svoid setLookAndFeel() {
  if (eq(laf, 'webLAF))
    installWebLAF();
  else
    jtattoo_mcWin();
}

please include function myTranspilationDate.

// should happen in swing thread
svoid setActiveModule(Module m) {
  if (activeModule != m) {
    activeModule = m;
    updateModules(onActiveModuleChange);
  }
}

static O unwrappedActiveModule() {
  ret unwrapDynamicModule(activeModule);
}

concept TopInput {
  S text;
}

svoid nohupJavax(S javaxArgs) {
  nohupJavax(javaxArgs, javaxDefaultVMArgs());
}

sbool cleanedUp;

svoid nohupJavax(S javaxArgs, S vmArgs) {
  if (desktop != null && !cleanedUp) pcall { fullScreenOff(); }
  directNohupJavax(javaxArgs, vmArgs);
}

sbool isDevVersion() {
  ret neq(programID(), #1016005);
}

svoid startOtherVersion {
  nohupJavax(isDevVersion() ? #1016005 : #1016478);
}

sS otherVersionName() {
  ret isDevVersion() ? "v5" : "v6";
}