1 | lib 1400521 // FlatLAF |
2 | |
3 | set flag PingV3. |
4 | set flag NotifyingPrintLog. |
5 | set flag SymbolAsString. |
6 | |
7 | // Do the JavaX init |
8 | svoid standaloneInit { |
9 | __javax = x30.class; |
10 | x30.__javax = x30.class; // for hotwire |
11 | x30_pkg.x30_util.__setJavaX(x30.class); |
12 | x30.cleanKillMsg = ""; |
13 | callOnLoadMethods(mc()); |
14 | } |
15 | |
16 | concept PrintLogModule > DynPrintLog { |
17 | start { setModuleName("Log"); } |
18 | |
19 | S getPrintLog() { |
20 | ret printLog(); |
21 | } |
22 | |
23 | bool useErrorHandling() { false; } |
24 | } |
25 | |
26 | concept Stem extends ConceptWithChangeListeners is AutoCloseable { |
27 | transient GazelleHost host; |
28 | transient LoadedDB db; |
29 | DynModule module; |
30 | settableWithVar Rect frameRect; |
31 | bool minimized, maximized, alwaysOnTop, minimizeToTray; |
32 | |
33 | transient JFrame window; |
34 | transient JButton btnMax; |
35 | transient JLabel titleComponent; |
36 | transient gettable JButton compileButton; |
37 | //transient bool autoUpdateDisabled; |
38 | transient bool started; |
39 | |
40 | *() {} |
41 | *(LoadedDB *db) { host = db.host; } |
42 | *(LoadedDB db, DynModule *module) { this(db); } |
43 | |
44 | transient Component selfVis; // how the module visualized itself |
45 | |
46 | // Not sure why we use LazyVar here instead of simplyCached |
47 | |
48 | transient LazyVar<SingleComponentPanel> scpMainWindowContents = new(-> scp()); |
49 | SingleComponentPanel scpMainWindowContents() { ret scpMainWindowContents!; } |
50 | |
51 | transient LazyVar<SingleComponentPanel> scpMainWindowWithTitle = new(-> scp()); |
52 | SingleComponentPanel scpMainWindowWithTitle() { ret scpMainWindowWithTitle!; } |
53 | |
54 | transient LazyVar<SingleComponentPanel> scpFrameIcon = new(-> scp()); |
55 | SingleComponentPanel scpFrameIcon() { ret scpFrameIcon!; } |
56 | |
57 | Rect grabFrameRect() { |
58 | ret toRect(getBounds(window)); |
59 | } |
60 | |
61 | void saveFrameRect { |
62 | frameRect(grabFrameRect()); |
63 | } |
64 | |
65 | void restoreFrameRect { |
66 | if (frameRect != null) |
67 | setBounds(window, frameRect); |
68 | } |
69 | |
70 | void setAlwaysOnTop(bool alwaysOnTop) { |
71 | this.alwaysOnTop = alwaysOnTop; |
72 | change(); |
73 | alwaysOnTop(window, alwaysOnTop); |
74 | } |
75 | |
76 | void setMinimizeToTray(bool minimizeToTray) { |
77 | cset(this, +minimizeToTray); |
78 | } |
79 | |
80 | void setWindow(JFrame window) { |
81 | this.window = window; |
82 | restoreFrameRect(); |
83 | //onWindowClosing(window, -> host.cleanExit()); |
84 | onBoundsChange(window, r saveFrameRect); |
85 | |
86 | if (host.restrictWindowsToScreenSize) |
87 | restrictWindowToScreenSize(window); |
88 | |
89 | alwaysOnTop(window, alwaysOnTop); |
90 | } |
91 | |
92 | void activateWindow { |
93 | print("activateWindow: " + window); |
94 | if (window != null) |
95 | activateFrame_v3(window); |
96 | else |
97 | startAndShow(false); |
98 | } |
99 | |
100 | bool isMain() { ret this == db.mainStem; } |
101 | |
102 | void minimize { |
103 | cset(this, minimized := true); |
104 | if (!minimizeToTray || host.trayIcon == null || !isMain()) |
105 | minimizeWindow(window); |
106 | else |
107 | hideWindow(window); |
108 | } |
109 | |
110 | void deleteMe { |
111 | temp tempPrintAlsoToSystemOut(); |
112 | print("deleteMe " + isMain()); |
113 | if (isMain()) |
114 | host.closeDatabase(db); |
115 | else |
116 | delete(); |
117 | } |
118 | |
119 | close { |
120 | disposeWindow(window); |
121 | cleanUp(module); |
122 | } |
123 | |
124 | void delete :: before { |
125 | close(); |
126 | } |
127 | |
128 | void grabWindowState { |
129 | setField(maximized := !isExtendedStateNormal(window)); |
130 | } |
131 | |
132 | void updateBtnMax { |
133 | if (!isShowing(btnMax)) ret; |
134 | grabWindowState(); |
135 | setText(btnMax, maximized ? "NORM" : "MAX"); |
136 | } |
137 | |
138 | JComponent makeWindowBorderAndTitle(JComponent contents, S title) { |
139 | //ret jCenteredSection_fontSizePlus(10, title, vis); |
140 | |
141 | var icons = jline(); |
142 | if (host.allowFrameMaximization) { |
143 | icons.add(btnMax = jbutton("", rThread { |
144 | maximizeOrRestoreFrame(window); |
145 | updateBtnMax(); |
146 | })); |
147 | onFirstShowing(btnMax, r updateBtnMax); |
148 | } |
149 | |
150 | icons.add(toolTip("Minimize this window", jbutton("MIN", r minimize))); |
151 | icons.add(toolTip("Close this window", jimageButtonScaledToWidth(16, #1103067, rThread deleteMe))); |
152 | |
153 | //setHorizontalMarginForAllButtons(icons, 4); |
154 | icons.add(new JPopDownButton(menu -> fillJPopupMenu(menu, flattenToList( |
155 | |
156 | // always on top |
157 | |
158 | jCheckBoxMenuItem/*_dyn*/("Always on top", |
159 | /*->*/ alwaysOnTop, |
160 | alwaysOnTop -> setAlwaysOnTop(alwaysOnTop)), |
161 | |
162 | // quit, restart + update |
163 | |
164 | "---", |
165 | "Exit Gazelle", rThread { host.cleanExit() }, |
166 | "Restart", rThread { host.restart() }, |
167 | "Re-open project", rThread { host.reopenDB(db) }, |
168 | "Hide project", rThread { db.hide() }, |
169 | "Update Gazelle", rThread { host.upgradeGazelle() }, |
170 | !isMain() ? null : jCheckBoxMenuItem/*_dyn*/("Auto-Update", |
171 | /*->*/ print(autoUpdate := db.g22utils.autoUpdateEnabled()), |
172 | b -> { |
173 | // save flag |
174 | db.g22utils.setAutoUpdate(b); |
175 | |
176 | // if user just turned on auto-update, do the update |
177 | if (b) thread { host.upgradeGazelle(); } |
178 | }), |
179 | |
180 | // debugging + screenshooting gazelle |
181 | |
182 | "---", |
183 | !isMain() ? null : ll( |
184 | "Screenshoot this window", rThread shootWindow, |
185 | "Dump threads", rThread { showText("User threads", renderUserThreadsWithStackTraces()); }, |
186 | "Log", !isMain() ? null : r { db.showPrintLog() }, |
187 | "Copy log to clipboard", !isMain() ? null : rThread { |
188 | copyToClipboard(printLog()); |
189 | infoBox("Copied log to clipboard"); |
190 | }, |
191 | "10 Second Profiler", rThread runTenSecondProfiler, |
192 | ), |
193 | |
194 | ccallOpt(module, "popDownItems") |
195 | )))); |
196 | |
197 | JComponent actualTitle = titleComponent = fontSizePlus(7, jCenteredLabel(title)); |
198 | installWindowDragger(actualTitle); |
199 | |
200 | if (/*isMain() &&*/ host.devMode()) |
201 | actualTitle = centerAndEastWithMargin(actualTitle, |
202 | jline( |
203 | compileButton = makeCompileButton(), |
204 | installWindowDragger(lblComputerAndUserCount()), |
205 | )); |
206 | |
207 | updateFrameIcon(); |
208 | |
209 | var spacer = gazelle_wavySpacer(color1(), color2()); |
210 | var titleBarMain = setOpaqueBackground(color1(), |
211 | jtransparent_recursive(westCenterAndEastWithMargin( |
212 | scpFrameIcon(), |
213 | actualTitle, |
214 | setOpaqueBackground(color2(), spacer))); |
215 | |
216 | installWindowDragger(spacer); |
217 | |
218 | var titleBar = setBackground(color2(), centerAndEast( |
219 | titleBarMain, |
220 | icons)); |
221 | |
222 | var border = |
223 | // createBevelBorder(); |
224 | BorderFactory.createLineBorder(color1(), host.borderSize); |
225 | |
226 | var outerPanel = withBorder(border, |
227 | northAndCenter(titleBar, contents)); |
228 | |
229 | installWindowResizeDraggerOnBorder(outerPanel); |
230 | ret markVisualizer(this, outerPanel); |
231 | } |
232 | |
233 | swappable S frameIconID() { ret host.trayIconImageID; } |
234 | swappable int frameIconHeight() { ret 24; } |
235 | |
236 | selfType frameIconID(S iconID) { |
237 | frameIconID = -> iconID; |
238 | updateFrameIcon(); |
239 | this; |
240 | } |
241 | |
242 | swappable JComponent makeFrameIcon() { |
243 | ret jImage_scaledToHeight(frameIconHeight(), frameIconID()); |
244 | } |
245 | |
246 | void updateFrameIcon { |
247 | scpFrameIcon().set(makeFrameIcon()); |
248 | } |
249 | |
250 | swappable Color color1() { ret host.color1; } |
251 | swappable Color color2() { ret host.color2; } |
252 | |
253 | JLabel lblComputerAndUserCount() { |
254 | JLabel label = jlabel(); |
255 | Runnable update = -> { |
256 | Int computerCount = host.computerCount(); |
257 | Int userCount = host.lvGazelleUserCount()!; |
258 | setText(label, |
259 | (computerCount == null ? "" : n2(computerCount)) |
260 | + appendSquareBracketed( |
261 | userCount == null ? "" : n2(userCount))); |
262 | }; |
263 | bindChangeListenerToComponent(label, host.lvComputerCount(), update); |
264 | bindChangeListenerToComponent(label, host.lvGazelleUserCount(), update); |
265 | ret label; |
266 | } |
267 | |
268 | JButton makeCompileButton() { |
269 | var gears = resizeImageIcon(imageIcon(#1103061), 16); |
270 | |
271 | var btn = jimageButtonScaledToWidth(16, #1101268, "Compile & update", |
272 | rThread { host.compile() }); |
273 | //enableComponentAccordingToVar(host.compileButtonEnabled, btn); |
274 | |
275 | componentPopupMenuItems(btn, |
276 | "Compile all", rThread { host.compileAll() }, |
277 | "Compile main library", rThread { host.compileUtilsAndRepackage() }, |
278 | "Repackage", rThread { host.repackage() }, |
279 | ); |
280 | |
281 | ret btn; |
282 | } |
283 | |
284 | JComponent wrapVis(JComponent vis) { |
285 | //ret jMinSize0(vis); |
286 | ret vis; |
287 | } |
288 | |
289 | void startAndShow(bool hidden default false) { |
290 | if (module._host != this) { |
291 | module._host = this; |
292 | setOpt_raw(module, +threadPool); // only if the field exists |
293 | setOpt_raw(module, concepts := db.concepts); |
294 | setOpt_raw(module, g22utils := db.g22utils); |
295 | copyLocalLog(module, mc()); |
296 | } |
297 | |
298 | var vis, title = unpair startAndVisualize(); |
299 | |
300 | if (!hidden) { |
301 | printWithMS("Show frame"); |
302 | |
303 | JFrame frame = makeUndecoratedFrame(title, wrapVis(vis)); |
304 | setFrameIcon(frame, host.trayIconImageID); |
305 | setWindow(frame); |
306 | |
307 | frame.addPropertyChangeListener("title", evt -> |
308 | setText(titleComponent, frame.getTitle())); |
309 | |
310 | if (minimized) minimize(); |
311 | onWindowDeiconified(frame, -> cset(this, minimized := false)); |
312 | |
313 | showWindow(window); |
314 | if (maximized && host.allowFrameMaximization) maximizeFrame(window); |
315 | } |
316 | } |
317 | |
318 | Pair<JComponent, S> startAndVisualize() { |
319 | JComponent vis = null; |
320 | { |
321 | printWithMS("Starting " + module); |
322 | try { |
323 | if (!started) { |
324 | set started; |
325 | temp module.enter(); |
326 | module.start(); |
327 | } |
328 | |
329 | printWithMS("Visualize"); |
330 | vis = swing(-> { |
331 | temp module.enter(); |
332 | ret module.visualize(); |
333 | }); |
334 | } on fail e { |
335 | waitForFrameClose(showErrorFrame(e)); |
336 | } |
337 | } |
338 | |
339 | selfVis = vis; |
340 | |
341 | var scp1 = scpMainWindowContents(); |
342 | scp1.set(vis); |
343 | |
344 | S title = createTitleText(); |
345 | var vis2 = makeWindowBorderAndTitle(scp1, title); |
346 | |
347 | var scp2 = scpMainWindowWithTitle(); |
348 | scp2.set(vis2); |
349 | ret pair(scp2, title); |
350 | } |
351 | |
352 | S createTitleText() { |
353 | S moduleName = takeFirst_unnull(800, module.moduleName()); |
354 | |
355 | if (isMain()) |
356 | // OLD: ret host.windowTitle + appendWithColon(moduleName); |
357 | ret joinNemptiesWithVBar(moduleName, host.windowTitle); |
358 | else |
359 | ret moduleName; |
360 | } |
361 | |
362 | void revisualize { |
363 | if (window == null) ret; |
364 | |
365 | pcall { |
366 | module.unvisualize(); |
367 | } |
368 | |
369 | var vis, title = unpair startAndVisualize(); |
370 | setFrameContents(window, wrapVis(vis)); |
371 | } |
372 | |
373 | void shootWindow { |
374 | var img = renderComponentToImage(window); |
375 | copyImageToClipboard(img); |
376 | File imgFile = saveInImageDirectoryWithCounter(gazelle22_imagesSubDirName() + "/Gazelle", img); |
377 | pcallOpt(module, "addToGallery", imgFile); |
378 | } |
379 | |
380 | // Breaking change 2022 Dec 10 (is this even used?) |
381 | // old: Component vis() { ret getFrameContents(window); } |
382 | Component vis() { ret scpMainWindowContents()!; } |
383 | |
384 | S tenSecondProfilerScript = [[ |
385 | temp profiler <- new AllThreadsProfiler |
386 | infoBox "Profiling..." |
387 | profiler start |
388 | sleepSeconds 10 |
389 | showText "10 Second Profile" < profiler renderFullResults false |
390 | ]]; |
391 | |
392 | File byteCodePath() { |
393 | ret assertNotNull(getBytecodePathForClass(this)); |
394 | } |
395 | |
396 | ClassNameResolver classNameResolver() { |
397 | ret new ClassNameResolver().byteCodePath(byteCodePath()).init(); |
398 | } |
399 | |
400 | GazelleV_LeftArrowScriptParser leftArrowParser() { |
401 | new GazelleV_LeftArrowScriptParser parser; |
402 | parser.classNameResolver(classNameResolver()); |
403 | parser.allowTheWorld(mc(), utils.class); |
404 | ret parser; |
405 | } |
406 | |
407 | void runTenSecondProfiler { |
408 | leftArrow(leftArrowParser(), tenSecondProfilerScript); |
409 | } |
410 | } // end of Stem |
411 | |
412 | sclass LoadedDB is AutoCloseable, IG22LoadedDB { |
413 | GazelleHost host; |
414 | gettable Concepts concepts; |
415 | gettable new G22Utils g22utils; |
416 | Stem mainStem; |
417 | gettable bool hidden; |
418 | |
419 | *(GazelleHost *host, Concepts *concepts, bool *hidden) { |
420 | g22utils.concepts(concepts); |
421 | g22utils.masterStuff(host); |
422 | } |
423 | |
424 | S name() { |
425 | ret fileName(conceptsDir(concepts)); |
426 | } |
427 | |
428 | void start { |
429 | concepts.grabThroughSocket(false); |
430 | concepts.makeStructureData = -> { |
431 | structure_Data data = concepts.makeStructureData_base(); |
432 | data.realShortName = name -> { |
433 | S n = dropPrefixOrNull("userCode.", name); |
434 | if (n != null) |
435 | ret takeFirst(n, indexOf(n, '_')); |
436 | ret data.realShortName_base(name); |
437 | }; |
438 | data.mcDollar = mcDollar(); |
439 | ret data; |
440 | }; |
441 | var lock = concepts.fileLock(); |
442 | try { |
443 | lock.contentsForLockFile("Locked by computer " + computerID() + "\n"); |
444 | lock.lockOrFail(); |
445 | } catch print e { |
446 | if (cic(str(e), "Couldn't aquire lock")) { |
447 | if (!lock.hasExpectedContents()) { |
448 | infoBox("Overriding project lock (" + lock.actualContents() + ")"); |
449 | lock.forceLock(); |
450 | } else { |
451 | S msg = "This project is already opened in another process"; |
452 | infoBox(msg, 10.0); |
453 | sleepSeconds(1); |
454 | fail(msg); |
455 | } |
456 | } else |
457 | throw rethrow(e); |
458 | } |
459 | concepts.fileLock().deleteOnExit(); |
460 | thinAProgramsBackups(concepts.conceptsDir(), true); |
461 | |
462 | //concepts.persist(); |
463 | concepts.loadConcepts(); |
464 | |
465 | // Check that we are not loading with an old program version |
466 | var projectInfo = optimizedUniq(concepts, G22ProjectInfo); |
467 | S compilationDate = compilationDateFromClassPath(this); |
468 | print("Compilation date in project: " + projectInfo.compilationDate); |
469 | |
470 | if (nempty(projectInfo.compilationDate) |
471 | && lessThan(compilationDate, projectInfo.compilationDate)) { |
472 | messageBoxAndFail("Trying to open project with old Gazelle version - please update Gazelle first (" + compilationDate + " vs " + projectInfo.compilationDate + ")"); |
473 | } |
474 | |
475 | if (cset(projectInfo, +compilationDate) != 0) |
476 | print("Bumped compilation date in project to " + compilationDate); |
477 | |
478 | concepts.autoSaveConcepts(); |
479 | |
480 | //db_setMainConcepts(concepts); |
481 | |
482 | //print("Concepts loaded: " + map className(allConcepts(concepts)); |
483 | |
484 | var stems = stems(); |
485 | print("Stems in DB: " + l(stems)); |
486 | mainStem = first(stems); |
487 | if (mainStem == null) { |
488 | print("Starting new module"); |
489 | var module = host.makeModule(); |
490 | mainStem = registerConcept(concepts, new Stem(this, module)); |
491 | print("Stems in DB: " + l(stems())); |
492 | } |
493 | |
494 | for (stem : stems()) { |
495 | stem.db = this; |
496 | stem.host = host; |
497 | stem.startAndShow(hidden); |
498 | } |
499 | |
500 | print("Stems in DB: " + l(stems())); |
501 | } |
502 | |
503 | L<Stem> stems() { ret list Stem(concepts); } |
504 | |
505 | Q systemQ() { ret host.systemQ; } |
506 | |
507 | Stem findPrintLog() { |
508 | ret firstThat(list(concepts, Stem), |
509 | stem -> stem.module instanceof PrintLogModule); |
510 | } |
511 | |
512 | void showPrintLog { |
513 | systemQ().add(-> { |
514 | var logModule = findPrintLog(); |
515 | if (logModule != null) |
516 | logModule.activateWindow(); |
517 | else |
518 | registerConcept(concepts, new Stem(this, new PrintLogModule)).startAndShow(); |
519 | }); |
520 | } |
521 | |
522 | void hidePrintLog { |
523 | systemQ().add(-> deleteConcept(findPrintLog())); |
524 | } |
525 | |
526 | void activate { |
527 | print("Activating DB: " + conceptsFile(concepts)); |
528 | if (hidden) { |
529 | hidden = false; |
530 | host.loadedDBsChange(); |
531 | } |
532 | mainStem?.activateWindow(); |
533 | } |
534 | |
535 | void hide { |
536 | hideWindow(mainWindow()); |
537 | if (!hidden) { |
538 | hidden = true; |
539 | host.loadedDBsChange(); |
540 | } |
541 | } |
542 | |
543 | public JFrame mainWindow() { |
544 | ret mainStem?.window; |
545 | } |
546 | |
547 | close { |
548 | print("Cleaning up " + concepts); |
549 | print("Stems in DB: " + l(stems())); |
550 | |
551 | for (Concept c : allConcepts(concepts)) |
552 | cleanUp(c); |
553 | |
554 | print("Releasing DB (" + n2(list(concepts, Stem), "stem") + ")"); |
555 | dispose concepts; |
556 | |
557 | host.loadedDBs.remove(this); |
558 | host.loadedDBsChange(); |
559 | |
560 | print("Remaining DBs: " + l(host.loadedDBs)); |
561 | } |
562 | |
563 | toString { ret name(); } |
564 | |
565 | DynModule module() { |
566 | ret mainStem?.module; |
567 | } |
568 | |
569 | public G22ProjectActions projectActions() { |
570 | ret (G22ProjectActions) module(); |
571 | } |
572 | } // end of LoadedDB |
573 | |
574 | // This replaces the Stefan's OS main class. It is not persisted |
575 | // and there is only one instance. |
576 | |
577 | transient sclass GazelleHost is G22MasterStuff { |
578 | Set<LoadedDB> loadedDBs = syncLinkedHashSet(); |
579 | event loadedDBsChange; |
580 | event newClassesDefined; |
581 | |
582 | transient S repackageURL = "https://botcompany.de/jar/1033860?withX30=1&withLibs=1&noSrc=1&mainClassForManifest=Starter&repackage=1"; |
583 | |
584 | // Title of this Gazelle instance |
585 | transient S windowTitle = "Gazelle 22"; |
586 | |
587 | transient S trayIconImageID = #1103047; |
588 | transient int borderSize = 5; |
589 | transient Color color1 = awtColor("ADD8E6"); |
590 | transient Color color2 = awtColor("EEEEEE"); |
591 | transient Color color3 = awtColor("A3C0AA"); |
592 | transient IF0<DynModule> moduleMaker; |
593 | transient ThreadPool threadPool; |
594 | |
595 | transient File dbMotherDir; |
596 | |
597 | transient TrayIcon trayIcon; |
598 | transient TrayIconLastPosition trayIconLastPosition; |
599 | |
600 | transient S[] args; |
601 | transient Set<S> argsSet; |
602 | transient bool incrementalDownloads = true; |
603 | transient volatile bool shuttingDown; |
604 | |
605 | transient bool restrictWindowsToScreenSize /*= true*/; |
606 | |
607 | // This caused problems, disabled for now |
608 | transient bool allowFrameMaximization; |
609 | |
610 | transient G22Utils g22utils; |
611 | transient StefansOS_ConnectToServer serverConnector; |
612 | transient ComputerCountListener computerCount; |
613 | transient GazellePresenceListener gazellePresenceListener; |
614 | |
615 | transient gettable Q systemQ = startQ("System Q"); |
616 | |
617 | transient AutoCloseable pimpedPopups; |
618 | |
619 | // Let's use one of these centrally because it blocks a thread |
620 | // and does next to nothing most of the time. |
621 | gettable new RunnablesReferenceQueue runnablesReferenceQueue; |
622 | |
623 | transient gettable ILASClassLoader lasClassLoader = |
624 | //new LASClassLoader(getClass()); |
625 | new LASMultiClassLoader(mc()).runnablesReferenceQueue(assertNotNull(runnablesReferenceQueue)); |
626 | |
627 | //transient BoolVarWithNotify compileButtonEnabled = new(true); |
628 | |
629 | gettable EphemeralObjectIDs ephemeralObjectIDs = new() |
630 | .runnablesReferenceQueue(assertNotNull(runnablesReferenceQueue)); |
631 | |
632 | transient bool autoExitDisabled; |
633 | |
634 | event fillingTrayIconMenu(PopupMenu menu); |
635 | |
636 | // add fields here |
637 | |
638 | *(ThreadPool *threadPool, IF0<DynModule> *moduleMaker) { |
639 | g22utils = new G22Utils().masterStuff(this); |
640 | |
641 | onLoadedDBsChange(-> { |
642 | if (shuttingDown) ret; |
643 | var dbs = getLoadedDBs(); |
644 | if (nempty(dbs)) |
645 | g22utils.setOpenDBs(dbs); |
646 | updateTrayIconMenu(); |
647 | }); |
648 | } |
649 | |
650 | // Reminder: We are in GazelleHost |
651 | |
652 | <A> A inSystemQ(IF0<A> f) { |
653 | ret evalInQ(systemQ(), f); |
654 | } |
655 | |
656 | JFrame mainWindow() { |
657 | ret firstNonNull(loadedDBs(), db -> db.mainWindow()); |
658 | } |
659 | |
660 | void trayIconLeftClick { |
661 | first(loadedDBs()).activate(); |
662 | } |
663 | |
664 | void makeTrayIcon { |
665 | pcall-short { |
666 | trayIcon_imageAutoSize = false; |
667 | trayIcon = installTrayIcon(trayIconImageID, windowTitle, |
668 | r trayIconLeftClick, |
669 | /*"Show " + windowTitle, r trayIconLeftClick, |
670 | "Exit " + windowTitle, r cleanExit*/ |
671 | ); |
672 | |
673 | updateTrayIconMenu(); |
674 | trayIconLastPosition = new TrayIconLastPosition(trayIcon); |
675 | } |
676 | } |
677 | |
678 | void updateTrayIconMenu { rstUpdateTrayIconMenu.trigger(); } |
679 | |
680 | transient ReliableSingleThread rstUpdateTrayIconMenu = rstWithPostDelay(1000, l0 updateTrayIconMenuImpl); |
681 | |
682 | void updateTrayIconMenuImpl { |
683 | if (trayIcon == null) ret; |
684 | |
685 | new PopupMenu menu; |
686 | var projects = openProjects(); |
687 | printVars("updateTrayIconMenuImpl", +projects); |
688 | for (IG22LoadedDB otherDB : projects) { |
689 | S name = otherDB.g22utils().projectName(); |
690 | S status = otherDB.hidden() ? "Hidden" : ""; |
691 | addMenuItem(menu, name + appendSquareBracketed(status), rThread { otherDB.activate() }); |
692 | } |
693 | |
694 | fillingTrayIconMenu(menu); |
695 | |
696 | addMenuItem(menu, "Exit " + windowTitle, rThread cleanExit); |
697 | trayIcon.setPopupMenu(menu); |
698 | } |
699 | |
700 | void run(S[] args) { |
701 | try { |
702 | this.args = args; |
703 | run2(args); |
704 | } catch print e { |
705 | //messageBox(e); |
706 | hideTrayIcon(trayIcon); |
707 | onWindowClosing(-> systemExit(1), getWindow( |
708 | showText_fast_noWrap("Gazelle Error", renderStackTrace(e)))); |
709 | } |
710 | } |
711 | |
712 | void run2(S[] args) { |
713 | argsSet = asSet(args); |
714 | |
715 | S s = first(args); |
716 | bool isDirectory = isDirectory(s); |
717 | if (isDirectory) |
718 | dbMotherDir = canonicalFile(s); |
719 | printVars(args := asList(args), +s, +isDirectory, +dbMotherDir); |
720 | |
721 | print("Using mother directory: " + databasesMotherDir()); |
722 | |
723 | windowTitle = or2(loadTextFile(newFile(dbMotherDir, "Gazelle Name")), windowTitle); |
724 | |
725 | if (!argsSet.contains("noflatlaf")) |
726 | com.formdev.flatlaf.FlatLightLaf.setup(); |
727 | |
728 | if (contains(args, "profile")) |
729 | profileToConsole(() -> actualMain(args)); |
730 | else |
731 | actualMain(args); |
732 | |
733 | if (argsSet.contains("brexit")) System.exit(0); |
734 | } |
735 | |
736 | void actualMain(S... args) { |
737 | vm_generalMap_put(stefansOS := this); |
738 | |
739 | System.out.println(hmsWithColonsAndMS() + ": Init"); |
740 | //x30.coreInit(); |
741 | |
742 | // Ready to roll |
743 | |
744 | printJavaVersion(); |
745 | |
746 | //tempAddGlobalCtrlKeyListener(b -> print("Ctrl key: " + b)); |
747 | |
748 | if (contains(argsSet, "profile")) |
749 | doEvery(1.0, r printRunningThreads); |
750 | |
751 | if (containsOneOf(argsSet, "upgrade", "update")) |
752 | ret with upgradeGazelle(); |
753 | |
754 | makeTrayIcon(); |
755 | |
756 | initAutoUpdate(); |
757 | |
758 | if (devMode()) { |
759 | print("Pimping popup menus"); |
760 | PopupMenuMaker.fix(); |
761 | pimpedPopups = tempGlobalPopupMenu((c, menu) -> { |
762 | printVars("pimpedPopups", c := className(c)); |
763 | |
764 | // Skip linux tray icon popups |
765 | if (contains(className(c), "XTrayIcon")) ret; |
766 | |
767 | // This is for popup menus displayed via setComponentPopupMenu |
768 | // which are re-used instances |
769 | if (any(getMenuItems(menu), menuItem |
770 | -> cicOneOf(menuItem.getText(), "Component: ", "src: "))) ret; |
771 | |
772 | O src = componentMetaSrc(c); |
773 | menu.add(commaCombine( |
774 | classNameAndEphString(c), |
775 | src == null ?: "src: " + classNameAndEphString(src))); |
776 | }); |
777 | } |
778 | |
779 | // Start DB |
780 | |
781 | LS dbNames = g22utils.dbsToOpen(); |
782 | for (S dbName : dbNames) { |
783 | S name2 = dropPrefix("*", dbName); |
784 | openDB(name2, !eq(dbName, name2)); |
785 | } |
786 | |
787 | exitIfAllDBsClosed(); |
788 | |
789 | printWithMS("Dudadoneski"); |
790 | } |
791 | |
792 | public bool projectExists(S dbName) { |
793 | File conceptsFile = newFile(newFile(databasesMotherDir(), dbName), conceptsFileName()); |
794 | ret isFile(conceptsFile); |
795 | } |
796 | |
797 | LoadedDB openDB(S dbName, bool hidden default false) { |
798 | File conceptsFile = newFile(newFile(databasesMotherDir(), dbName), conceptsFileName()); |
799 | ret openDB(conceptsFile, hidden); |
800 | } |
801 | |
802 | public IG22LoadedDB getLoadedDB(Concepts concepts) { |
803 | ret firstThat(cloneList(loadedDBs), db -> db.concepts == concepts); |
804 | } |
805 | |
806 | IG22LoadedDB getLoadedDBForConceptDir(File dir) { |
807 | ret findLoadedDBForConceptsDir(dir); |
808 | } |
809 | |
810 | LoadedDB findLoadedDBForConceptsDir(File conceptsDir) { |
811 | ret firstThat(cloneList(loadedDBs), db -> sameFile( |
812 | conceptsDir(db.concepts), conceptsDir)); |
813 | } |
814 | |
815 | LoadedDB findLoadedDBForConceptsFile(File conceptsFile) { |
816 | ret firstThat(cloneList(loadedDBs), db -> sameFile( |
817 | conceptsFile(db.concepts), conceptsFile)); |
818 | } |
819 | |
820 | public IG22LoadedDB reopenDB(IG22LoadedDB db) { |
821 | ret reopenDB((LoadedDB) db); |
822 | } |
823 | |
824 | LoadedDB reopenDB(LoadedDB db) { |
825 | File conceptsFile = db.concepts.conceptsFile(); |
826 | try { |
827 | autoExitDisabled = true; |
828 | closeDatabase(db); |
829 | ret openDB(conceptsFile); |
830 | } finally { |
831 | autoExitDisabled = false; |
832 | } |
833 | } |
834 | |
835 | LoadedDB openDB(File conceptsFile, bool hidden default false) { |
836 | ret inSystemQ(-> { |
837 | var loadedDB = findLoadedDBForConceptsFile(conceptsFile); |
838 | if (loadedDB != null) { |
839 | if (!hidden) |
840 | loadedDB.activate(); |
841 | ret loadedDB; |
842 | } |
843 | |
844 | temp tempInfoBoxNoHide("Opening Gazelle project " + quote(fileName(dirOfFile(conceptsFile)))); |
845 | |
846 | // profile project opening |
847 | //temp tempProfileSingleThreadToConsole(); |
848 | |
849 | print("Loading new DB: " + f2s(conceptsFile)); |
850 | g22utils.addToRecentDBNames(parentName(conceptsFile), hidden); |
851 | Concepts concepts = newConceptsWithClassFinder(conceptsFile, makeClassFinder()); |
852 | |
853 | concepts.onSaveError(l1 infoBox); |
854 | |
855 | // to clean-up resources in project variables |
856 | concepts.closeConceptsOnExit(true); |
857 | |
858 | var db = new LoadedDB(this, concepts, hidden); |
859 | loadedDBs.add(db); |
860 | try { |
861 | db.start(); |
862 | loadedDBsChange(); |
863 | } catch print e { |
864 | loadedDBs.remove(db); |
865 | _close(db); |
866 | } |
867 | ret db; |
868 | }); |
869 | } |
870 | |
871 | public Cl<File> openConceptDirs() { |
872 | ret cloneMap(loadedDBs, _db -> conceptsDir(_db.concepts)); |
873 | } |
874 | |
875 | O resolveModule(O moduleOrStem) { |
876 | ret moduleOrStem cast Stem ? moduleOrStem.module : moduleOrStem; |
877 | } |
878 | |
879 | void cleanExit { |
880 | set shuttingDown; |
881 | print("Closing " + n2(loadedDBs, "database")); |
882 | closeAndClear(loadedDBs); |
883 | System.exit(0); |
884 | } |
885 | |
886 | File myJar() { ret getBytecodePathForClass(this); } |
887 | |
888 | AutoCloseable tempDisableCompileButtons() { |
889 | ret tempDisableButtons(compileButtons()); |
890 | } |
891 | |
892 | swappable void upgradeGazelleForeplay { |
893 | infoBox("Updating Gazelle in 10..."); |
894 | sleepSeconds(10); |
895 | upgradeGazelle(); |
896 | } |
897 | |
898 | void upgradeGazelle { |
899 | try { |
900 | temp tempPrintAlsoToSystemOut(); |
901 | |
902 | // Show an infoBox to make sure the code for infoBox is loaded |
903 | temp tempInfoBox("Updating Gazelle..."); |
904 | |
905 | //temp tempSetBoolVar(compileButtonEnabled, false); |
906 | temp tempDisableCompileButtons(); |
907 | //for (f : allJFrames()) setFrameTitle(f, "Updating..."); |
908 | |
909 | File myJar = myJar(); |
910 | print(+myJar); |
911 | |
912 | S existingFilesComp = !incrementalDownloads ? null |
913 | : existingFilesInfo(); |
914 | |
915 | S date = ymdMinusHMS(); |
916 | File f = javaxCodeDir("Downloaded Updates/" + "gazelle-" + date + ".jar"); |
917 | temp progress = tempProgressBar_addToWindowIfPossible(mainWindow(), "Updating Gazelle"); |
918 | |
919 | progress.setText("Downloading Update..."); |
920 | time "Downloading zip" { |
921 | postBinaryPageToFile(downloadURL(), f, +existingFilesComp); |
922 | } |
923 | printFileInfo(f); |
924 | if (!isNonEmptySingleZip_byMagicHeader(f)) |
925 | ret with infoBox("Bad file downloaded... :("); |
926 | |
927 | bool replaced; |
928 | if (isFile(myJar)) { |
929 | // load all my classes to be sure (TODO: could do this in parallel to downloading) |
930 | //print("Loaded " + nClasses(loadAllClassesInByteCodePath(myJar))); |
931 | |
932 | progress.setText("Replacing with new version: " + renderFileInfo(myJar)); |
933 | File myJarSaved = fileInSubDir("old-code", |
934 | appendToBaseName(myJar, ".bak." + date)); |
935 | File myJarNew = appendToFileName(myJar, ".new"); |
936 | |
937 | Set<S> toKeep = asSet(tlft(loadTextFileFromZip(f, "to-keep"))); |
938 | if (nempty(toKeep)) { |
939 | int toUpdate = countFilesInZip(f)-1; |
940 | //progress.setText("Keeping " + nFiles(toKeep)); |
941 | progress.setText("Updating " + nFiles(toUpdate)); |
942 | |
943 | // Do we have anything to updatE? |
944 | |
945 | int have = countFilesInZip(myJar); |
946 | if (l(toKeep) == have && countFilesInZip(f) == 1) { |
947 | infoBox("Nothing to update - Gazelle is up to date!"); |
948 | deleteFile(myJarNew); |
949 | deleteFile(myJarSaved); |
950 | ret; |
951 | } |
952 | |
953 | temp Zip4j_MergeZipFilesWithFilter merger = new(myJarNew); |
954 | merger.addZipFile(myJar, name -> contains(toKeep, name)); |
955 | merger.addZipFile(f, name -> !eq(name, "to-keep")); |
956 | merger.finish(); |
957 | } else |
958 | myJarNew = f; |
959 | |
960 | if (isWindows()) { |
961 | copyFileVerbose(myJar, myJarSaved); |
962 | copyFileVerbose(myJarNew, myJar); |
963 | deleteFileVerbose_assertSuccess(myJarNew); |
964 | } else { |
965 | renameFileVerbose_assertSuccess(myJar, myJarSaved); |
966 | renameFileVerbose_assertSuccess(myJarNew, myJar); |
967 | } |
968 | |
969 | printFileInfo(myJar); |
970 | set replaced; |
971 | } |
972 | |
973 | progress.setText("Done"); |
974 | |
975 | if (replaced) |
976 | infoBox("Installed update, replaced " + f2s(myJar) + " - now starting"); |
977 | else |
978 | infoBox("Updated but could not replace originally downloaded jar"); |
979 | |
980 | restart(replaced ? myJar : f); |
981 | } on fail e { printStackTrace(e); } |
982 | } |
983 | |
984 | void restart(File jar default myJar()) { |
985 | temp tempPrintAlsoToSystemOut(); |
986 | |
987 | S javaCmd = or2(currentProcessCommand(), "java"); |
988 | S cmd = platformQuoteArgs(flattenToList( |
989 | javaCmd, "-jar", jar, args)); |
990 | print(cmd); |
991 | nohup(cmd); |
992 | cleanExit(); |
993 | } |
994 | |
995 | DynModule makeModule() { |
996 | ret callF(moduleMaker); |
997 | } |
998 | |
999 | void reloadModuleInBackground(Stem mod) { |
1000 | restart(); |
1001 | } |
1002 | |
1003 | void initAutoUpdate { |
1004 | print("Making server connection for updates."); |
1005 | serverConnector = snippetUpdateConnector(/*verbose := true*/); |
1006 | //serverConnector.verbose = true; |
1007 | |
1008 | vmBus_onMessage snippetUpdate(voidfunc(L l) { |
1009 | S uri = getString(l, 1); |
1010 | new Matches m; |
1011 | if (swic(uri, "/transpileOK/", m)) { |
1012 | if (sameSnippetID(programID(), firstIntAsString(m.rest()))) { |
1013 | if (g22utils.autoUpdateEnabled() /*&& autoUpdateDisabled*/) { |
1014 | upgradeGazelleForeplay(); |
1015 | } |
1016 | //infoBox("Gazelle can be updated!"); |
1017 | } |
1018 | } else if (swic(uri, "/edit/", m)) { |
1019 | S snippetID = fsI(firstIntAsString(m.rest()); |
1020 | if (contains(stdFunctionListSnippetIDs(), snippetID)) |
1021 | stdFunctions_clearCache(); |
1022 | if (contains(standardClassesSnippetIDs(), snippetID)) |
1023 | standardClassesMap_clearCache(); |
1024 | } |
1025 | }); |
1026 | |
1027 | pcall { |
1028 | computerCount = new ComputerCountListener(serverConnector); |
1029 | computerCount.init(); |
1030 | } |
1031 | |
1032 | pcall { |
1033 | gazellePresenceListener = GazellePresenceListener(serverConnector); |
1034 | gazellePresenceListener.init(); |
1035 | } |
1036 | } |
1037 | |
1038 | S downloadURL() { |
1039 | ret "https://botcompany.de/jar/" |
1040 | + psI(programID()) + "?withLibs=1&withX30=1" |
1041 | + "&mainClassForManifest=Starter" |
1042 | + (!downloadJarWithSrc() ? "&noSrc=1" : "") |
1043 | + (downloadUncompressedJAR() ? "&uncompressed=1" : ""); |
1044 | } |
1045 | |
1046 | bool downloadJarWithSrc() { true; } |
1047 | bool downloadUncompressedJAR() { false; } |
1048 | |
1049 | // called by DynModule |
1050 | void revisualizeModule(Stem stem) { |
1051 | stem.revisualize(); |
1052 | } |
1053 | |
1054 | Int computerCount() { ret computerCount == null ? (Int) 0 : computerCount!; } |
1055 | LiveValue<Int> lvComputerCount() { ret computerCount?.liveValue(); } |
1056 | |
1057 | public IGetterWithNotify<Int> lvGazelleUserCount() { ret gazellePresenceListener?.userCount; } |
1058 | |
1059 | L<JButton> compileButtons() { ret mapNonNulls(allStems(), s -> s.compileButton()); } |
1060 | |
1061 | L<Stem> allStems() { |
1062 | ret concatLists(cloneMap(loadedDBs, db -> list(db.concepts, Stem))); |
1063 | } |
1064 | |
1065 | // compile main program |
1066 | void compile { |
1067 | compile(programID, null); |
1068 | } |
1069 | |
1070 | void resetNeedLatest { |
1071 | editSnippet(#1035306, "need latest ."); |
1072 | } |
1073 | |
1074 | void compileAll { |
1075 | resetNeedLatest(); |
1076 | compile(utilsSnippetID(), l0 compile); |
1077 | } |
1078 | |
1079 | void compileUtilsAndRepackage { |
1080 | compile(utilsSnippetID(), l0 repackage); |
1081 | } |
1082 | |
1083 | void compile(S snippetID, Runnable onSuccess) { |
1084 | print("Compiling " + snippetID); |
1085 | |
1086 | var gears = resizeImageIcon(imageIcon(#1103061), 16); |
1087 | |
1088 | temp tempDisableCompileButtons(); |
1089 | temp tempSetButtonImages(compileButtons(), gears); |
1090 | |
1091 | thread { existingFilesInfo(); } // precompute |
1092 | |
1093 | var tr = TranspileWithErrorWindow().snippetID(snippetID); |
1094 | /*tr.transpileAction(-> |
1095 | new TranspileForServer_v2(snippetID).run());*/ |
1096 | tr.transpileAction(-> { |
1097 | var p = transpileOnNewServer(snippetID, "medium"); |
1098 | if (!p.a) fail(p.b); |
1099 | }); |
1100 | tr.onSuccess(onSuccess); |
1101 | tr.run(); |
1102 | |
1103 | print("Done transpilation of " + snippetID); |
1104 | } |
1105 | |
1106 | void repackage { |
1107 | File f = javaxCachesDir("Gazelle-22/repackaged.jar"); |
1108 | deleteFile(f); |
1109 | loadBinaryPageToFile(appendParamsToURL(repackageURL, uniq := randomID()), f); |
1110 | if (isJAR(f)) |
1111 | infoBox("Repackaged."); |
1112 | else |
1113 | messageBox("Oh shit"); |
1114 | } |
1115 | |
1116 | public void closeDatabase(LoadedDB db) { |
1117 | db.close(); |
1118 | exitIfAllDBsClosed(); |
1119 | } |
1120 | |
1121 | void exitIfAllDBsClosed { |
1122 | if (!autoExitDisabled && empty(loadedDBs)) |
1123 | cleanExit(); |
1124 | } |
1125 | |
1126 | Cl<? extends IG22LoadedDB> getLoadedDBs aka loadedDBs() { |
1127 | ret cloneList(loadedDBs); |
1128 | } |
1129 | |
1130 | public void closeDatabase(File conceptsDir) { |
1131 | for (db : cloneList(loadedDBs)) |
1132 | if (sameFile(conceptsDir, conceptsDir(db.concepts))) |
1133 | db.close(); |
1134 | } |
1135 | |
1136 | public void switchToDatabase(File conceptsDir) { |
1137 | if (conceptsDir == null) ret; |
1138 | // open/activate the desired database first |
1139 | var db = openDatabase(conceptsDir); |
1140 | |
1141 | if (db == null) ret; // probably impossible but whatever |
1142 | |
1143 | closeAllDBsExcept(db); |
1144 | } |
1145 | |
1146 | void closeAllDBsExcept(LoadedDB db) { |
1147 | closeAll(listMinus(loadedDBs, db)); |
1148 | } |
1149 | |
1150 | public LoadedDB openDatabase(File conceptsDir, bool hidden default false) { |
1151 | temp tempPrintAlsoToSystemOut(); |
1152 | |
1153 | File conceptsFile = newFile(conceptsDir, conceptsFileName()); |
1154 | if (!fileExists(conceptsFile)) |
1155 | fail(infoBox("Database not found: " + conceptsFile)); |
1156 | |
1157 | var db = openDB(conceptsFile, hidden); |
1158 | |
1159 | print("Database opening complete"); |
1160 | ret db; |
1161 | } |
1162 | |
1163 | Map<S, Class> classMigrations() { |
1164 | ret litmap( |
1165 | G22RecognizerScript := G22Analyzer, |
1166 | GalleryImage := G22GalleryImage); |
1167 | } |
1168 | |
1169 | public IF1<S, Class> makeClassFinder() { |
1170 | IF1<S, Class> defaultClassFinder = toIF1(_defaultClassFinder()); |
1171 | var migrations = classMigrations(); |
1172 | IF1<S, Class> classFinder = name -> { |
1173 | // class migration |
1174 | S shortName = dropPrefixOrNull("main$", name); |
1175 | if (shortName != null) { |
1176 | Class c = migrations.get(shortName); |
1177 | if (c != null) { |
1178 | S newName = className(c); |
1179 | print("Migrating legacy class " + name + " to " + newName); |
1180 | name = newName; |
1181 | } |
1182 | } |
1183 | |
1184 | ret defaultClassFinder.get(name); |
1185 | }; |
1186 | |
1187 | // Test classFinder sanity |
1188 | |
1189 | assertEquals(callF(classFinder, "main$Stem"), Stem.class); |
1190 | |
1191 | ret classFinder; |
1192 | } |
1193 | |
1194 | transient cached S existingFilesInfo() { |
1195 | L<Map> fingerprints = zipFileToJSONFingerprint_md5(myJar()); |
1196 | S s = base64encode(gzipString(jsonEncode(fingerprints))); |
1197 | print(existingFilesInfo := nBytes(l(s))); |
1198 | ret s; |
1199 | } |
1200 | |
1201 | bool devMode() { ret g22_devMode(); } |
1202 | |
1203 | // remember ephemeral object, return script code to get it back |
1204 | S ephString(O o) { |
1205 | ret o == null ? "" : "eph " + ephemeralObjectIDs.remember(o); |
1206 | } |
1207 | |
1208 | S classNameAndEphString(O o) { |
1209 | ret o == null ?: spaceCombine(ephString(o), className(o)); |
1210 | } |
1211 | |
1212 | public File databasesMotherDir() { |
1213 | if (dbMotherDir == null) |
1214 | dbMotherDir = javaxDataDir("Gazelle-22"); |
1215 | ret dbMotherDir; |
1216 | } |
1217 | } // end of GazelleHost [G22MasterStuff] |
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: | 735 / 3594 |
Version history: | 365 change(s) |
Referenced in: | [show references] |