Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

1899
LINES

< > BotCompany Repo | #1031320 // Stefan's OS v7 [backup before allowing dynamic main classes]

JavaX source code (desktop) [tags: use-pretranspiled] - run with: x30.jar

Download Jar. Uses 1177K of libraries. Click here for Pure Java version (35658L/266K).

!7

set flag print_rst. // speed up printing a lot

set flag jCheckBoxMenuItem_dyn_debug.

set flag InOSCore.
set flag NotifyingPrintLog.

sS defaultBackground = #1101488; //#1101303; //#1101465; //#1101438; //#1101355; // #1009931;
sS background = defaultBackground;
sS backgroundMode = 'fit;
sS mainIconID = #1102666, moduleDefaultIconID = #1101337;
sS laf;
static double minLoadScreenShowingTime = 2;
sbool loadBackgroundLater = true;
static double backgroundLoadDelay = 2.0;
sbool showLoadingScreen = false;
static JDesktopPane desktop;
sbool useCodeSharing = true; // share code between identical modules

static int autoSaveInterval = -10000; // 10 seconds plus slowdown logic
static int persistenceDelayPerModule = 10000; // 10 seconds

static int systemErrorsToKeep = 10;

// OUTDATED
static ReliableSingleThread rstUpdateModules = new(r { systemQ.add(r updateModules) });
sS lastTopInput;
static ReliableSingleThread rstTopInputChanged = rstWithDelay(250, r {
  S s = getText(tfTopInput);
  if (neq(lastTopInput, s))
    vmBus_send('topInputChanged, lastTopInput = s);
});

static volatile long updateCycles;
static L systemErrors = synchroList();
static int systemErrors_pointer;
static new Flag stopRequested;
static SimpleLiveValue<S> systemStatus = stringLiveValue("loading");
static bool firstModuleShown;
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 JTextField tfTopInput; // the input field in the OS menu bar
static long tfTopInput_dontSelectAll; // sysNow timestamp
static StefansOS_ConnectToServer connector;
static Map generalMap = synchroMap();
static L<Component> trayAreaComponents = synchroList();
sbool printOnPersistContents, verboseCleanUp;
static JMenuBar menuBar;
sbool katze_send = true, katze_sendTyped;
sO makeReloadingComponent; // F1<Module, Component>
static long startTime;
static NotTooOften backgroundGCFrequency = everyTenMinutes();
static Set<S> hideFeatures;
static LS standardModules;
sS botName = "Stefan's OS.";
sS osName;
sbool showTopInput = true;
static Runnable shutdownConsole; // instead of showConsole()
sbool verboseModuleDeletes, cacheTranspilationsDuringBoot = true;
sO confirmOSExit; // func -> bool or null
sO gc_subcondition; // disable GC by module; func -> bool or null
sbool showTimeInPrintLog = true;
static JCheckBoxMenuItem miEnableQuickSearches;
sbool debugFrameSizes;

// if you want other initial modules
sO initialModules_override; // VF1<OS reference>

static Runnable restarter_override;
static O onStart;

static S[] mainArgs;

static Set<S> disabledModuleIDs;
static Set<DynamicModule> modulesBeingReloaded = syncSet();

p {
  startTime = sysNow();
  
  // Just load all my classes so we are not dependent on jar or
  // classes files on disk.
  time "loadAllClasses" {
    loadAllClasses();
  }
  
  mainArgs = args;
  vm_generalMap_put(stefansOS := mc());
  callF(onStart, mc());
  
  //set getServerTranspiled_printStackTrace;
  
  print(headless() ? "We are headless." : "Starting GUI.");
  
  if (!headless()) showControls(jbutton("TROUBLESHOOTING", r { stopRequested.raise() }));
  noRegularGC(); // not while we're loading
  
  /*if (onLocallyInferiorJavaX()) {
    print("Restarting to use standard JavaX");
    restart();
  }*/
  
  checkMaxWindowBoundsBug();
  
  if (!eq("0", trim(loadTextFile(javaxDataDir("compile-on-server")))))
    set hotwire_compileOnServer;
  
  /*if (isLinux() && isRoot())
    addToDefaultVMArgs("-XX:ThreadPriorityPolicy");*/
    
  if (java10OrHigher()) {
    addStandardAddOpensFlags();
    addPermitIllegalAccessesFlag();
    if (!hasPermitIllegalAccessesFlagInCurrentVM()) {
      print("RESTARTING with proper JDK 11 arguments");
      restart();
    }
  }
  
  if (java8OrHigher() && !containsOneOf(defaultVMArgs(), "+UseZGC", "+ShenandoahGC"))
    addToDefaultVMArgs(smallHeapArgs());
  
  //laf = 'webLAF; // Let's not generally use it, is sluggish on my machine
  //laf = 'nimbus;
  laf = 'platform;
  
  //if (isMac()) laf = 'platform; // because https://github.com/michael-hagen/JTattoo/issues/1
  if (isMac()) vmGeneralMap_set('iconifyBroken, true);
  if (isLinux()) laf = 'jtattoo;
  
  /*if (vmArgs().contains("-XX:+PrintAssembly")) pcall {
    teeSystemOutAndErrToFile(programFile("os.log"));
  }*/
  
  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();
  
  vm_generalMap_put('consoleInUse, true); // bots made by modules should not handle console

  monitorThreadAllocatedMemory();
  
  // Solve deadlocks every 10 to be sure
  doEvery(10000, r printDeadlocksAndSolve);
  
  print("Data dir: " + javaxDataDir());
  vm_setGlobalACC(globalACC = acc_current());
  
  if (swic(activateFramesOf(programIDPlusHome()), "OK")) {
    print("OS already running!");
    cleanKill();
  }
  
  framesBot();
  if (nempty(botName)) makeBot(botName);
  
  S printLogSize = trim(loadTextFile(javaxDataDir("os-print-log-size.txt")));
  if (isInteger(printLogSize)) printLogMaxChars(parseInt(printLogSize));
  
  vm_generalMap_put('newSwingComponentRegistry, voidfunc(Component c) {
    allAWTComponents_extraList.add(c)
  });
  
  connector = new StefansOS_ConnectToServer;
  connector.start();
  
  //substance();
  final SimpleLiveValue<S> lvDetails = stringLiveValue();
  lvDetails.onChange(r { print(lvDetails!) });
  stefansOS_loadingAnimation_fullScreen = startInFullScreen() && !isWindows() /* bug */;
  final Component loadingAnim = showLoadingScreen ? stefansOS_loadingAnimation(r {
    stopRequested.raise()
  }, lvDetails) : null;
  
  try {
    long start = sysNow();
    systemQ = startQ("System Q");
    useDBOf(#1015871);
    lvDetails.set("Loading database");
    db(autoSaveInterval);
    db_mainConcepts().quietSave = true;
    if (!headless()) {
      // if you use Processing
      JPopupMenu.setDefaultLightWeightPopupEnabled(false);
  
      lvDetails.set("Loading background");
      background = or2(trim(loadTextFile(javaxDataDir("os-background.txt"))), background);
      if (!isSnippetID(background))
        background = fileToURI(new File(background));
      try {
        if (eq(backgroundMode, 'fit))
          desktop = loadBackgroundLater
            ? jDesktopPaneWithFitPicture_smooth_cacheScaled_loadLater(background, delay := backgroundLoadDelay)
            : jDesktopPaneWithFitPicture_smooth_cacheScaled(background);
        else
          desktop = jDesktopPaneWithSkyPicture/*_autoUnload*/(background, Color.black);
      } catch print e {
        try {
          desktop = jDesktopPaneWithFitPicture_smooth(defaultBackground);
        } catch print e2 {
          desktop = jDesktopPane();
        }
      }
      setMainDesktopPane(desktop);
      autoFixDesktopPane(desktop);
      
      // Allow frame switching from AI bar & similar components
      installInternalFrameSwitcher_v3(desktop).shouldSwitch =
        func -> bool { sameComponentOrDeepChildOf(getFocusOwner(), menuBar) };
    }
    
    long ms = consoleShowing() ? toMS(minLoadScreenShowingTime) : 0;
    long remaining = start+ms-sysNow();
    if (remaining <= 0)
      lvDetails.set("Done loading");
    else {
      S seconds = formatDouble(toSeconds(remaining), 1);
      lvDetails.set("Done loading, but will wait if you hit the troubleshooting button 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("More troubleshooting options will go here."),
          jcenteredbuttons(
            "Start " + osName() + " normally", r restart,
            //"Switch to " + otherVersionName(), r startOtherVersion,
            eq(programID(), #1016478) ? null: "Switch to v6", r { nohupJavax(#1016478) },
            eq(programID(), #1024932) ? null: "Switch to v7", r { nohupJavax(#1024932) },
            "Use last session backup", rThread useLastSessionBackup,
            jPopDownButton_noText(
              "Delete session", disableButtonWhileCalcing(func -> bool {
              if (!fileExists(conceptsFile()))
                ret false with infoBox("Session already empty.");
              if (!confirmOKCancel("Really delete your current session?")) false;
              cleanMeUp_concepts();
              renameFileToSomeBackupName(conceptsFile());
              infoBox("Session deleted. Starting again.");
              disableAllButtonsInWindow(heldInstance(JButton));
              sleepSeconds(3);
              restart();
              false;
            })))));
  
    //autoRestart();
    thread { serverAutoRestartMD5(); } // just a short ping
    
    pcall { setLookAndFeel(); }
    
    showDesktop();
  } finally {
    disposeWindow(loadingAnim);
  }
    
  initAfterDBLoad();
  setOpt(javax(), regularGC_firstDelay := 60000);
  call(javax(), 'regularGC);
  if (!headless()) {
    setOpt(javax(), regularGC_condition := func -> bool {
      if (isFalse(callF(gc_subcondition))) false;
      if (backgroundGCFrequency! || vmHasActiveFrame())
        ret true /*with print("GC")*/;
      else
        ret false /* with print("Skipping GC (not in foreground)") */;
    });
  }
  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;
  
  S title;
  if (nempty(osName)) title = osName;
  else {
    title = osName(); //programName();
    S subDir = javaxVirtualSpaceName();
    if (subDir != null) title = "[" + subDir + "] " + title;
  }

  if (showDesktop_first && startInFullScreen()) pcall {
    showFullScreen(title, desktop);
    done = true;
  }

  if (!done) {
    showMaximizedFrame(title, desktop);
    titlePopupMenu_top(desktop, voidfunc(JPopupMenu menu) {
      addMenuItems(menu,
        "New Session", rThreadPcallMessageBox(r deleteAllModules),
        "Restore Initial Modules", r initialModules,
      );
    });
  }
  
  if (showTopInput)
    registerFunctionKey(getFrame(desktop), 1, r { requestFocus(tfTopInput) });
  
  fillMenuBar();
  doNothingOnClose(getFrame(desktop));
  frameIcon(mainIconID, desktop);
  showDesktop_first = false;
  cleanExitOnFrameClose_ifStillInSameFrame(desktop,
    () -> (Bool) callF(confirmOSExit));
}

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 updateMenuBar() swing {
  //temp tempRememberFocusedComponent();
  JMenuBar mb = getMenuBar(desktop);
  if (mb == null) {
    mb = addMenuBar(desktop);
    mb.setLayout(new javax.swing.plaf.basic.DefaultMenuLayout(menuBar, BoxLayout.X_AXIS));
  } 
  keepComponentsByPred(mb, func(Component c) -> bool {
    c instanceof JMenu || c == tfTopInput
  });
  //clearMenuBar();
  fillMenuBar();
}

svoid fillMenuBar() swing {
  menuBar = addMenuBar(desktop);
  set jmenuItem_newThreads;
  S version = version();
  S name = osName();
  //S subDir = userHomeSubDirName();
  //if (subDir != null) name += " [" + subDir + "]";
  addMenuIfNempty(desktop, empty(osName) ? "OS" : trim(name),
    featureMenuItem("Screenshot"), r stefansOS_screenshot,
    featureMenuItem("Full screen on/off"), r fullScreenOnOff,
    featureMenuItem("Switch OS version", "Switch to " + otherVersionName()), r {
      innerCleanUp();
      startOtherVersion();
      cleanKill();
    },
    "---",
    featureMenuItem("Restart OS", "Restart " + or2(osName, "Stefan's OS")), r { showConsole(); restart(); },
    featureMenuItem("Exit"), r cleanKill
  );
  
  L moduleMenu = ll(
    featureMenuItem("Other Module..."), r stefansOS_addDynamicModuleDialog,
    "Show selected module's menu", r showCurrentModuleMenu
  );
    
  for (int i = 0; i+1 < l(standardModules); i += 2) {
    moduleMenu.add(standardModules.get(i));
    S id = standardModules.get(i+1);
    moduleMenu.add(r { makeOrShowModule(id) });
  }
    
  moduleMenu.addAll(ll(
    featureMenuItem("Welcome Screen"), r { makeOrShowModule("#1016067/WelcomeScreen") },
    featureMenuItem("Quick Module Search"), r addQuickModuleSearch,
    //"Hello (input field for everything)", r { makeOrShowModule("Hello") },
    featureMenuItem("Task Bar"), r { makeOrShowModule("#1019954/TaskBar_dev") },
    featureMenuItem("System Print Log"), r { makeOrShowModule("#1018866/SystemPrintLog") },
    hideFeature("Internal Types") ? null : jmenu("Internal Types", map(myNonAbstractClassesImplementing(Module), func(final Class c) -> JMenuItem {
      jMenuItem(shortClassName(c), rThread { makeOrShowStaticModuleOfType(c) })
    })),
    featureMenuItem("Module Classes"), r {  makeOrShowStaticModuleOfType(ModuleClasses) },
    "---",
    jDynamicScrollingMenu("Loaded Modules", voidfunc(JPopupMenu menu) {
      for (final Module m : sortedByMethodIC('moduleName, onModules()))
        addMenuItem(menu, m.moduleName(), rThread { showModule(m) });
    })
  ));
  
  addMenu(desktop, "Modules", asObjectArray(moduleMenu));
  
  // TODO - need to generate menu items differently in enhanceFrame
  //addFullyDynamicMenu(desktop, "Current module",
  
  addMenu(desktop, "More",
    featureMenuItem("Start External JavaX Program..."), r stefansOS_startExternalJavaXProgram,
    "---",
    featureMenuItem("Run 10 Second Profiler"), r tenSecondProfileAndShowInFrame,
    featureMenuItem("Find memory leaks", "Find memory leaks (PID: " + getPID() + ")"), r stefansOS_findMemoryLeaks,
    "Computer ID: " + computerID(), r { copyTextToClipboard_infoBox(computerID()) },
    featureMenuItem("Show console"), rThread showConsole,
    featureMenuItem("System Version"), r { makeOrShowModule_early("#1016442/SystemVersion") },
    featureMenuItem("Remove moduleless frames"), rThread dm_closeModuleLessFrames,
    featureMenuItem("Clean disk caches"), rThread stefansOS_cleanDiskCaches,
    hideFeature("Enable quick searches") ? null : (miEnableQuickSearches = menuItem_enableQuickSearches()),
    featureMenuItem("Hard exit"), rHardExit(),
  );
    
  // Add the fancy stuff
  
  JMenuBar menuBar = cast call(getFrame(desktop), 'getJMenuBar);
  
  if (tfTopInput == null && showTopInput) {
    tfTopInput = jcenteredtextfield(uniq(TopInput).text);
    setFontSize(tfTopInput, 16);
    selectAllOnFocusIf(tfTopInput, func -> bool {
      elapsedTime(tfTopInput_dontSelectAll) >= 2000
    });
    onChange(tfTopInput, r {
      cset(uniq(TopInput), text := getText(tfTopInput));
      rstTopInputChanged.trigger();
    });
    onEnter(tfTopInput, r {
      S info = cast generalMap.get('topInputInfo);
      temp dm_generalMap_tempPut(topInputInfo := or2(info, "typed"));
      S text = getText(tfTopInput);
      vmBus_send('topInput, text);
      logQuotedWithDate(topInputsLogFile(), text);
      if (katze_send)
        if (nempty(info))
          katze_userSaid(info, text);
        else if (katze_sendTyped)
          katze_userTyped(text);
    });
    componentPopupMenu(tfTopInput, voidfunc(JPopupMenu menu) {
      menu.add(menuItem_enableQuickSearches());
    });
  }
    
  if (showTopInput && getParent(tfTopInput) == null) {
    menuBar.add(Box.createHorizontalStrut(20));
    menuBar.add(tfTopInput);
  }
  
  if (!containsChildOfType(menuBar, JLabel.class)) { // look for clock label
    if (!showTopInput)
      menuBar.add(Box.createHorizontalGlue());
    else {
      menuBar.add(Box.createHorizontalStrut(5));
      menuBar.add(onClick(setToolTip([[Simulate pressing Enter key in input field to the left ("AI bar")]], jimage(#1101426)), r { simulateEnter(tfTopInput) }));
    }
    //menuBar.add(jhgrid(null, tfTopInput));
    //menuBar.add(jhgrid(tfTopInput, null));
    menuBar.add(Box.createHorizontalStrut(20));
    for i over trayAreaComponents: {
      Component c = trayAreaComponents.get(i);
      continue if c == null;
      menuBar.add(c);
      menuBar.add(Box.createHorizontalStrut(i == l(trayAreaComponents)-1 ? 20 : 6));
    }
    menuBar.add(jLiveValueLabel(clockTimeLiveValue()));
    menuBar.add(Box.createHorizontalStrut(6));
  }
}

svoid addQuickModuleSearch {
  makeOrShowModule("#1016702/LoadedModuleSearch");
  makeOrShowModule("#1016932/ServerModuleSearch");
}

static JCheckBoxMenuItem menuItem_enableQuickSearches() {
  ret jCheckBoxMenuItem("Enable quick searches", quickSearchesEnabled(), voidfunc(bool b) {
    cset(uniq(TopInput), quickSearchesEnabled := b);
    setChecked(miEnableQuickSearches, b);
  });
}

static tempDisposeInternalFrame_obj<JLabel> tempBusyAnimation(fS text) {
  ret vmExiting() ? null : tempBusyAnimation(stringLiveValue(text));
}

static tempDisposeInternalFrame_obj<JLabel> tempBusyAnimation(LiveValue<S> text) {
  if (headless()) null;
  final JLabel anim = jBackground(Color.white, jAnimation_liveValueText(#1101316, text));
  swing {
    addInternalFrame_dontSelect.set(true);
    addInternalFrame_layer.set(JLayeredPane.POPUP_LAYER);
    addInternalFrame(desktop, "", null, anim);
    packInternalFrameInTopRightCorner(anim);
    JInternalFrame f = getInternalFrame(anim);
    //print("Have busy animation: " + f + " in " + getParent(f));
  }
  ret tempDisposeInternalFrame(anim);
}

svoid allRegularFixes {
  applyStandardSwingFixes();
  cleanDefunctACCsInAllThreads();
  cleanDefunctACCsInAllSwingComponents();
}

svoid gcWithFixes {
  allRegularFixes();
  gc();
  allRegularFixes();
}

svoid initAfterDBLoad {
  doEvery(30000, r allRegularFixes);
  doEvery(600000, r clearSnippetTitleCacheIfOnline);
  set cleanUp_interruptThreads; 
  
  // for Java Chrome
  classForNameOpt("java.util.prefs.FileSystemPreferences");
  
  SimpleLiveValue<S> lvText = stringLiveValue(jlabel_textAsHTML_center("Restoring Session"));
  temp final tempDisposeInternalFrame_obj<JLabel> anim = tempBusyAnimation(lvText);
  JLabel animComponent = anim == null ? null : anim.component;
  if (anim != null) {
    onResizeAndNow(animComponent, r {
      setVerticalAlignment(
        animComponent.getHeight() >= 150 ? JLabel.CENTER : JLabel.TOP,
        animComponent)
    });
    growInternalFrameLeft(animComponent, 50);
    growInternalFrameSouth(animComponent, 50);
  }
  
  disabledModuleIDs = asSet(tlft(loadTextFile(javaxDataDir("stefans-os/disabled-modules.txt"))));
  
  initialModules();
  
  UpdateCycles uc = conceptWhere(UpdateCycles);
  if (uc != null) updateCycles = uc.value;
  
  stefansOS_installQuickSearchHelper();
  
  // START MODULES
  
  L<Module> modules = startOrder(onModules());
  
  AutoCloseable disposeTempCache2 = cacheTranspilationsDuringBoot ? tempSetMCOpt(getServerTranspiled2_tempCache :=  syncMap()) : null;
  
  if (cacheTranspilationsDuringBoot) thread {
    for (Module m : modules) pcall {
      if (m cast DynamicModule) {
        print("Preloading " + m.moduleID);
        getServerTranspiled2(m.moduleID);
      }
    }
    print("Preloading done");
  }

  AutoCloseable disposeTempCache = tempSetTL(loadLibraryOrSrcLib_tempCache, new Map);
  
  int i = 0;
  for (Module m : modules) {
    ++i;
    int _i = i;
    systemQ.add(new Runnable { 
      run {
        if (isShuttingDown()) ret;
        lvText.set(jlabel_textAsHTML_center(
          "Starting module " + _i + "/" + l(modules) + ":\n"
          + m.moduleName()));
        startModule(m);
      }
      
      toString { ret "Start module " + m.id; }
    });
  }
  
  if (anim != null) anim.disableAutoDisposal();
  systemQ.add(r {
    close(disposeTempCache);
    close(disposeTempCache2);
    disposeInternalFrame(animComponent);
    if (isShuttingDown()) ret;
    systemStatus.set("running");
    print("== STARTED IN " + iceil(elapsedSeconds(/*vmStartTime_sys()*/startTime)) + " SECONDS ==");
  });
}

static L<Module> startOrder(L<Module> modules) {
  ret sortedByComparator(
    combineComparators(
      main.<Module> descFieldComparator('visible),
      main.<Module> fieldComparator('zOrder)),
    modules);
}

svoid initialModules {
  if (initialModules_override != null)
    ret with pcallF(initialModules_override, mc());
  initialModules_base();
}

svoid initialModules_base {
  if (headless()) {
    makeOrShowModule("#1016576/ConnectToServer");
    ret;
  }
    
  if (empty(onModules())) {
    //showModule(new DynamicModule(#1016081, 'main$WelcomeScreen));
    showModule_noFocus(getModuleByID(makeModule("#1018866/SystemPrintLog")));
    showModule_noFocus(getModuleByID(makeModule("#1019954/TaskBar_dev")));
    //makeModule("#1016872/SnippetsDB");
    makeModule("#1016932/ServerModuleSearch");
    makeModule("#1016702/LoadedModuleSearch");
    //makeModule("#1018602/QuickAudioRecord");
    makeModule("#1019326/WitAILastRecording");
    makeModule("#1019400/SpeechRecogConfirmation");
    makeModule("#1021905/AutoReloadButtons");
  }
}

svoid triggerUpdate { rstUpdateModules.trigger(); }

// not done anymore
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 saveZOrder {
  for (Module m)
    cset(m, zOrder := getComponentZOrder(getInternalFrame(m.vis)));
}

svoid cleanMeUp {
  saveZOrder();
  autoConsole();
  if (shutdownConsole == null) showConsole(); else pcallF(shutdownConsole);
  killProgram(#1019683); // stop watch dog
  systemStatus.set("shutting down");
  temp tempBusyAnimation("Shutting Down");
  
  print("PRE-CLEAN UP");
  preCleanUp(list(Module));
  for (Module m) {
    if (m.vis != null) pcall {
      print("UNVISUALIZING " + moduleIDAndName(m));
      m.unvisualize();
      print("UNVISUALIZED " + moduleIDAndName(m));
    }
    print("CLEANING UP " + moduleIDAndName(m));
    cleanUp(m);
    print("CLEANED UP " + moduleIDAndName(m));
  }
  print("CLEAN UP DONE");
  cleanedUp = true;
}

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

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

sbool startModule_reloading, startModule_doShare;
sO startModule_beforeStart;

svoid startModule(Module m) {
  runInSystemQAndWait(r-pcall {
    //addIfNotThere(modules, m);
    temp m.enter();
    if (m.started || m.startedWithError) ret;
    temp tempSet(m, starting := true);
    print("Starting module " + m.moduleName());
    try {
      if (m cast DynamicModule)
        if (contains(disabledModuleIDs, m.moduleID))
          failWithInfoBox("Not starting disabled module " + m);
      m.start();
      m.started = true;
    } catch e {
      m.startedWithError = true;
      m.setError(e);
      _handleException(e);
    }
    vmBus_send('moduleStarted, m.moduleID());
    //rstUpdateModules.trigger();
    if (m.visible) showModule(m);
  });
}

static Module showModule(final Module m) {
  ret showModule(m, true);
}

static Module showModule_noFocus(final Module m) {
  ret showModule(m, false);
}

svoid runInSystemQAndWait(Runnable r) {
  if (isAWTThread()) ret with callF(r);
  runInQAndWait(systemQ, r);
}

sO evalInSystemQ_gen(O f) {
  ret evalInSystemQ(toF0(f));
}

static <A> A evalInSystemQ(F0<A> f) {
  if (isAWTThread()) ret callF(f);
  ret evalInQ(systemQ, f);
}

static Module showModule(final Module m, final bool focus) {
  if (m == null) ret m;
  runInSystemQAndWait(r {
    startModule(m);
    if (headless()) ret;
    temp m.enter();
    if (m.vis != null) {
      if (focus && !isLoading())
        if (stefansOS_moduleIsPoppedOut(m))
          activateFrame(m.vis);
        else
          activateInternalFrame(m.vis);
      ret;
    }
    csetAndStatusChange(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 = makeDefaultRectForModule(m);
      print("Showing frame at " + r);*/
      S frameTitle = m.moduleName();
      final JInternalFrame f;
      bool toBack = isLoading() && firstModuleShown;
      bool activate = focus && !toBack;
      {
        temp tempSetThreadLocal(addInternalFrame_toBack, toBack);
        temp tempSetThreadLocal(addInternalFrame_dontSelect, !activate);
        f = showInternalFrame(desktop, frameTitle, r, m.vis);
        set firstModuleShown;
      }
      if (r == null) {
        if (debugFrameSizes) print("Setting initial size for module " + m);
        Pt p = m.getMinSize();
        if (debugFrameSizes) print("Minimum size: " + p);
        if (isTrue(callOpt(resolveModule(m), "_usePreferredSizeInitially"))) {
          p = toPt(getPreferredSize(m.vis));
          if (debugFrameSizes) print("Using preferred size: " + p);
        }
          
        centerPackInternalFrameWithMinSize(f, p);
      }
      f.setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE);
      
      onInternalFrameIconified(f, r { hideModule(m) });
      onInternalFrameClosing(f, rThread { deleteModule(m) });
      onInternalFrameActivated(f, r { setActiveModule(m) });
      onInternalFrameDeactivated(f, r { if (activeModule == m) setActiveModule(null) });
      onBoundsChange(f, r { m.grabFrameRect() });
      internalFrameIcon(f, or2(m.iconID, moduleDefaultIconID));
      m.enhanceFrame(f);
      if (activate) setActiveModule(m);
    }
  });
  ret m;
}

static Rect makeDefaultRectForModule(Module m) {
  ret Rect(10, 10, 200, 100);
}

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;
  }
  deleteModule_noConfirm(m);
  true;
}

svoid deleteModule_noConfirm(Module m) {
  runInSystemQAndWait(r {
    if (verboseModuleDeletes) print("delete phase 1");
    O m2 = unwrapDynamicModule(m);
    vmBus_send('deletingModule, m2);
    ghostModules.put(m == m2 ? m : m2.getClass(), nu(GhostModule,
      name := m.moduleName(),
      created := m.created,
      deleted := now(),
      instance := new WeakReference(m2)));
    if (verboseModuleDeletes) print("delete phase 2");
    pcall { m.unvisualize(); }
    if (verboseModuleDeletes) print("delete phase 3");
    removeConcept(m); // generates oStruct
    vmBus_send('moduleDeleted, m);
    if (verboseModuleDeletes) print("delete phase 4");
    pcall {
      S snippetID = m instanceof DynamicModule ? m/DynamicModule.moduleID : "internal_" + shortClassName(m);
      logStructure(deletedModulesLogFile(snippetID), litmap(
        globalID := aGlobalID(),
        deleted := now(),
        module := m));
    }
    //triggerUpdate();
  });
}

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

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

svoid csetAndStatusChange(Module m, O... params) {
  if (cset(m, params) > 0)
    vmBus_send('moduleStatusChange, m, params);
}

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 starting, started, startedWithError;
  transient Lock lock = lock();
  
  static int ALWAYSONTOPWHENPOPPEDOUT = 1;

  bool on = true, visible;
  int flags;
  Rect frameRect;
  int zOrder;
  S iconID;
  PersistableThrowable error;
  
  //transient Set timers;
  
  JComponent visualize() { null; }
  void unvisualize() {
    pcall { unvisualize1(); }
    pcall { unvisualize2(); }
    vmBus_send('moduleUnvisualized, this);
  }
  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 = startedWithError = false; }

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

  S sourceCode() {  
    ret javaxSourceOfMyClass1(shortClassName(this));
  }
  
  // for all modules
  void triggerUpdate { rstUpdateModules.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));
  }
  
  bool hasFlag(int flag) { ret anyCommonBits(flags, flag); }
  void setFlag(int flag) { _setField(flags := flags | flag); }
  void clearFlag(int flag) { _setField(flags := flags & ~flag); }
  
  // minimum size of JInternalFrame
  Pt getMinSize() { null; }
} // 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 asList(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 int moduleCount() {
  ret l(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 ? 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();
}

concept DynamicModule extends Module {
  // moduleID is the snippet ID
  // "className" is taken by DynamicObject; _className == null for old-style dyn module
  S moduleID, _className;
  S oStruct; // serialized dynamic object
  sbool reload_replaceFrame = true;
  
  transient Class c;
  transient O o;
  transient bool contentsDirty = true;
  transient ReliableSingleThread rstPersist = rstWithDelay(persistenceDelayPerModule, r persistContents);
  transient O reloadData; // data held for module during reload

  *() {}
  *(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) {
    final WeakReference<JInternalFrame> fRef = new(f);
    final WeakReference<DynamicModule> mRef = new(this);
    S idText = str(id);
    
    if (isTrue(vmGeneralMap_get('iconifyBroken)))
      internalFrameTitlePopupMenuItem(f, "Hide/Minimize", r { hideModule(mRef!) });
    
    idText += "/" + moduleLibID();
    if (!hideFeature("Show source code"))
      internalFrameTitlePopupMenuItem(f, "Source code [Module ID: " + idText + "]", r-thread { pcall {
        if (empty(mRef->moduleID)) ret with infoBox("No source code found");
        dm_openSnippetInEditor(mRef->moduleID);
      }});
    
    internalFrameTitlePopupMenuItem(f, "Reload", r reload);
    internalFrameTitlePopupMenuItem(f, "Duplicate", rThread { duplicateModule(DynamicModule.this) });
    if (!hideFeature("Retranspile"))
      internalFrameTitlePopupMenuItem(f, "Medium Retranspile", rThread { transpileOnServerWithErrorWindow(moduleID, true, r reload) });
    if (!hideFeature("Pop Out")) internalFrameTitlePopupMenuItem(f, "Pop Out", r {
      clearFlag(ALWAYSONTOPWHENPOPPEDOUT);
      stefansOS_popOutModule(DynamicModule.this);
    });
    if (!hideFeature("Pop Out + Always On Top")) internalFrameTitlePopupMenuItem(f, "Pop Out + Always On Top", r {
      setFlag(ALWAYSONTOPWHENPOPPEDOUT);
      stefansOS_popOutModule(DynamicModule.this);
    });
    
    internalFrameTitlePopupMenu(f, (IVF1<JPopupMenu>) menu -> {
      LS sisterModules = dm_sisterModules(this);
      if (empty(sisterModules)) ret;
      JMenu subMenu = jmenu("Sister modules");
      for (S mod : sisterModules)
        addMenuItem(subMenu, dm_moduleName(mod), rThread { dm_showModule(mod) });
      menu.add(subMenu);
    });
    
    {
      temp enter();
      pcallOpt(o, 'enhanceFrame, f);
    }
    internalFrameTitle(f, moduleName());
  }
  
  S moduleLibID() {
    ret moduleID + "/" + dropPrefix("main$", _className);
  }
  
  S moduleName() {
    S name = (S) callOpt(o, 'moduleName);
    if (nempty(name)) ret name;
    ret originalModuleName();
  }
  
  S originalModuleName() {
    S title = snippetTitle_cached(moduleID);
    //ret dropSuffixICTrimOneOf(title, "[Dyn Module]", "[Dyn Module, OK]", "[Dyn Module, shortened]");
    ret stefansOS_cleanModuleName(title);
  }
  
  void start() {
    try {
      start_impl();
    } catch e {
      setError(e);
      _handleException(e);
    }
  }
  
  void start_impl {
    if (moduleID == null) ret;
    if (c == null)
      c = startModule_reloading && !startModule_doShare
        ? hotwireModule(moduleID)
        : hotwireModule_withSharing(moduleID);
    replaceACCInClassLoader(c, globalACC);
    if (oStruct != null) pcall {
      Map<S, O> renames = cast getOpt(c, '_renameClasses);
      //print("Class renames: " + sfu(renames));
      S renamed = migrateClassesInStructureText(oStruct, renames);
      //print("Renamed: " + renamed);
      
      // notify module that we are loading (dev.)
      /*ThreadLocal tlLoading = cast getOpt(getClass(c, "loadableUtils.utils"), 'dynamicObjectIsLoading);
      temp tempSetTL(tlLoading, true);*/
      
      // load
      o = unstructureInRealm(renamed, c);
    }
    if (o == null)
      if (_className == null)
        o = c; // old-style - just a class
      else {
        o = nu(_getClass(c, _className)); // new-style (module is an object)
        if (!stefansOS_checkModuleInstance(o)) { // TODO: doesn't seem to trigger for modules with wrong class name
          o = null;
          S msg = this + " failed to load - " + className(o) + " not an instance of DynModule";
          infoBox(msg);
          error = persistableThrowable(new RuntimeException(msg));
          ret;
        }
      }
    setOptAll(o, _host := this, lock := lock);
    
    // call module's start() method
    
    temp enter();

    // this is set when making a module with params
    callF(startModule_beforeStart, this);
    
    // some modules need the reload data before starting
    if (reloadData != null)
      callOpt(o, '_setReloadData_early, reloadData);

    if (o instanceof Class)
      callMain(o);
    else
      callOpt(o, 'start);
  }
  
  void unvisualize2() { callOpt(o, 'unvisualize); }
  
  void update() { callOpt(o, 'update); }
  
  // TODO - only when called from DynModule
  // (works for now, but should be renamed)
  void _change() {
    super._change();
    vmBus_send moduleChanged(this);
    contentsDirty = true;
    if (rstPersist != null)
      rstPersist.trigger();
  }
  
  void persistContents() enter {
    if (_concepts != null && contentsDirty) {
      vmBus_send persistingModuleContents(this);
      contentsDirty = false;
      //oStruct = null;
      pcall {
        if (o != null && !o instanceof Class) {
          cset(this, oStruct := struct(o));
          if (printOnPersistContents)
            print("Persisted contents: " + moduleID + " - " + l(oStruct) + " chars");
          callOpt(o, 'onPersisted);
        }
      }
    }
  }
  
  void cleanMeUp() {
    persistContents();
    if (verboseCleanUp)
      print("o: " + className(o) + ". Cleaning up main class: " + mainClass(o));
    if (mainClass(o) == mc()) // anomalous object
      cleanUp(o);
    else {
      if (classLoadedInOtherModule(mainClass(o), this)) {
        print("KEEPING code (loaded in other module)");
        cleanUp(o);
      } else
        cleanUpObjectAndItsMainClass(o);
    }
    o = null;
    c = null;
  }
  
  void reload() {
    reloadModuleInBackground(this);
  }
  
  // don't call directly - runs in system queue
  void reload_impl() {
    temp tempAddToCollection(modulesBeingReloaded, this);
    
    if (reloadData == null) {
      temp enter();
      reloadData = callOpt(o, '_getReloadData);
    }

    bool poppedOut = stefansOS_moduleIsPoppedOut(this);
    if (poppedOut) stefansOS_popInModule(this);
    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,
        makeReloadingComponent != null
          ? callF(makeReloadingComponent, this)
          : makeStandardReloadingComponent(this));
          
    visible = false;
    
    {
      temp tempSetMC(startModule_reloading := true);
      startModule(this);
    }
    
    if (reloadData != null) {
      temp enter();
      callOpt(o, '_setReloadData, reloadData);
      reloadData = null;
    }
    
    if (frame != null) {
      if (reload_replaceFrame) { // avoids some bugs
        cset(this, frameRect := toRect(getBounds(frame)));
        disposeInternalFrame(frame);
        showModule(this);
      } else {
        csetAndStatusChange(this, visible := true);
        visualizeModule(this);
        //print("New content: " + vis);
        setInternalFrameContents(frame, vis);
      }
    }
    
    if (poppedOut) stefansOS_popOutModule(this);

    vmBus_send moduleReloaded(this);
  }
  
  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);
  }
  
  Pt getMinSize() {
    ret shallowCloneToClass Pt(callOpt(o, 'minimumSize));
  }
} // END CONCEPT DYNAMICMODULE

/*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(O m) {
  ret m instanceof Module ? m/Module.resolve() : m;
}

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);
}

sS makeOrShowModule_early(S moduleLibID) {
  ret makeOrShowModule_early(moduleLibID, true);
}

sS makeOrShowModule(fS moduleLibID, final bool orShow) {
  ret evalInQ(systemQ, func -> S { // XXX - changed 2019/07/10
    makeOrShowModule_impl(moduleLibID, orShow)
  });
}

// add to beginning of system queue
sS makeOrShowModule_early(fS moduleLibID, bool orShow) {
  ret evalInQ_first(systemQ, func -> S {
    makeOrShowModule_impl(moduleLibID, orShow)
  });
}

sS makeOrShowModule_impl(fS moduleLibID, final 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 = fsI(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_withSharing(snippetID);
    m = DynamicModule(snippetID, className, c);
    vmBus_send('moduleLoaded, m);
  }
  if (orShow) showModule(m); else startModule(m);
  ret moduleID(m);
}

// beforeStart: voidfunc(DynamicModule)
sS makeNewModule(fS moduleLibID, final bool show, O... _) {
  temp tempSetMC(startModule_beforeStart := optPar beforeStart(_));
  ret evalInSystemQ(func -> S {
    print("Loading module " + 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_withSharing(snippetID);
    
    Module m = DynamicModule(snippetID, className, c);
    startModule(m);
    if (show && !stefansOS_shouldStartMinimized(m))
      showModule(m);
    ret moduleID(m);
  });
}

// dynamic only
static S findModuleByLibID(S moduleLibID) {
  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;
  Cl<DynamicModule> list = findConcepts(DynamicModule, on := true, moduleID := snippetID);
  DynamicModule m = firstWhere(list, _className := className);
  if (m != null) ret moduleID(m);
  if (className != null) null;
  ret moduleID(first(list));
}

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 = cast nu(c);
  if (show) showModule(m); else startModule(m);
  ret m;
}

//!include once #1016217 // Sticky Libs
!include once #1020744 // Sticky Libs & Src Libs

static Class hotwireModule(S snippetID) {
  hotwire_autoStickyLibs();
  ret hotwire(snippetID, lambda1 mainClassNameForClassLoader);
}

static Class hotwireModule_withSharing(S snippetID) {
  Class c = null;
  if (useCodeSharing) {
    c = findLoadedCodeBySnippetID(snippetID);
    if (c != null)
      print("SHARING code for " + snippetID);
  }
  if (c == null)
    c = hotwireModule(snippetID);
  ret c;
}

// 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, makeSubModulePrint(c, progID));
}

static swappable _SubModulePrint makeSubModulePrint(Class c, S progID) {
  _SubModulePrint p = new("[" + progID + "] ");
  p.makePrefix = () ->
    showTimeInPrintLog ? "[" + progID + " " + timeWithMilliseconds() + "] " : p.prefix;
  ret p;
}

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

sbool isShuttingDown() {
  ret eq(systemStatus!, "shutting down");
}

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.

svoid setLookAndFeel() {
  if (cic(mainArgs, "-nolaf")) ret;
  new Matches m;
  S laf = or2(getLAF(), main.laf);
  if (isSnippetID(laf))
    runDependent(laf);
  else if (eq(laf, 'webLAF))
    installWebLAF();
  else if (eq(laf, 'nimbus))
    nimbus();
  else if (eq(laf, 'jtattoo))
    jtattoo_mcWin();
  else if (eq(laf, 'platform))
    systemLAF();
  else if (eq(laf, 'substance))
    call(hotwire(#1025722), 'substance);
  else if (startsWith_trim(laf, "substance ", m))
    call(hotwire(#1025722), 'substance, $1);
  else
    print("Unknown LAF: " + laf);
    
  pcall {
    S scale = trim(loadTextFile(javaxDataDir("os-font-scale.txt")));
    if (nempty(scale))
      swingFontScale(parseDouble(scale));
  }
}

please include function myTranspilationDate.

// should happen in swing thread
svoid setActiveModule(Module m) {
  if (activeModule != m) {
    activeModule = m;
    vmBus_send('newActiveModule, m);
  }
}

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

concept TopInput {
  S text;
  bool quickSearchesEnabled = true;
}

concept LAF {
  S laf; // 'platform etc. or module ID
}

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(), #1016478);
}

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

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

sS restoreModule(S structure) {
  if (structure == null) null;
  
  print(shorten(500, structure));
  
  O mod = safeUnstructure(structure);
  if (mod instanceof Map)
    mod = get((Map) mod, 'module);
    
  S shortName = dynShortName(mod);
  if (!eq(shortName, "DynamicModule"))
    fail("Can't restore static modules yet [" + shortName + "]");
    
  S snippetID = getString(mod, "moduleID");
  S className = getString(mod, "_className");
  
  print("Restoring " + snippetID + "/" + className);
  
  Class c = hotwireModule_withSharing(snippetID);
  DynamicModule m = new(snippetID, className, c);
  copyFields(mod, m, 'iconID);
  m.frameRect = (Rect) restruct(getOpt(m, 'frameRect));
  m.oStruct = getString(mod, "oStruct");
  showModule(m);
  ret moduleID(m);
}

sS moduleStructForDuplication(DynamicModule m) {
  if (m == null) null;
  m.persistContents();
  S struct = struct(m);
  fO resolved = resolveModule(m);
  
  if (hasMethod(resolved, '_modifyStructForDuplication, struct))
    struct = jreplace_first_dyn(struct, "oStruct=*", func(LS tok, int i) -> S {
      S s = unquote(tok.get(i+4));
      s = callOpt(resolved, '_modifyStructForDuplication, s);
      if (s == null) fail("Module does not want to be duplicated");
      ret "oStruct=" + quote(s);
    });
    
  ret struct;
}

sS duplicateModule(DynamicModule m) {
  ret restoreModule(moduleStructForDuplication(m));
}

static O dm_current_mandatory() { fail(); }

// for modules
please include function mechList_opt_raw.
please include function mechList_clearCache.
please include function mechLists_clearCache.

// This is only answered from localhost
answer {
  if "is stefan's os" ret "yes";
  
  if "show module * with params *" {
    ret dm_showModuleWithParams(unstructString($1), unstructObjectArray($2));
  }
  
  if (swic(s, "restore module:", m))
    ret ok2(restoreModule(m.rest()));

  if "swing latency" { ret str(swingLatency()); }
  
  if "activate frames" {
    awt { activateMyFrames(); }
    ret "OK";
  }
  
  if "program id" ret programID();
  if "main program id" ret mainProgramID();
  
  if "enable remote control from *" {
    O mod = getDynModuleByID(makeOrShowModule("#1017127/RemoteControlled"));
    call(mod, 'setFields, new O[] {new O[] {controllingComputers := $1, enabled := true}});
    ret "OK";
  }
  
  if "stack traces"
    ret renderAllThreadsWithStackTraces();
}

srecord Service(O module, O worker) {}

static new MultiMap<S, Service> registeredServices;

static bool callService(S name, O... args) {
  for (Service service : cloneList(registeredServices.get(name)))
    if (isTrue(pcallF(service.worker, name, args))) true;
  false;
}

static void registerService(O module, S name, O service) {
  registeredServices.put(name, Service(module, service));
}

static void unregisterService(O module, S name, O service) {
  registeredServices.remove(name, Service(module, service));
}

static O dm_os() { ret main.class; }

please include function robot_keyPress.
please include function robot_keyRelease.
please include function robot_safetyKeyReleaser.

svoid showCurrentModuleMenu {
  swing {
    showInternalFramePopupMenu(desktop.getSelectedFrame());
  }
}

svoid inSystemQ(Runnable r) {
  systemQ.add(r);
}

svoid reloadModuleInBackground(final DynamicModule m) {
  if (m == null) ret;
  vmBus_send reloadingModule(m);
  inSystemQ(r { m.reload_impl() });
}

svoid deleteModuleInBackground(final DynamicModule m) {
  if (m == null) ret;
  inSystemQ(r { deleteModule(m) });
}

svoid restart {
  if (restarter_override != null)
    ret with callF(restarter_override);
  restartWithDefaultVMArgs();
  sleep();
}

static Component makeStandardReloadingComponent(Module m) {
  ret jcenteredlabel("Reloading...");
}

sbool classLoadedInOtherModule(Class mainClass, Module mod) {
  for (DynamicModule m)
    if (m != mod && mainClass(m.o) == mainClass)
      true;
  false;
}

static Class findLoadedCodeBySnippetID(S snippetID) {
  for (Module m : onModules())
    if (m cast DynamicModule)
      if (sameSnippetID(m.moduleID, snippetID) && m.c != null)
        ret m.c;
  null;
}

svoid cleanKill {
  setOpt(javax(), cleanKill_verbose := true);
  cleanKillVM();
}

sS moduleIDAndName(Module m) {
  ret m == null ? "-" : m.id + " - " + rcall_pcall moduleName(m);
}

sbool hideFeature(S name) {
  ret contains(hideFeatures, name);
}

sS featureMenuItem(S name) {
  ret featureMenuItem(name, name);
}

sS featureMenuItem(S feature, S name) {
  ret hideFeature(feature) ? null : name;
}

sS osName() { ret or2(osName, "Stefan's OS " + version()); }

sS version() { ret ai_versionFromName(programName()); }

static IF0<Bool> standardOSExitConfirm() {
  ret () -> confirmOKCancel("Really exit " + osName() + "?");
}

svoid setLAF(S laf) {
  cset(uniq(LAF), +laf);
}

sS getLAF() {
  ret getString laf(conceptWhere(LAF));
}

static TopInput topInputConcept() {
  ret uniq(TopInput);
}

sbool quickSearchesEnabled() {
  ret topInputConcept().quickSearchesEnabled;
}

svoid useLastSessionBackup {
  restoreNextToLastConceptsBackup(dbProgramID());
  restart();
}

Author comment

Began life as a copy of #1024932

download  show line numbers  debug dex  old transpilations   

Travelled to 4 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, vouqrxazstgt

No comments. add comment

Snippet ID: #1031320
Snippet name: Stefan's OS v7 [backup before allowing dynamic main classes]
Eternal ID of this version: #1031320/1
Text MD5: 72b2b3ce1fd18c032086aac5f6847eb7
Transpilation MD5: da6bcb3a47eb7709ef691325d54306e7
Author: stefan
Category: javax / stefan's os
Type: JavaX source code (desktop)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-06-05 00:03:23
Source code size: 57396 bytes / 1899 lines
Pitched / IR pitched: No / No
Views / Downloads: 260 / 744
Referenced in: [show references]