11package io.github.footermandev.tritium.core.project
22
33import io.github.footermandev.tritium.TConstants
4- import io.github.footermandev.tritium.core.project.settings.ProjectScopedSettingsMngr
54import io.github.footermandev.tritium.core.project.templates.MigrationRegistry
65import io.github.footermandev.tritium.core.project.templates.ProjectFileLoader
76import io.github.footermandev.tritium.core.project.templates.TemplateDescriptor
87import io.github.footermandev.tritium.core.project.templates.TemplateRegistry
9- import io.github.footermandev.tritium.extension.core.BuiltinRegistries
108import io.github.footermandev.tritium.extension.core.CoreSettingValues
119import io.github.footermandev.tritium.fromTR
1210import io.github.footermandev.tritium.io.VPath
1311import io.github.footermandev.tritium.logger
1412import io.github.footermandev.tritium.ui.dashboard.Dashboard
1513import io.github.footermandev.tritium.ui.project.ProjectWindows
1614import io.github.footermandev.tritium.ui.theme.TIcons
15+ import io.qt.widgets.QApplication
16+ import io.qt.widgets.QMessageBox
1717import kotlinx.serialization.KSerializer
1818import kotlinx.serialization.Serializable
1919import kotlinx.serialization.SerializationException
@@ -91,39 +91,6 @@ object ProjectMngr {
9191 listeners.forEach { it.onProjectsFinishedLoading(snapshot) }
9292 }
9393
94- private fun resolveProjectTypeForSettings (typeId : String ): ProjectType ? {
95- BuiltinRegistries .ProjectType .get(typeId)?.let { return it }
96- val localId = typeId.substringAfterLast(' :' , missingDelimiterValue = typeId)
97- if (localId != typeId) {
98- BuiltinRegistries .ProjectType .get(localId)?.let { resolved ->
99- logger.debug(
100- " Resolved project type '{}' to local id '{}' for project-scoped settings" ,
101- typeId,
102- localId
103- )
104- return resolved
105- }
106- }
107- return null
108- }
109-
110- private fun ensureProjectScopedSettings (project : ProjectBase ) {
111- try {
112- val type = resolveProjectTypeForSettings(project.typeId)
113- if (type == null ) {
114- logger.debug(
115- " Skipping project-scoped settings initialization for {} (unknown project type '{}')" ,
116- project.path,
117- project.typeId
118- )
119- return
120- }
121- ProjectScopedSettingsMngr .ensureProjectFiles(project, type.projectSettings)
122- } catch (t: Throwable ) {
123- logger.warn(" Failed to initialize project-scoped settings for {}" , project.path, t)
124- }
125- }
126-
12794 private fun loadProjectFromDir (dir : VPath ): ProjectBase ? {
12895 val trMeta = ProjectFiles .readTrProject(dir) ? : run {
12996 logger.warn(" No trproj.json found in {}" , dir)
@@ -139,14 +106,10 @@ object ProjectMngr {
139106 val descriptor = TemplateRegistry .get(typeId)
140107 if (descriptor is ProjectFileLoader ) {
141108 return try {
142- descriptor.loadFromProjectFile(trMeta, dir).also { project ->
143- ensureProjectScopedSettings(project)
144- }
109+ descriptor.loadFromProjectFile(trMeta, dir)
145110 } catch (e: Exception ) {
146111 logger.error(" Failed to load project via ProjectFileLoader for type=$typeId in $dir " , e)
147- ProjectBase (typeId, dir, name, icon, metaElem.jsonObjectOrEmpty()).also { project ->
148- ensureProjectScopedSettings(project)
149- }
112+ ProjectBase (typeId, dir, name, icon, metaElem.jsonObjectOrEmpty())
150113 }
151114 }
152115
@@ -165,21 +128,15 @@ object ProjectMngr {
165128 val typed = json.decodeFromJsonElement(serializer, migratedMeta)
166129 @Suppress(" UNCHECKED_CAST" )
167130 val typedDescriptor = descriptor as TemplateDescriptor <Any >
168- return typedDescriptor.createProjectFromMeta(typed, descriptor.currentSchema, dir).also { project ->
169- ensureProjectScopedSettings(project)
170- }
131+ return typedDescriptor.createProjectFromMeta(typed, descriptor.currentSchema, dir)
171132 } catch (e: Exception ) {
172133 logger.error(" Failed to decode meta for type=$typeId in $dir " , e)
173- return ProjectBase (typeId, dir, name, icon, metaElem.jsonObjectOrEmpty()).also { project ->
174- ensureProjectScopedSettings(project)
175- }
134+ return ProjectBase (typeId, dir, name, icon, metaElem.jsonObjectOrEmpty())
176135 }
177136 }
178137
179138 logger.warn(" Unknown project type: $typeId (directory $dir )" )
180- return ProjectBase (typeId, dir, name, icon, metaElem.jsonObjectOrEmpty()).also { project ->
181- ensureProjectScopedSettings(project)
182- }
139+ return ProjectBase (typeId, dir, name, icon, metaElem.jsonObjectOrEmpty())
183140 }
184141
185142 /* *
@@ -507,12 +464,18 @@ object ProjectMngr {
507464 fun openProject (project : ProjectBase ) {
508465 logger.info(" Loading project {}" , project.name)
509466 addProjectToCatalog(project.projectDir, project.name)
510- val wasDifferent = activeProject != = project
467+ val previousActive = activeProject
468+ val openMode = resolveOpenMode(project) ? : return
469+ val wasDifferent = previousActive != = project
511470 activeProject = project
512471 val closeDashboard = CoreSettingValues .closeDashboardOnProjectOpen() && wasDifferent
513472
514473 try {
515- ProjectWindows .openProject(project, closeDashboard = closeDashboard)
474+ ProjectWindows .openProject(
475+ project = project,
476+ closeDashboard = closeDashboard,
477+ mode = openMode
478+ )
516479 } catch (e: Exception ) {
517480 logger.debug(" Failed to open project" , e)
518481 }
@@ -567,6 +530,39 @@ object ProjectMngr {
567530 return getProjectsFromCatalog(source)
568531 }
569532
533+ private fun resolveOpenMode (project : ProjectBase ): ProjectWindows .OpenMode ? {
534+ val existing = ProjectWindows .anyOpenWindow() ? : return ProjectWindows .OpenMode .NEW_WINDOW
535+ val targetCanonical = project.path.toString().trim()
536+ if (existing.projectCanonicalPath() == targetCanonical) {
537+ return ProjectWindows .OpenMode .NEW_WINDOW
538+ }
539+
540+ return when (CoreSettingValues .projectOpenPromptMode()) {
541+ CoreSettingValues .ProjectOpenPromptMode .Always -> promptOpenMode(project)
542+ CoreSettingValues .ProjectOpenPromptMode .Never -> when (CoreSettingValues .projectOpenDefaultTarget()) {
543+ CoreSettingValues .ProjectOpenDefaultTarget .Current -> ProjectWindows .OpenMode .CURRENT_WINDOW
544+ CoreSettingValues .ProjectOpenDefaultTarget .New -> ProjectWindows .OpenMode .NEW_WINDOW
545+ }
546+ }
547+ }
548+
549+ private fun promptOpenMode (project : ProjectBase ): ProjectWindows .OpenMode ? {
550+ val parent = QApplication .activeWindow() ? : Dashboard .I
551+ val box = QMessageBox (parent)
552+ box.icon = QMessageBox .Icon .Question
553+ box.windowTitle = " Open Project"
554+ box.text = " Open '${project.name} ' in current window or a new window?"
555+ val currentButton = box.addButton(" Current Window" , QMessageBox .ButtonRole .AcceptRole )
556+ val newButton = box.addButton(" New Window" , QMessageBox .ButtonRole .ActionRole )
557+ box.addButton(QMessageBox .StandardButton .Cancel )
558+ box.exec()
559+ return when (box.clickedButton()) {
560+ currentButton -> ProjectWindows .OpenMode .CURRENT_WINDOW
561+ newButton -> ProjectWindows .OpenMode .NEW_WINDOW
562+ else -> null
563+ }
564+ }
565+
570566 private fun isDashboardActive (): Boolean {
571567 val dash = Dashboard .I ? : return false
572568 return dash.isVisible
@@ -581,7 +577,6 @@ object ProjectMngr {
581577 * Notify listeners that a project was created outside the manager.
582578 */
583579 fun notifyCreatedExternal (project : ProjectBase ) {
584- ensureProjectScopedSettings(project)
585580 addProjectToCatalog(project.projectDir, project.name)
586581 synchronized(_projectsLock ) {
587582 val existing = _projects .any { it.projectDir.toAbsolute() == project.projectDir.toAbsolute() }
0 commit comments