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

1217
LINES

< > BotCompany Repo | #1033891 // GazelleHost + Stem [dev]

JavaX (incomplete)

lib 1400521 // FlatLAF

set flag PingV3.
set flag NotifyingPrintLog.
set flag SymbolAsString.

// Do the JavaX init
svoid standaloneInit {
  __javax = x30.class;
  x30.__javax = x30.class; // for hotwire
  x30_pkg.x30_util.__setJavaX(x30.class);
  x30.cleanKillMsg = "";
  callOnLoadMethods(mc());
}

concept PrintLogModule > DynPrintLog {
  start { setModuleName("Log"); }
  
  S getPrintLog() {
    ret printLog();
  }
  
  bool useErrorHandling() { false; }
}

concept Stem extends ConceptWithChangeListeners is AutoCloseable {
  transient GazelleHost host;
  transient LoadedDB db;
  DynModule module;
  settableWithVar Rect frameRect;
  bool minimized, maximized, alwaysOnTop, minimizeToTray;
  
  transient JFrame window;
  transient JButton btnMax;
  transient JLabel titleComponent;
  transient gettable JButton compileButton;
  //transient bool autoUpdateDisabled;
  transient bool started;

  *() {}
  *(LoadedDB *db) { host = db.host; }
  *(LoadedDB db, DynModule *module) { this(db); }
  
  transient Component selfVis; // how the module visualized itself
  
  // Not sure why we use LazyVar here instead of simplyCached
  
  transient LazyVar<SingleComponentPanel> scpMainWindowContents = new(-> scp());
  SingleComponentPanel scpMainWindowContents() { ret scpMainWindowContents!; }

  transient LazyVar<SingleComponentPanel> scpMainWindowWithTitle = new(-> scp());
  SingleComponentPanel scpMainWindowWithTitle() { ret scpMainWindowWithTitle!; }

  transient LazyVar<SingleComponentPanel> scpFrameIcon = new(-> scp());
  SingleComponentPanel scpFrameIcon() { ret scpFrameIcon!; }

  Rect grabFrameRect() {
    ret toRect(getBounds(window));
  }
  
  void saveFrameRect {
    frameRect(grabFrameRect());
  }
  
  void restoreFrameRect {
    if (frameRect != null)  
      setBounds(window, frameRect);
  }
  
  void setAlwaysOnTop(bool alwaysOnTop) {
    this.alwaysOnTop = alwaysOnTop;
    change();
    alwaysOnTop(window, alwaysOnTop);
  }
  
  void setMinimizeToTray(bool minimizeToTray) {
    cset(this, +minimizeToTray);
  }

  void setWindow(JFrame window) {  
    this.window = window;
    restoreFrameRect();
    //onWindowClosing(window, -> host.cleanExit());
    onBoundsChange(window, r saveFrameRect);
    
    if (host.restrictWindowsToScreenSize)
      restrictWindowToScreenSize(window);
    
    alwaysOnTop(window, alwaysOnTop);
  }
  
  void activateWindow {
    print("activateWindow: " + window);
    if (window != null)
      activateFrame_v3(window);
    else
      startAndShow(false);
  }
  
  bool isMain() { ret this == db.mainStem; }
  
  void minimize {
    cset(this, minimized := true);
    if (!minimizeToTray || host.trayIcon == null || !isMain())
      minimizeWindow(window);
    else
      hideWindow(window);
  }
  
  void deleteMe {
    temp tempPrintAlsoToSystemOut();
    print("deleteMe " + isMain());
    if (isMain())
      host.closeDatabase(db);
    else
      delete();
  }
  
  close {
    disposeWindow(window);
    cleanUp(module);
  }
  
  void delete :: before {
    close();
  }
  
  void grabWindowState {
    setField(maximized := !isExtendedStateNormal(window));
  }
  
  void updateBtnMax {
    if (!isShowing(btnMax)) ret;
    grabWindowState();
    setText(btnMax, maximized ? "NORM" : "MAX");
  }
  
  JComponent makeWindowBorderAndTitle(JComponent contents, S title) {
    //ret jCenteredSection_fontSizePlus(10, title, vis);
    
    var icons = jline();
    if (host.allowFrameMaximization) {
      icons.add(btnMax = jbutton("", rThread {
        maximizeOrRestoreFrame(window);
        updateBtnMax();
      }));
      onFirstShowing(btnMax, r updateBtnMax);
    }
    
    icons.add(toolTip("Minimize this window", jbutton("MIN", r minimize)));
    icons.add(toolTip("Close this window", jimageButtonScaledToWidth(16, #1103067, rThread deleteMe)));
      
    //setHorizontalMarginForAllButtons(icons, 4);
    icons.add(new JPopDownButton(menu -> fillJPopupMenu(menu, flattenToList(
              
      // always on top
      
      jCheckBoxMenuItem/*_dyn*/("Always on top",
        /*->*/ alwaysOnTop,
        alwaysOnTop -> setAlwaysOnTop(alwaysOnTop)),
        
      // quit, restart + update
      
      "---",
      "Exit Gazelle", rThread { host.cleanExit() },
      "Restart", rThread { host.restart() },
      "Re-open project", rThread { host.reopenDB(db) },
      "Hide project", rThread { db.hide() },
      "Update Gazelle", rThread { host.upgradeGazelle() },
      !isMain() ? null : jCheckBoxMenuItem/*_dyn*/("Auto-Update",
        /*->*/ print(autoUpdate := db.g22utils.autoUpdateEnabled()),
        b -> {
          // save flag
          db.g22utils.setAutoUpdate(b);
          
          // if user just turned on auto-update, do the update
          if (b) thread { host.upgradeGazelle(); }
        }),

      // debugging + screenshooting gazelle
      
      "---",
      !isMain() ? null : ll(
        "Screenshoot this window", rThread shootWindow,
        "Dump threads", rThread { showText("User threads", renderUserThreadsWithStackTraces()); },
        "Log", !isMain() ? null : r { db.showPrintLog() },
        "Copy log to clipboard", !isMain() ? null : rThread {
          copyToClipboard(printLog());
          infoBox("Copied log to clipboard");
        },
        "10 Second Profiler", rThread runTenSecondProfiler,
      ),
      
      ccallOpt(module, "popDownItems")
    ))));
    
    JComponent actualTitle = titleComponent = fontSizePlus(7, jCenteredLabel(title));
    installWindowDragger(actualTitle);
    
    if (/*isMain() &&*/ host.devMode())
      actualTitle = centerAndEastWithMargin(actualTitle,
        jline(
          compileButton = makeCompileButton(),
          installWindowDragger(lblComputerAndUserCount()),
        ));
        
    updateFrameIcon();
    
    var spacer = gazelle_wavySpacer(color1(), color2());
    var titleBarMain = setOpaqueBackground(color1(),
      jtransparent_recursive(westCenterAndEastWithMargin(
        scpFrameIcon(),
        actualTitle,
        setOpaqueBackground(color2(), spacer)));
    
    installWindowDragger(spacer);
    
    var titleBar = setBackground(color2(), centerAndEast(
      titleBarMain,
      icons));
    
    var border =
      // createBevelBorder();
      BorderFactory.createLineBorder(color1(), host.borderSize);
      
    var outerPanel = withBorder(border,
      northAndCenter(titleBar, contents));
      
    installWindowResizeDraggerOnBorder(outerPanel);
    ret markVisualizer(this, outerPanel);
  }
  
  swappable S frameIconID() { ret host.trayIconImageID; }
  swappable int frameIconHeight() { ret 24; }
  
  selfType frameIconID(S iconID) {
    frameIconID = -> iconID;
    updateFrameIcon();
    this;
  }
  
  swappable JComponent makeFrameIcon() {
    ret jImage_scaledToHeight(frameIconHeight(), frameIconID());
  }
  
  void updateFrameIcon {
    scpFrameIcon().set(makeFrameIcon());
  }
  
  swappable Color color1() { ret host.color1; }
  swappable Color color2() { ret host.color2; }

  JLabel lblComputerAndUserCount() {
    JLabel label = jlabel();
    Runnable update = -> {
      Int computerCount = host.computerCount();
      Int userCount = host.lvGazelleUserCount()!;
      setText(label, 
        (computerCount == null ? "" : n2(computerCount))
        + appendSquareBracketed(
          userCount == null ? "" : n2(userCount)));
    };
    bindChangeListenerToComponent(label, host.lvComputerCount(), update);
    bindChangeListenerToComponent(label, host.lvGazelleUserCount(), update);
    ret label;
  }
  
  JButton makeCompileButton() {
    var gears = resizeImageIcon(imageIcon(#1103061), 16);

    var btn = jimageButtonScaledToWidth(16, #1101268, "Compile & update",
      rThread { host.compile() });
    //enableComponentAccordingToVar(host.compileButtonEnabled, btn);
    
    componentPopupMenuItems(btn,
      "Compile all", rThread { host.compileAll() },
      "Compile main library", rThread { host.compileUtilsAndRepackage() },
      "Repackage", rThread { host.repackage() },
    );
    
    ret btn;
  }
  
  JComponent wrapVis(JComponent vis) {
    //ret jMinSize0(vis);
    ret vis;
  }
  
  void startAndShow(bool hidden default false) {
    if (module._host != this) {
      module._host = this;
      setOpt_raw(module, +threadPool); // only if the field exists
      setOpt_raw(module, concepts := db.concepts);
      setOpt_raw(module, g22utils := db.g22utils);
      copyLocalLog(module, mc());
    }
  
    var vis, title = unpair startAndVisualize();
    
    if (!hidden) {
      printWithMS("Show frame");
  
      JFrame frame = makeUndecoratedFrame(title, wrapVis(vis));
      setFrameIcon(frame, host.trayIconImageID);
      setWindow(frame);
      
      frame.addPropertyChangeListener("title", evt ->
        setText(titleComponent, frame.getTitle()));
      
      if (minimized) minimize();
      onWindowDeiconified(frame, -> cset(this, minimized := false));
      
      showWindow(window);
      if (maximized && host.allowFrameMaximization) maximizeFrame(window);
    }
  }
  
  Pair<JComponent, S> startAndVisualize() {
    JComponent vis = null;
    {
      printWithMS("Starting " + module);
      try {
        if (!started) {
          set started;
          temp module.enter();
          module.start();
        }
      
        printWithMS("Visualize");
        vis = swing(-> {
          temp module.enter();
          ret module.visualize();
        });
      } on fail e {
        waitForFrameClose(showErrorFrame(e));
      }
    }
    
    selfVis = vis;
    
    var scp1 = scpMainWindowContents();
    scp1.set(vis);
    
    S title = createTitleText();
    var vis2 = makeWindowBorderAndTitle(scp1, title);
    
    var scp2 = scpMainWindowWithTitle();
    scp2.set(vis2);
    ret pair(scp2, title);
  }
    
  S createTitleText() {
    S moduleName = takeFirst_unnull(800, module.moduleName());
    
    if (isMain())
      // OLD: ret host.windowTitle + appendWithColon(moduleName);
      ret joinNemptiesWithVBar(moduleName, host.windowTitle);
    else
      ret moduleName;
  }
  
  void revisualize {
    if (window == null) ret;
    
    pcall {
      module.unvisualize();
    }
    
    var vis, title = unpair startAndVisualize();
    setFrameContents(window, wrapVis(vis));
  }
  
  void shootWindow {
    var img = renderComponentToImage(window);
    copyImageToClipboard(img);
    File imgFile = saveInImageDirectoryWithCounter(gazelle22_imagesSubDirName() + "/Gazelle", img);
    pcallOpt(module, "addToGallery", imgFile);
  }
  
  // Breaking change 2022 Dec 10 (is this even used?)
  // old: Component vis() { ret getFrameContents(window); }
  Component vis() { ret scpMainWindowContents()!; }
  
  S tenSecondProfilerScript = [[
    temp profiler <- new AllThreadsProfiler
    infoBox "Profiling..."
    profiler start
    sleepSeconds 10
    showText "10 Second Profile" < profiler renderFullResults false
  ]];
  
  File byteCodePath() {
    ret assertNotNull(getBytecodePathForClass(this));
  }
  
  ClassNameResolver classNameResolver() {
    ret new ClassNameResolver().byteCodePath(byteCodePath()).init();
  }
  
  GazelleV_LeftArrowScriptParser leftArrowParser() {
    new GazelleV_LeftArrowScriptParser parser;
    parser.classNameResolver(classNameResolver());
    parser.allowTheWorld(mc(), utils.class);
    ret parser;
  }
  
  void runTenSecondProfiler {
    leftArrow(leftArrowParser(), tenSecondProfilerScript);
  }
} // end of Stem

sclass LoadedDB is AutoCloseable, IG22LoadedDB {
  GazelleHost host;
  gettable Concepts concepts;
  gettable new G22Utils g22utils;
  Stem mainStem;
  gettable bool hidden;
  
  *(GazelleHost *host, Concepts *concepts, bool *hidden) {
    g22utils.concepts(concepts);
    g22utils.masterStuff(host);
  }
  
  S name() {
    ret fileName(conceptsDir(concepts));
  }
  
  void start {
    concepts.grabThroughSocket(false);
    concepts.makeStructureData = -> {
      structure_Data data = concepts.makeStructureData_base();
      data.realShortName = name -> {
        S n = dropPrefixOrNull("userCode.", name);
        if (n != null)
          ret takeFirst(n, indexOf(n, '_'));
        ret data.realShortName_base(name);
      };
      data.mcDollar = mcDollar();
      ret data;
    };
    var lock = concepts.fileLock();
    try {
      lock.contentsForLockFile("Locked by computer " + computerID() + "\n");
      lock.lockOrFail();
    } catch print e {
      if (cic(str(e), "Couldn't aquire lock")) {
        if (!lock.hasExpectedContents()) {
          infoBox("Overriding project lock (" + lock.actualContents() + ")");
          lock.forceLock();
        } else {
          S msg = "This project is already opened in another process";
          infoBox(msg, 10.0);
          sleepSeconds(1);
          fail(msg);
        }
      } else
        throw rethrow(e);
    }
    concepts.fileLock().deleteOnExit();
    thinAProgramsBackups(concepts.conceptsDir(), true);
    
    //concepts.persist();
    concepts.loadConcepts();
    
    // Check that we are not loading with an old program version
    var projectInfo = optimizedUniq(concepts, G22ProjectInfo);
    S compilationDate = compilationDateFromClassPath(this);
    print("Compilation date in project: " + projectInfo.compilationDate);
    
    if (nempty(projectInfo.compilationDate)
      && lessThan(compilationDate, projectInfo.compilationDate)) {
        messageBoxAndFail("Trying to open project with old Gazelle version - please update Gazelle first (" + compilationDate + " vs " + projectInfo.compilationDate + ")");
        }
        
    if (cset(projectInfo, +compilationDate) != 0)
      print("Bumped compilation date in project to " + compilationDate);

    concepts.autoSaveConcepts();
    
    //db_setMainConcepts(concepts);
    
    //print("Concepts loaded: " + map className(allConcepts(concepts));
    
    var stems = stems();
    print("Stems in DB: " + l(stems));
    mainStem = first(stems);
    if (mainStem == null) {
      print("Starting new module");
      var module = host.makeModule();
      mainStem = registerConcept(concepts, new Stem(this, module));
      print("Stems in DB: " + l(stems()));
    }

    for (stem : stems()) {
      stem.db = this;
      stem.host = host;
      stem.startAndShow(hidden);
    }
    
    print("Stems in DB: " + l(stems()));
  }
  
  L<Stem> stems() { ret list Stem(concepts); }
  
  Q systemQ() { ret host.systemQ; }

  Stem findPrintLog() {  
    ret firstThat(list(concepts, Stem),
      stem -> stem.module instanceof PrintLogModule);
  }
  
  void showPrintLog {
    systemQ().add(-> {
      var logModule = findPrintLog();
      if (logModule != null)
        logModule.activateWindow();
      else
        registerConcept(concepts, new Stem(this, new PrintLogModule)).startAndShow();
    });
  }
  
  void hidePrintLog {
    systemQ().add(-> deleteConcept(findPrintLog()));
  }
  
  void activate {
    print("Activating DB: " + conceptsFile(concepts));
    if (hidden) {
      hidden = false;
      host.loadedDBsChange();
    }
    mainStem?.activateWindow();
  }
  
  void hide {
    hideWindow(mainWindow());
    if (!hidden) {
      hidden = true;
      host.loadedDBsChange();
    }
  }
  
  public JFrame mainWindow() {
    ret mainStem?.window;
  }

  close {  
    print("Cleaning up " + concepts);
    print("Stems in DB: " + l(stems()));
    
    for (Concept c : allConcepts(concepts))
      cleanUp(c);
      
    print("Releasing DB (" + n2(list(concepts, Stem), "stem") + ")");
    dispose concepts;

    host.loadedDBs.remove(this);
    host.loadedDBsChange();
    
    print("Remaining DBs: " + l(host.loadedDBs));
  }
  
  toString { ret name(); }
  
  DynModule module() {
    ret mainStem?.module;
  }
  
  public G22ProjectActions projectActions() {
    ret (G22ProjectActions) module();
  }
} // end of LoadedDB

// This replaces the Stefan's OS main class. It is not persisted
// and there is only one instance.

transient sclass GazelleHost is G22MasterStuff {
  Set<LoadedDB> loadedDBs = syncLinkedHashSet();
  event loadedDBsChange;
  event newClassesDefined;
  
  transient S repackageURL = "https://botcompany.de/jar/1033860?withX30=1&withLibs=1&noSrc=1&mainClassForManifest=Starter&repackage=1";

  // Title of this Gazelle instance
  transient S windowTitle = "Gazelle 22";
  
  transient S trayIconImageID = #1103047;
  transient int borderSize = 5;
  transient Color color1 = awtColor("ADD8E6");
  transient Color color2 = awtColor("EEEEEE");
  transient Color color3 = awtColor("A3C0AA");
  transient IF0<DynModule> moduleMaker;
  transient ThreadPool threadPool;
  
  transient File dbMotherDir;
  
  transient TrayIcon trayIcon;
  transient TrayIconLastPosition trayIconLastPosition;
  
  transient S[] args;
  transient Set<S> argsSet;
  transient bool incrementalDownloads = true;
  transient volatile bool shuttingDown;
  
  transient bool restrictWindowsToScreenSize /*= true*/;
  
  // This caused problems, disabled for now
  transient bool allowFrameMaximization;
  
  transient G22Utils g22utils;
  transient StefansOS_ConnectToServer serverConnector;
  transient ComputerCountListener computerCount;
  transient GazellePresenceListener gazellePresenceListener;
  
  transient gettable Q systemQ = startQ("System Q");
  
  transient AutoCloseable pimpedPopups;
  
  // Let's use one of these centrally because it blocks a thread
  // and does next to nothing most of the time.
  gettable new RunnablesReferenceQueue runnablesReferenceQueue;
  
  transient gettable ILASClassLoader lasClassLoader =
    //new LASClassLoader(getClass());
    new LASMultiClassLoader(mc()).runnablesReferenceQueue(assertNotNull(runnablesReferenceQueue));
  
  //transient BoolVarWithNotify compileButtonEnabled = new(true);
  
  gettable EphemeralObjectIDs ephemeralObjectIDs = new()
    .runnablesReferenceQueue(assertNotNull(runnablesReferenceQueue));
    
  transient bool autoExitDisabled;
  
  event fillingTrayIconMenu(PopupMenu menu);
  
  // add fields here
  
  *(ThreadPool *threadPool, IF0<DynModule> *moduleMaker) {
    g22utils = new G22Utils().masterStuff(this);
    
    onLoadedDBsChange(-> {
      if (shuttingDown) ret;
      var dbs = getLoadedDBs();
      if (nempty(dbs))
        g22utils.setOpenDBs(dbs);
      updateTrayIconMenu();
    });
  }
  
  // Reminder: We are in GazelleHost
  
  <A> A inSystemQ(IF0<A> f) {
    ret evalInQ(systemQ(), f);
  }
  
  JFrame mainWindow() {
    ret firstNonNull(loadedDBs(), db -> db.mainWindow());
  }

  void trayIconLeftClick {
    first(loadedDBs()).activate();
  }
  
  void makeTrayIcon {
    pcall-short {
      trayIcon_imageAutoSize = false;
      trayIcon = installTrayIcon(trayIconImageID, windowTitle,
        r trayIconLeftClick,
        /*"Show " + windowTitle, r trayIconLeftClick,
        "Exit " + windowTitle, r cleanExit*/
      );
      
      updateTrayIconMenu();
      trayIconLastPosition = new TrayIconLastPosition(trayIcon);
    }
  }
  
  void updateTrayIconMenu { rstUpdateTrayIconMenu.trigger(); }
  
  transient ReliableSingleThread rstUpdateTrayIconMenu = rstWithPostDelay(1000, l0 updateTrayIconMenuImpl);
  
  void updateTrayIconMenuImpl {
    if (trayIcon == null) ret;
    
    new PopupMenu menu;
    var projects = openProjects();
    printVars("updateTrayIconMenuImpl", +projects);
    for (IG22LoadedDB otherDB : projects) {
      S name = otherDB.g22utils().projectName();
      S status = otherDB.hidden() ? "Hidden" : "";
      addMenuItem(menu, name + appendSquareBracketed(status), rThread { otherDB.activate() });
    }
    
    fillingTrayIconMenu(menu);

    addMenuItem(menu, "Exit " + windowTitle, rThread cleanExit);
    trayIcon.setPopupMenu(menu);
  }
  
  void run(S[] args) {
    try {
      this.args = args;
      run2(args);
    } catch print e {
      //messageBox(e);
      hideTrayIcon(trayIcon);
      onWindowClosing(-> systemExit(1), getWindow(
        showText_fast_noWrap("Gazelle Error", renderStackTrace(e))));
    }
  }
  
  void run2(S[] args) {
    argsSet = asSet(args);
    
    S s = first(args);
    bool isDirectory = isDirectory(s);
    if (isDirectory)
      dbMotherDir = canonicalFile(s);
    printVars(args := asList(args), +s, +isDirectory, +dbMotherDir);
      
    print("Using mother directory: " + databasesMotherDir());
    
    windowTitle = or2(loadTextFile(newFile(dbMotherDir, "Gazelle Name")), windowTitle);
    
    if (!argsSet.contains("noflatlaf"))
      com.formdev.flatlaf.FlatLightLaf.setup();
    
    if (contains(args, "profile"))
      profileToConsole(() -> actualMain(args));
    else
      actualMain(args);
      
    if (argsSet.contains("brexit")) System.exit(0);
  }
  
  void actualMain(S... args) {
    vm_generalMap_put(stefansOS := this);
    
    System.out.println(hmsWithColonsAndMS() + ": Init");
    //x30.coreInit();
    
    // Ready to roll
    
    printJavaVersion();
    
    //tempAddGlobalCtrlKeyListener(b -> print("Ctrl key: " + b));
    
    if (contains(argsSet, "profile"))
      doEvery(1.0, r printRunningThreads);
    
    if (containsOneOf(argsSet, "upgrade", "update"))
      ret with upgradeGazelle();
      
    makeTrayIcon();
    
    initAutoUpdate();
    
    if (devMode()) {
      print("Pimping popup menus");
      PopupMenuMaker.fix();
      pimpedPopups = tempGlobalPopupMenu((c, menu) -> {
        printVars("pimpedPopups", c := className(c));
        
        // Skip linux tray icon popups
        if (contains(className(c), "XTrayIcon")) ret;
        
        // This is for popup menus displayed via setComponentPopupMenu
        // which are re-used instances
        if (any(getMenuItems(menu), menuItem
          -> cicOneOf(menuItem.getText(), "Component: ", "src: "))) ret;
          
        O src = componentMetaSrc(c);
        menu.add(commaCombine(
          classNameAndEphString(c),
          src == null ?: "src: " + classNameAndEphString(src)));
      });
    }
    
    // Start DB
    
    LS dbNames = g22utils.dbsToOpen();
    for (S dbName : dbNames) {
      S name2 = dropPrefix("*", dbName);
      openDB(name2, !eq(dbName, name2));
    }
    
    exitIfAllDBsClosed();
    
    printWithMS("Dudadoneski");
  }
  
  public bool projectExists(S dbName) {
    File conceptsFile = newFile(newFile(databasesMotherDir(), dbName), conceptsFileName());
    ret isFile(conceptsFile);
  }
  
  LoadedDB openDB(S dbName, bool hidden default false) {
    File conceptsFile = newFile(newFile(databasesMotherDir(), dbName), conceptsFileName());
    ret openDB(conceptsFile, hidden);
  }
  
  public IG22LoadedDB getLoadedDB(Concepts concepts) {
    ret firstThat(cloneList(loadedDBs), db -> db.concepts == concepts);
  }
  
  IG22LoadedDB getLoadedDBForConceptDir(File dir) {
    ret findLoadedDBForConceptsDir(dir);
  }

  LoadedDB findLoadedDBForConceptsDir(File conceptsDir) {
    ret firstThat(cloneList(loadedDBs), db -> sameFile(
      conceptsDir(db.concepts), conceptsDir));
  }
  
  LoadedDB findLoadedDBForConceptsFile(File conceptsFile) {
    ret firstThat(cloneList(loadedDBs), db -> sameFile(
      conceptsFile(db.concepts), conceptsFile));
  }
  
  public IG22LoadedDB reopenDB(IG22LoadedDB db) {
    ret reopenDB((LoadedDB) db);
  }
  
  LoadedDB reopenDB(LoadedDB db) {
    File conceptsFile = db.concepts.conceptsFile();
    try {
      autoExitDisabled = true;
      closeDatabase(db);
      ret openDB(conceptsFile);
    } finally {
      autoExitDisabled = false;
    }
  }
  
  LoadedDB openDB(File conceptsFile, bool hidden default false) {
    ret inSystemQ(-> {
      var loadedDB = findLoadedDBForConceptsFile(conceptsFile);
      if (loadedDB != null) {
        if (!hidden)
          loadedDB.activate();
        ret loadedDB;
      }
        
      temp tempInfoBoxNoHide("Opening Gazelle project " + quote(fileName(dirOfFile(conceptsFile))));
      
      // profile project opening
      //temp tempProfileSingleThreadToConsole();
  
      print("Loading new DB: " + f2s(conceptsFile));
      g22utils.addToRecentDBNames(parentName(conceptsFile), hidden);
      Concepts concepts = newConceptsWithClassFinder(conceptsFile, makeClassFinder());
      
      concepts.onSaveError(l1 infoBox);
      
      // to clean-up resources in project variables
      concepts.closeConceptsOnExit(true);
      
      var db = new LoadedDB(this, concepts, hidden);
      loadedDBs.add(db);
      try {
        db.start();
        loadedDBsChange();
      } catch print e {
        loadedDBs.remove(db);
        _close(db);
      }
      ret db;
    });
  }
  
  public Cl<File> openConceptDirs() {
    ret cloneMap(loadedDBs, _db -> conceptsDir(_db.concepts));
  }
  
  O resolveModule(O moduleOrStem) {
    ret moduleOrStem cast Stem ? moduleOrStem.module : moduleOrStem;
  }
  
  void cleanExit {
    set shuttingDown;
    print("Closing " + n2(loadedDBs, "database"));
    closeAndClear(loadedDBs);
    System.exit(0);
  }
  
  File myJar() { ret getBytecodePathForClass(this); }
  
  AutoCloseable tempDisableCompileButtons() {
    ret tempDisableButtons(compileButtons());
  }

  swappable void upgradeGazelleForeplay { 
    infoBox("Updating Gazelle in 10...");
    sleepSeconds(10);
    upgradeGazelle();
  }
  
  void upgradeGazelle {
    try {
      temp tempPrintAlsoToSystemOut();
      
      // Show an infoBox to make sure the code for infoBox is loaded
      temp tempInfoBox("Updating Gazelle...");
      
      //temp tempSetBoolVar(compileButtonEnabled, false);
      temp tempDisableCompileButtons();
      //for (f : allJFrames()) setFrameTitle(f, "Updating...");
      
      File myJar = myJar();
      print(+myJar);
      
      S existingFilesComp = !incrementalDownloads ? null
        : existingFilesInfo();
      
      S date = ymdMinusHMS();
      File f = javaxCodeDir("Downloaded Updates/" + "gazelle-" + date + ".jar");
      temp progress = tempProgressBar_addToWindowIfPossible(mainWindow(), "Updating Gazelle");

      progress.setText("Downloading Update...");
      time "Downloading zip" {
        postBinaryPageToFile(downloadURL(), f, +existingFilesComp);
      }
      printFileInfo(f);
      if (!isNonEmptySingleZip_byMagicHeader(f))
        ret with infoBox("Bad file downloaded... :(");
        
      bool replaced;
      if (isFile(myJar)) {
        // load all my classes to be sure (TODO: could do this in parallel to downloading)
        //print("Loaded " + nClasses(loadAllClassesInByteCodePath(myJar)));
        
        progress.setText("Replacing with new version: " + renderFileInfo(myJar));
        File myJarSaved = fileInSubDir("old-code",
          appendToBaseName(myJar, ".bak." + date));
        File myJarNew = appendToFileName(myJar, ".new");
          
        Set<S> toKeep = asSet(tlft(loadTextFileFromZip(f, "to-keep")));
        if (nempty(toKeep)) {
          int toUpdate = countFilesInZip(f)-1;
          //progress.setText("Keeping " + nFiles(toKeep));
          progress.setText("Updating " + nFiles(toUpdate));
        
          // Do we have anything to updatE?
          
          int have = countFilesInZip(myJar);
          if (l(toKeep) == have && countFilesInZip(f) == 1) {
            infoBox("Nothing to update - Gazelle is up to date!");
            deleteFile(myJarNew);
            deleteFile(myJarSaved);
            ret;
          }
          
          temp Zip4j_MergeZipFilesWithFilter merger = new(myJarNew);
          merger.addZipFile(myJar, name -> contains(toKeep, name));
          merger.addZipFile(f, name -> !eq(name, "to-keep"));
          merger.finish();
        } else
          myJarNew = f;

        if (isWindows()) {
          copyFileVerbose(myJar, myJarSaved);
          copyFileVerbose(myJarNew, myJar);
          deleteFileVerbose_assertSuccess(myJarNew);
        } else {
          renameFileVerbose_assertSuccess(myJar, myJarSaved);
          renameFileVerbose_assertSuccess(myJarNew, myJar);
        }

        printFileInfo(myJar);
        set replaced;
      }
        
      progress.setText("Done");
      
      if (replaced)
        infoBox("Installed update, replaced " + f2s(myJar) + " - now starting");
      else
        infoBox("Updated but could not replace originally downloaded jar");
        
      restart(replaced ? myJar : f);
    } on fail e { printStackTrace(e); }
  }
  
  void restart(File jar default myJar()) {
    temp tempPrintAlsoToSystemOut();
    
    S javaCmd = or2(currentProcessCommand(), "java");
    S cmd = platformQuoteArgs(flattenToList(
      javaCmd, "-jar", jar, args));
    print(cmd);
    nohup(cmd);
    cleanExit();
  }
  
  DynModule makeModule() {
    ret callF(moduleMaker);
  }
  
  void reloadModuleInBackground(Stem mod) {
    restart();
  }

  void initAutoUpdate {  
    print("Making server connection for updates.");
    serverConnector = snippetUpdateConnector(/*verbose := true*/);
    //serverConnector.verbose = true;
    
    vmBus_onMessage snippetUpdate(voidfunc(L l) {
      S uri = getString(l, 1);
      new Matches m;
      if (swic(uri, "/transpileOK/", m)) {
        if (sameSnippetID(programID(), firstIntAsString(m.rest()))) {
          if (g22utils.autoUpdateEnabled() /*&& autoUpdateDisabled*/) {
            upgradeGazelleForeplay();
          }
          //infoBox("Gazelle can be updated!");
        }
      } else if (swic(uri, "/edit/", m)) {
        S snippetID = fsI(firstIntAsString(m.rest());
        if (contains(stdFunctionListSnippetIDs(), snippetID))
          stdFunctions_clearCache();
        if (contains(standardClassesSnippetIDs(), snippetID))
          standardClassesMap_clearCache();
      }
    });
    
    pcall {
      computerCount = new ComputerCountListener(serverConnector);
      computerCount.init();
    }
    
    pcall {
      gazellePresenceListener = GazellePresenceListener(serverConnector);
      gazellePresenceListener.init();
    }
  }
  
  S downloadURL() {
    ret "https://botcompany.de/jar/" 
      + psI(programID()) + "?withLibs=1&withX30=1"
      + "&mainClassForManifest=Starter"
      + (!downloadJarWithSrc() ? "&noSrc=1" : "")
      + (downloadUncompressedJAR() ? "&uncompressed=1" : "");
  }
  
  bool downloadJarWithSrc() { true; }
  bool downloadUncompressedJAR() { false; }
  
  // called by DynModule
  void revisualizeModule(Stem stem) {
    stem.revisualize();
  }
  
  Int computerCount() { ret computerCount == null ? (Int) 0 : computerCount!; }
  LiveValue<Int> lvComputerCount() { ret computerCount?.liveValue(); }
  
  public IGetterWithNotify<Int> lvGazelleUserCount() { ret gazellePresenceListener?.userCount; }
  
  L<JButton> compileButtons() { ret mapNonNulls(allStems(), s -> s.compileButton()); }
  
  L<Stem> allStems() {
    ret concatLists(cloneMap(loadedDBs, db -> list(db.concepts, Stem)));
  }
  
  // compile main program
  void compile {
    compile(programID, null);
  }
  
  void resetNeedLatest {
    editSnippet(#1035306, "need latest .");
  }
  
  void compileAll {
    resetNeedLatest();
    compile(utilsSnippetID(), l0 compile);
  }
  
  void compileUtilsAndRepackage {
    compile(utilsSnippetID(), l0 repackage);
  }
  
  void compile(S snippetID, Runnable onSuccess) {
    print("Compiling " + snippetID);
    
    var gears = resizeImageIcon(imageIcon(#1103061), 16);
    
    temp tempDisableCompileButtons();
    temp tempSetButtonImages(compileButtons(), gears);
    
    thread { existingFilesInfo(); } // precompute
          
    var tr = TranspileWithErrorWindow().snippetID(snippetID);
    /*tr.transpileAction(->
      new TranspileForServer_v2(snippetID).run());*/
    tr.transpileAction(-> {
      var p = transpileOnNewServer(snippetID, "medium");
      if (!p.a) fail(p.b);
    });
    tr.onSuccess(onSuccess);
    tr.run();

    print("Done transpilation of " + snippetID);
  }
  
  void repackage {
    File f = javaxCachesDir("Gazelle-22/repackaged.jar");
    deleteFile(f);
    loadBinaryPageToFile(appendParamsToURL(repackageURL, uniq := randomID()), f);
    if (isJAR(f))
      infoBox("Repackaged.");
    else
      messageBox("Oh shit");
  }
  
  public void closeDatabase(LoadedDB db) {
    db.close();
    exitIfAllDBsClosed();
  }
  
  void exitIfAllDBsClosed {
    if (!autoExitDisabled && empty(loadedDBs))
      cleanExit();
  }
  
  Cl<? extends IG22LoadedDB> getLoadedDBs aka loadedDBs() {
    ret cloneList(loadedDBs);
  }
  
  public void closeDatabase(File conceptsDir) {
    for (db : cloneList(loadedDBs))
      if (sameFile(conceptsDir, conceptsDir(db.concepts)))
        db.close();
  }
  
  public void switchToDatabase(File conceptsDir) {
    if (conceptsDir == null) ret;
    // open/activate the desired database first
    var db = openDatabase(conceptsDir);
    
    if (db == null) ret; // probably impossible but whatever
    
    closeAllDBsExcept(db);
  }
  
  void closeAllDBsExcept(LoadedDB db) {
    closeAll(listMinus(loadedDBs, db));
  }
  
  public LoadedDB openDatabase(File conceptsDir, bool hidden default false) {
    temp tempPrintAlsoToSystemOut();
    
    File conceptsFile = newFile(conceptsDir, conceptsFileName());
    if (!fileExists(conceptsFile))
      fail(infoBox("Database not found: " + conceptsFile));
      
    var db = openDB(conceptsFile, hidden);
    
    print("Database opening complete");
    ret db;
  }
  
  Map<S, Class> classMigrations() {
    ret litmap(
      G22RecognizerScript := G22Analyzer,
      GalleryImage := G22GalleryImage);
  }

  public IF1<S, Class> makeClassFinder() {
    IF1<S, Class> defaultClassFinder = toIF1(_defaultClassFinder());
    var migrations = classMigrations();
    IF1<S, Class> classFinder = name -> {
      // class migration
      S shortName = dropPrefixOrNull("main$", name);
      if (shortName != null) {
        Class c = migrations.get(shortName);
        if (c != null) {
          S newName = className(c);
          print("Migrating legacy class " + name + " to " + newName);
          name = newName;
        }
      }
      
      ret defaultClassFinder.get(name);
    };
    
    // Test classFinder sanity
    
    assertEquals(callF(classFinder, "main$Stem"), Stem.class);
    
    ret classFinder;
  }
  
  transient cached S existingFilesInfo() {
    L<Map> fingerprints =  zipFileToJSONFingerprint_md5(myJar());
    S s = base64encode(gzipString(jsonEncode(fingerprints)));
    print(existingFilesInfo := nBytes(l(s)));
    ret s;
  }
  
  bool devMode() { ret g22_devMode(); }
  
  // remember ephemeral object, return script code to get it back
  S ephString(O o) {
    ret o == null ? "" : "eph " + ephemeralObjectIDs.remember(o);
  }
  
  S classNameAndEphString(O o) {
    ret o == null ?: spaceCombine(ephString(o), className(o));
  }
  
  public File databasesMotherDir() {
    if (dbMotherDir == null)
      dbMotherDir = javaxDataDir("Gazelle-22");
    ret dbMotherDir;
  }
} // end of GazelleHost [G22MasterStuff]

Author comment

Began life as a copy of #1033540

download  show line numbers  debug dex  old transpilations   

Travelled to 6 computer(s): bhatertpkbcr, ekrmjmnbrukm, iveijnkanddl, mowyntqkapby, mqqgnosmbjvj, wnsclhtenguj

No comments. add comment

Snippet ID: #1033891
Snippet name: GazelleHost + Stem [dev]
Eternal ID of this version: #1033891/366
Text MD5: f40e7bbf5635c05ba56c7fa4aef23ff0
Author: stefan
Category: javax / gazelle 22
Type: JavaX (incomplete)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2023-05-07 13:36:56
Source code size: 36016 bytes / 1217 lines
Pitched / IR pitched: No / No
Views / Downloads: 729 / 3586
Version history: 365 change(s)
Referenced in: [show references]