diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 3002c722..fb00b155 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -10,7 +10,8 @@
],
"options": {
"cwd": "${workspaceFolder}"
- }
+ },
+ "problemMatcher": []
},
{
"label": "build-launchpad",
diff --git a/Launchpad.ahk b/Launchpad.ahk
index 00552dc8..d34cd5e1 100644
--- a/Launchpad.ahk
+++ b/Launchpad.ahk
@@ -13,8 +13,7 @@ appVersion := "{{VERSION}}"
Launchpad(Map(
"appName", "Launchpad",
- "developer", "Volantis Development",
"version", appVersion,
- "trayIcon", "Resources\Graphics\Launchpad.ico",
+ "trayIcon", "Resources\Graphics\launchpad.ico",
"console", true
))
diff --git a/Launchpad.services.json b/Launchpad.services.json
index 6a725f60..7df3dbb0 100644
--- a/Launchpad.services.json
+++ b/Launchpad.services.json
@@ -1,9 +1,14 @@
{
"parameters": {
+ "app.website_url": "https://launchpad.games",
+ "app.custom_tray_menu": true,
+ "app.developer": "Volantis Development",
+ "app.has_settings": true,
+ "app.settings_window": "SettingsWindow",
+ "app.show_about_menu_item": true,
+ "app.about_window": "AboutWindow",
+ "app.show_website_menu_item": true,
"backups_config": {},
- "config.api_authentication": false,
- "config.api_auto_login": false,
- "config.api_endpoint": "",
"config.assets_dir": "@@{data_dir}\\Launcher Assets",
"config.auto_backup_config_files": true,
"config.backups_to_keep": 5,
@@ -12,7 +17,6 @@
"config.clean_launchers_on_build": false,
"config.clean_launchers_on_exit": true,
"config.create_desktop_shortcuts": true,
- "config.data_source_key": "local",
"config.default_launcher_theme": "",
"config.destination_dir": "@@{data_dir}\\Launchers",
"config.launcher_double_click_action": "Edit",
@@ -22,7 +26,6 @@
"config.platforms_file": "@@{data_dir}\\Platforms.json",
"config.platforms_view_mode": "Report",
"config.tasks_view_mode": "Report",
- "config.player_name": "",
"config.rebuild_existing_launchers": false,
"config.tasks_file": "@@{data_dir}\\Tasks.json",
"config.use_advanced_launcher_editor": false,
@@ -33,27 +36,29 @@
"storage_config_storage_parent_key": "Games",
"storage_config_path_parameter": "config.launcher_file",
"manager_view_mode_parameter": "config.launcher_view_mode",
- "default_icon": "Game",
+ "default_icon": "game",
"allow_add": true,
"allow_edit": true,
"allow_delete": true,
"manager_gui": "MainWindow"
},
- "entity_type.managed_game": {
+ "entity_type.game_process": {
"name_singular": "Managed Game",
"name_plural": "Managed Games",
- "entity_class": "ManagedGameEntity",
+ "entity_class": "GameProcessEntity",
"storage_config_storage_parent_key": "Games",
"storage_config_path_parameter": "config.launcher_file",
- "parent_entity_type": "launcher"
+ "parent_entity_type": "launcher",
+ "parent_entity_storage": true
},
- "entity_type.managed_launcher": {
+ "entity_type.launcher_process": {
"name_singular": "Managed Launcher",
"name_plural": "Managed Launchers",
- "entity_class": "ManagedLauncherEntity",
+ "entity_class": "LauncherProcessEntity",
"storage_config_storage_parent_key": "Games",
"storage_config_path_parameter": "config.launcher_file",
- "parent_entity_type": "launcher"
+ "parent_entity_type": "launcher",
+ "parent_entity_storage": true
},
"entity_type.platform": {
"name_singular": "Platform",
@@ -65,8 +70,8 @@
"storage_config_storage_parent_key": "Platforms",
"storage_config_path_parameter": "config.platforms_file",
"manager_view_mode_parameter": "config.platforms_view_mode",
- "default_icon": "Platform",
- "manager_gui": "ManagePlatformsWindow",
+ "default_icon": "platform",
+ "manager_gui": "PlatformsWindow",
"manager_link_in_tools_menu": true
},
"entity_type.task": {
@@ -112,10 +117,6 @@
"class": "LaunchpadConfig",
"arguments": ["@config_storage.app_config", "@{}", "@@config_key"]
},
- "data_source.local": {
- "class": "LocalDataSource",
- "arguments": ["@manager.cache", "local"]
- },
"ini_migrator": {
"class": "LaunchpadIniMigrator",
"arguments": ["@{App}", "@manager.gui"]
@@ -132,10 +133,6 @@
"class": "BuilderManager",
"arguments": ["@entity_manager.launcher", "@{}", "@manager.event", "@notifier"]
},
- "manager.data_source": {
- "class": "DataSourceManager",
- "arguments": ["@{}", "@manager.event", "@notifier", "@@config.data_source_key"]
- },
"state.app": {
"class": "LaunchpadAppState",
"arguments": ["@{App}", "@@state_path"]
diff --git a/LaunchpadOverlay/LaunchpadOverlay.rc b/LaunchpadOverlay/LaunchpadOverlay.rc
index a2535bad..2e1c9f22 100644
--- a/LaunchpadOverlay/LaunchpadOverlay.rc
+++ b/LaunchpadOverlay/LaunchpadOverlay.rc
@@ -52,7 +52,7 @@ END
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
-IDI_ICON1 ICON "E:\\Tools\\Launchpad\\Resources\\Graphics\\Launchpad.ico"
+IDI_ICON1 ICON "E:\\Tools\\Launchpad\\Resources\\Graphics\\launchpad.ico"
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
diff --git a/LaunchpadTest.ahk b/LaunchpadTest.ahk
index 304cb70f..c9720435 100644
--- a/LaunchpadTest.ahk
+++ b/LaunchpadTest.ahk
@@ -13,7 +13,7 @@
appVersion := "1.0.0"
-TraySetIcon("Resources\Graphics\Launchpad.ico")
+TraySetIcon("Resources\Graphics\launchpad.ico")
HtmlResultViewer("Launchpad Test")
.ViewResults(SimpleTestRunner(FilesystemTestLoader().GetTests()).RunTests())
diff --git a/Lib/Launchpad/App/Launchpad.ahk b/Lib/Launchpad/App/Launchpad.ahk
index 73eb3320..8ab4a97d 100644
--- a/Lib/Launchpad/App/Launchpad.ahk
+++ b/Lib/Launchpad/App/Launchpad.ahk
@@ -1,30 +1,5 @@
class Launchpad extends AppBase {
- customTrayMenu := true
detectGames := false
- isSetup := false
-
- CheckForUpdates(notify := true) {
- updateAvailable := false
-
- if (this.Version != "{{VERSION}}" && this.Service("manager.data_source").GetDefaultDataSource()) {
- dataSource := this.Service("manager.data_source").GetDefaultDataSource()
- releaseInfoStr := dataSource.ReadItem("release-info")
-
- if (releaseInfoStr) {
- data := JsonData()
- releaseInfo := data.FromString(&releaseInfoStr)
-
- if (releaseInfo && releaseInfo["data"].Has("version") && releaseInfo["data"]["version"] && this.Service("version_checker").VersionIsOutdated(releaseInfo["data"]["version"], this.Version)) {
- updateAvailable := true
- this.Service("manager.gui").Dialog(Map("type", "UpdateAvailableWindow"), releaseInfo)
- }
- }
- }
-
- if (!updateAvailable && notify) {
- this.Service("notifier").Info("You're running the latest version of Launchpad. Shiny!")
- }
- }
UpdateIncludes() {
this.RunAhkScript(this.appDir . "\Scripts\UpdateIncludes.ahk")
@@ -32,7 +7,7 @@
}
InitializeApp(config) {
- eventMgr := this.Service("manager.event")
+ eventMgr := this["manager.event"]
eventMgr.Register(EntityEvents.ENTITY_CREATED, "LaunchpadEntityCreated", ObjBindMethod(this, "OnEntityCreated"))
eventMgr.Register(EntityEvents.ENTITY_UPDATED, "LaunchpadEntityUpdated", ObjBindMethod(this, "OnEntityUpdated"))
eventMgr.Register(EntityEvents.ENTITY_DELETED, "LaunchpadEntityDeleted", ObjBindMethod(this, "OnEntityDeleted"))
@@ -109,35 +84,31 @@
RunApp(config) {
this.MigrateConfiguration()
-
- if (this.Config["api_auto_login"] && this.Services.Has("Auth")) {
- this.Service("Auth").Login()
- }
super.RunApp(config)
- this.Service("entity_manager.platform").LoadComponents()
- this.Service("entity_manager.launcher").LoadComponents()
- this.Service("entity_manager.backup").LoadComponents()
+ this["entity_manager.platform"].LoadComponents()
+ this["entity_manager.launcher"].LoadComponents()
+ this["entity_manager.backup"].LoadComponents()
this.OpenApp()
if (this.detectGames) {
- this.Service("entity_manager.platform").DetectGames()
+ this["entity_manager.platform"].DetectGames()
}
}
MigrateConfiguration() {
- configFile := this.Parameter("previous_config_file")
+ configFile := this.Parameter["previous_config_file"]
if (configFile && FileExist(configFile)) {
- response := this.Service("manager.gui").Dialog(Map(
+ response := this["manager.gui"].Dialog(Map(
"title", "Migrate settings?",
"text", this.appName . " uses a new configuration file format, and has detected that you have a previous configuration file.`n`nWould you like to automatically migrate your settings?`n`nChoose Yes to migrate your previous configuration. Choose no to simply delete it and start from scratch."
))
if (response == "Yes") {
- this.Service("ini_migrator").Migrate(configFile, this.Config)
+ this["ini_migrator"].Migrate(configFile, this.Config)
} else {
FileDelete(configFile)
}
@@ -145,7 +116,7 @@
}
InitialSetup(config) {
- result := this.Service("manager.gui").Dialog(Map("type", "SetupWindow"))
+ result := this["manager.gui"].Dialog(Map("type", "SetupWindow"))
if (result == "Exit") {
this.ExitApp()
@@ -153,45 +124,87 @@
this.detectGames := true
}
- this.isSetup := true
super.InitialSetup(config)
}
UpdateStatusIndicators() {
- if (this.Service("manager.gui").Has("MainWindow")) {
- this.Service("manager.gui")["MainWindow"].UpdateStatusIndicator()
+ if (this["manager.gui"].Has("MainWindow")) {
+ serviceMgr := this.container["entity_manager.web_service"]
+ webServices := serviceMgr.EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES)
+ .Condition(IsTrueCondition(), "Enabled")
+ .Condition(IsTrueCondition(), "StatusIndicator")
+ .Execute()
+
+ windowObj := this["manager.gui"]["MainWindow"]
+
+ for serviceId, webService in webServices {
+ windowObj.UpdateStatusIndicator(webService)
+ }
}
}
ExitApp() {
if (this.isSetup && this.Config["clean_launchers_on_exit"]) {
- this.Service("manager.builder").CleanLaunchers()
+ this["manager.builder"].CleanLaunchers()
}
if (this.isSetup && this.Config["flush_cache_on_exit"]) {
- this.Service("manager.cache").FlushCaches(false, false)
+ this["manager.cache"].FlushCaches(false, false)
}
super.ExitApp()
}
- OpenWebsite() {
- Run("https://launchpad.games")
+ RestartApp() {
+ if (this.Services.Has("manager.gui")) {
+ guiMgr := this["manager.gui"]
+
+ if (guiMgr.Has("MainWindow")) {
+ guiMgr.StoreWindowState(this["manager.gui"]["MainWindow"])
+ }
+ }
+
+ super.RestartApp()
}
- ProvideFeedback() {
- this.Service("manager.gui").Dialog(Map("type", "FeedbackWindow"))
+ AddMainMenuEarlyItems(menuItems, showOpenItem := false) {
+ menuItems := super.AddMainMenuEarlyItems(menuItems, showOpenItem)
+
+ launchersItems := []
+ launchersItems.Push(Map("label", "&Clean Launchers", "name", "CleanLaunchers"))
+ launchersItems.Push(Map("label", "&Reload Launchers", "name", "ReloadLaunchers"))
+
+ menuItems.Push(Map("label", "&Launchers", "name", "LaunchersMenu", "childItems", launchersItems))
+
+ return menuItems
}
- RestartApp() {
- if (this.Services.Has("manager.gui")) {
- guiMgr := this.Service("manager.gui")
+ HandleMainMenuClick(result) {
+ if (result == "CleanLaunchers") {
+ this["manager.builder"].CleanLaunchers()
+ } else if (result == "ReloadLaunchers") {
+ this["entity_manager.launcher"].LoadComponents(true)
+ guiMgr := this["manager.gui"]
if (guiMgr.Has("MainWindow")) {
- guiMgr.StoreWindowState(this.Service("manager.gui")["MainWindow"])
+ guiMgr["MainWindow"].UpdateListView()
}
+ } else if (result == "ManageModules") {
+ this["manager.gui"].OpenWindow("ManageModulesWindow")
+ } else if (result == "FlushCache") {
+ this["manager.cache"].FlushCaches(true, true)
+ } else {
+ super.HandleMainMenuClick(result)
}
- super.RestartApp()
+ return result
+ }
+
+ GetToolsMenuItems() {
+ toolsItems := super.GetToolsMenuItems()
+ toolsItems.Push(Map("label", "&Modules", "name", "ManageModules"))
+ toolsItems.Push(Map("label", "&Flush Cache", "name", "FlushCache"))
+
+ return toolsItems
}
}
diff --git a/Lib/Launchpad/Builder/BuildFile/CopyableBuildFile.ahk b/Lib/Launchpad/Builder/BuildFile/CopyableBuildFile.ahk
index 2bd1146e..aaa77f98 100644
--- a/Lib/Launchpad/Builder/BuildFile/CopyableBuildFile.ahk
+++ b/Lib/Launchpad/Builder/BuildFile/CopyableBuildFile.ahk
@@ -56,7 +56,7 @@ class CopyableBuildFile extends BuildFileBase {
filePath := FileSelect(1,, this.launcherEntityObj.Id . ": " . this.RequestMessage, this.SelectFilter)
if (filePath == "") {
- this.app.Service("notifier").Warning("No file selected. Skipping build file.")
+ this.app["notifier"].Warning("No file selected. Skipping build file.")
}
return filePath
diff --git a/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk b/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk
index ed0eaedc..d1cbe8cd 100644
--- a/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk
+++ b/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk
@@ -14,9 +14,9 @@ class GameAhkFile extends ComposableBuildFile {
"launcherName", this.launcherEntityObj.Id . " - Launchpad",
"appVersion", appVersion,
"appDir", this.appDir,
- "gameConfig", this.launcherEntityObj["ManagedGame"].FieldData,
+ "gameConfig", this.launcherEntityObj["GameProcess"].FieldData,
"launchpadLauncherConfig", this.launcherEntityObj.FieldData,
- "launcherConfig", this.launcherEntityObj["ManagedLauncher"].FieldData,
+ "launcherConfig", this.launcherEntityObj["LauncherProcess"].FieldData,
"launcherId", this.launcherEntityObj.Id,
"themesDir", this.launcherEntityObj["ThemesDir"],
"resourcesDir", this.launcherEntityObj["ResourcesDir"],
@@ -35,7 +35,7 @@ class GameAhkFile extends ComposableBuildFile {
GetPlatforms() {
platforms := Map()
- for key, platform in this.app.Service("entity_manager.platform").GetActivePlatforms(EntityQuery.RESULT_TYPE_ENTITIES) {
+ for key, platform in this.app["entity_manager.platform"].GetActivePlatforms(EntityQuery.RESULT_TYPE_ENTITIES) {
platforms[key] := platform.FieldData
}
diff --git a/Lib/Launchpad/Builder/BuildFile/IconFile.ahk b/Lib/Launchpad/Builder/BuildFile/IconFile.ahk
index 333333a4..729d4c01 100644
--- a/Lib/Launchpad/Builder/BuildFile/IconFile.ahk
+++ b/Lib/Launchpad/Builder/BuildFile/IconFile.ahk
@@ -53,7 +53,7 @@ class IconFile extends CopyableBuildFile {
}
if (iconsCount == 0) {
- this.app.Service("notifier").Warning("No icons could be extracted from %exeFile%. Please try another file.")
+ this.app["notifier"].Warning("No icons could be extracted from %exeFile%. Please try another file.")
iconFilePath := ""
this.Cleanup()
} else {
@@ -61,7 +61,7 @@ class IconFile extends CopyableBuildFile {
iconFilePath := FileSelect(, iconsDir, "Select the correct icon from the extracted files", "Icons (*.ico)")
if (iconFilePath == "") {
- this.app.Service("notifier").Warning("Canceled icon selection. Please try again.")
+ this.app["notifier"].Warning("Canceled icon selection. Please try again.")
this.Cleanup()
}
}
diff --git a/Lib/Launchpad/Builder/BuildFile/ShortcutFile.ahk b/Lib/Launchpad/Builder/BuildFile/ShortcutFile.ahk
index 6f61ab18..d434a996 100644
--- a/Lib/Launchpad/Builder/BuildFile/ShortcutFile.ahk
+++ b/Lib/Launchpad/Builder/BuildFile/ShortcutFile.ahk
@@ -6,14 +6,14 @@ class ShortcutFile extends CopyableBuildFile {
if (destPath == "") {
ext := ".lnk"
- if (launcherEntityObj["ManagedGame"]["ShortcutSrc"] != "" && SubStr(launcherEntityObj["ManagedGame"]["ShortcutSrc"], -4) == ".url") {
+ if (launcherEntityObj["GameProcess"]["ShortcutSrc"] != "" && SubStr(launcherEntityObj["GameProcess"]["ShortcutSrc"], -4) == ".url") {
ext := ".url"
}
destPath := launcherEntityObj["AssetsDir"] . "\" . launcherEntityObj.Id . ext
}
- super.__New(launcherEntityObj, launcherEntityObj["ManagedGame"]["ShortcutSrc"], destPath)
+ super.__New(launcherEntityObj, launcherEntityObj["GameProcess"]["ShortcutSrc"], destPath)
}
Locate() {
diff --git a/Lib/Launchpad/Builder/BuilderBase.ahk b/Lib/Launchpad/Builder/BuilderBase.ahk
index b9609894..d4f77787 100644
--- a/Lib/Launchpad/Builder/BuilderBase.ahk
+++ b/Lib/Launchpad/Builder/BuilderBase.ahk
@@ -32,7 +32,7 @@ class BuilderBase {
}
NeedsShortcutFile(launcherEntityObj) {
- return (launcherEntityObj["ManagedGame"]["UsesShortcut"])
+ return (launcherEntityObj["GameProcess"]["UsesShortcut"])
}
BuildAction(launcherEntityObj, launcherDir, assetsDir) {
diff --git a/Lib/Launchpad/BulkOperation/LauncherBuilderOp/LauncherBuilderOpBase.ahk b/Lib/Launchpad/BulkOperation/LauncherBuilderOp/LauncherBuilderOpBase.ahk
index f1194602..f9f976b3 100644
--- a/Lib/Launchpad/BulkOperation/LauncherBuilderOp/LauncherBuilderOpBase.ahk
+++ b/Lib/Launchpad/BulkOperation/LauncherBuilderOp/LauncherBuilderOpBase.ahk
@@ -11,7 +11,7 @@ class LauncherBuilderOpBase extends LauncherGameOpBase {
}
if (Type(builder) == "String" && builder != "") {
- builder := app.Service("manager.builder")[builder]
+ builder := app["manager.builder"][builder]
}
InvalidParameterException.CheckTypes("LauncherBuilderOpBase", "builder", builder, "BuilderBase")
diff --git a/Lib/Launchpad/BulkOperation/LauncherGameOp/LauncherGameOpBase.ahk b/Lib/Launchpad/BulkOperation/LauncherGameOp/LauncherGameOpBase.ahk
index 558ee665..f9193b3d 100644
--- a/Lib/Launchpad/BulkOperation/LauncherGameOp/LauncherGameOpBase.ahk
+++ b/Lib/Launchpad/BulkOperation/LauncherGameOp/LauncherGameOpBase.ahk
@@ -14,7 +14,7 @@ class LauncherGameOpBase extends BulkOperationBase {
itemFailedText := "Failed."
__New(app, launcherEntities := "", owner := "") {
- this.launcherManager := app.Service("entity_manager.launcher")
+ this.launcherManager := app["entity_manager.launcher"]
if (launcherEntities == "") {
launcherEntities := this.launcherManager.All()
@@ -63,12 +63,12 @@ class LauncherGameOpBase extends BulkOperationBase {
VerifyRequirements() {
if (this.app.Config["destination_dir"] == "") {
- this.app.Service("notifier").Error("Launcher directory is not set.")
+ this.app["notifier"].Error("Launcher directory is not set.")
return false
}
if (this.app.Config["assets_dir"] == "") {
- this.app.Service("notifier").Error("Assets directory is not set.")
+ this.app["notifier"].Error("Assets directory is not set.")
return false
}
diff --git a/Lib/Launchpad/BulkOperation/LoadOp/LoadEntitiesOp.ahk b/Lib/Launchpad/BulkOperation/LoadOp/LoadEntitiesOp.ahk
index fab21b04..c51002ac 100644
--- a/Lib/Launchpad/BulkOperation/LoadOp/LoadEntitiesOp.ahk
+++ b/Lib/Launchpad/BulkOperation/LoadOp/LoadEntitiesOp.ahk
@@ -10,7 +10,7 @@ class LoadEntitiesOp extends BulkOperationBase {
failedMessage := "{n} launcher(s) could not be loaded due to errors."
__New(app, launcherConfigObj := "", owner := "") {
- this.launcherManager := app.Service("entity_manager.launcher")
+ this.launcherManager := app["entity_manager.launcher"]
if (launcherConfigObj == "") {
launcherConfigObj := this.launcherManager.GetConfig()
@@ -29,7 +29,7 @@ class LoadEntitiesOp extends BulkOperationBase {
}
; @todo replace this since EntityFactory is no longer used
- factory := this.app.Service("EntityFactory")
+ factory := this.app["EntityFactory"]
for key, config in this.launcherConfigObj {
this.StartItem(key, key)
diff --git a/Lib/Launchpad/DataSource/LocalDataSource.ahk b/Lib/Launchpad/DataSource/LocalDataSource.ahk
deleted file mode 100644
index 7ac01421..00000000
--- a/Lib/Launchpad/DataSource/LocalDataSource.ahk
+++ /dev/null
@@ -1,2 +0,0 @@
-class LocalDataSource extends DataSourceBase {
-}
diff --git a/Lib/Launchpad/DetectedGame/DetectedGame.ahk b/Lib/Launchpad/DetectedGame/DetectedGame.ahk
index 02851d7c..e92e87a7 100644
--- a/Lib/Launchpad/DetectedGame/DetectedGame.ahk
+++ b/Lib/Launchpad/DetectedGame/DetectedGame.ahk
@@ -8,7 +8,7 @@ class DetectedGame {
installDir := ""
launcherInstallDir := ""
exeName := ""
- launcherSpecificId := ""
+ platformRef := ""
possibleExeNames := []
keyMap := Map()
; @todo Move this to properties or config or allow it to be extended
@@ -17,16 +17,16 @@ class DetectedGame {
prioritySuffixes := ["-Win64-Shipping", "-Win32-Shipping"]
filterExes := []
- __New(key, platform, launcherType, gameType := "Default", installDir := "", exeName := "", launcherSpecificId := "", possibleExeNames := "") {
+ __New(key, platform, launcherType, gameType := "Default", installDir := "", exeName := "", platformRef := "", possibleExeNames := "", displayName := "") {
this.key := key
- this.displayName := key
+ this.displayName := displayName ? displayName : key
this.platform := platform
this.detectedKey := key
this.launcherType := launcherType
this.gameType := gameType
this.installDir := installDir
this.exeName := exeName
- this.launcherSpecificId := launcherSpecificId
+ this.platformRef := platformRef
if (possibleExeNames) {
if (Type(possibleExeNames) == "String") {
@@ -44,12 +44,10 @@ class DetectedGame {
if (
this.displayName != launcher["name"]
- || this.launcherType != launcher["ManagedLauncher"].EntityTypeId
- || this.gameType != launcher["ManagedGame"].EntityTypeId
- || this.installDir != launcher["ManagedGame"]["InstallDir"]
- || this.launcherInstallDir != launcher["ManagedLauncher"]["InstallDir"]
- || this.exeName != launcher["ManagedGame"]["Exe"]
- || this.launcherSpecificId != launcher["ManagedGame"]["LauncherSpecificId"]
+ || this.installDir != launcher["GameProcess"]["InstallDir"]
+ || this.launcherInstallDir != launcher["LauncherProcess"]["InstallDir"]
+ || this.exeName != launcher["GameProcess"]["Exe"]
+ || this.platformRef != launcher["GameProcess"]["PlatformRef"]
) {
hasChanges := true
}
@@ -70,40 +68,28 @@ class DetectedGame {
modified := true
}
- if (this.launcherType && launcher["ManagedLauncher"].EntityTypeId != this.launcherType) {
- launcher["ManagedLauncher"].EntityType := this.launcherType
- modified := true
+ if (this.launcherInstallDir && launcher["LauncherProcess"]["InstallDir"] != this.launcherInstallDir) {
+ launcher["LauncherProcess"]["InstallDir"] := this.launcherInstallDir
}
- if (this.gameType && launcher["ManagedGame"].EntityTypeId != this.gameType) {
- launcher["ManagedGame"].EntityType := this.gameType
+ if (this.installDir && launcher["GameProcess"]["InstallDir"] != this.installDir) {
+ launcher["GameProcess"]["InstallDir"] := this.installDir
modified := true
}
- if (this.launcherInstallDir && launcher["ManagedLauncher"]["InstallDir"] != this.launcherInstallDir) {
- launcher["ManagedLauncher"]["InstallDir"] := this.launcherInstallDir
- }
-
- if (this.installDir && launcher["ManagedGame"]["InstallDir"] != this.installDir) {
- launcher["ManagedGame"]["InstallDir"] := this.installDir
- modified := true
- }
-
- if (this.exeName && launcher["ManagedGame"]["Exe"] != this.exeName) {
- launcher["ManagedGame"]["Exe"] := this.exeName
+ if (this.exeName && launcher["GameProcess"]["Exe"] != this.exeName) {
+ launcher["GameProcess"]["Exe"] := this.exeName
modified := true
}
if (modified) {
- launcher.SaveModifiedData()
+ launcher.SaveEntity(true)
}
}
CreateLauncher(launcherManager) {
config := Map(
- "Platform", this.platform.key,
- "LauncherType", this.launcherType,
- "GameType", this.gameType
+ "Platform", this.platform.key
)
if (this.displayName && this.displayName != this.key) {
@@ -122,8 +108,8 @@ class DetectedGame {
config["GameExe"] := this.exeName
}
- if (this.launcherSpecificId) {
- config["GameLauncherSpecificId"] := this.launcherSpecificId
+ if (this.platformRef) {
+ config["GamePlatformRef"] := this.platformRef
}
entityObj := launcherManager.GetFactory().CreateEntity(this.key, config)
@@ -153,15 +139,19 @@ class DetectedGame {
}
}
- key := StrReplace(key, ": ", " - ")
- key := StrReplace(key, ":", "")
- key := StrReplace(key, "\", "")
- key := StrReplace(key, "/", "")
- key := StrReplace(key, "*", "")
- key := StrReplace(key, "?", "")
- key := StrReplace(key, "`"", "")
- key := StrReplace(key, "®", "")
- key := StrReplace(key, "â„¢", "")
+ replacements := [
+ [" : ", " - "],
+ [": ", " - "],
+ [":", "-"],
+ ["®", ""],
+ ["â„¢", ""]
+ ]
+
+ for , vals in replacements {
+ key := StrReplace(key, vals[1], vals[2])
+ }
+
+ key := RegExReplace(key, "[\\/:*?`"<>|]'")
return key
}
diff --git a/Lib/Launchpad/Entity/ManagedGameEntity.ahk b/Lib/Launchpad/Entity/GameProcessEntity.ahk
similarity index 70%
rename from Lib/Launchpad/Entity/ManagedGameEntity.ahk
rename to Lib/Launchpad/Entity/GameProcessEntity.ahk
index 907991e6..da93710b 100644
--- a/Lib/Launchpad/Entity/ManagedGameEntity.ahk
+++ b/Lib/Launchpad/Entity/GameProcessEntity.ahk
@@ -1,8 +1,5 @@
-class ManagedGameEntity extends ManagedEntityBase {
- configPrefix := "Game"
- defaultType := "Default"
+class GameProcessEntity extends LaunchProcessEntity {
defaultClass := "SimpleGame"
- dataSourcePath := "game-types"
BaseFieldDefinitions() {
definitions := super.BaseFieldDefinitions()
@@ -10,7 +7,7 @@ class ManagedGameEntity extends ManagedEntityBase {
definitions["HasLoadingWindow"] := Map(
"type", "boolean",
"description", "Whether or not the game has a loading window to watch for.",
- "storageKey", this.configPrefix . "HasLoadingWindow",
+ "storageKey", "HasLoadingWindow",
"default", false
)
@@ -20,7 +17,7 @@ class ManagedGameEntity extends ManagedEntityBase {
definitions["LoadingWindowProcessType"] := Map(
"description", "Which method to use to wait for the game's loading window to open.",
"help", "This lets Launchpad know when the game is loading. Only used if a LoadingWindowProcessId is set",
- "storageKey", this.configPrefix . "LoadingWindowProcessType",
+ "storageKey", "LoadingWindowProcessType",
"default", "Exe",
"widget", "select",
"selectOptionsCallback", ObjBindMethod(this, "ListProcessTypes")
@@ -30,7 +27,7 @@ class ManagedGameEntity extends ManagedEntityBase {
; - Title - This value will default to the game's Key unless overridden
; - Class - This value should be set to the game's window class
definitions["LoadingWindowProcessId"] := Map(
- "storageKey", this.configPrefix . "LoadingWindowProcessId",
+ "storageKey", "LoadingWindowProcessId",
"help", "This value's type is dependent on the GameProcessType above. It can often be detected from other values, and is not needed if the GameRunType is RunWait.",
"modes", Map(
"simple", Map("formField", false)
@@ -40,16 +37,6 @@ class ManagedGameEntity extends ManagedEntityBase {
return definitions
}
- GetBlizzardProductKey() {
- productKey := this["LauncherSpecificId"]
-
- if (this.HasConfigValue("BlizzardProductId", true, false)) {
- productKey := this.GetConfigValue("BlizzardProductId")
- }
-
- return productKey
- }
-
ShouldDetectShortcutSrc(extraConfig) {
detectShortcut := false
@@ -78,28 +65,28 @@ class ManagedGameEntity extends ManagedEntityBase {
return detectShortcut
}
- AutoDetectValues(recurse := true) {
- detectedValues := super.AutoDetectValues(recurse)
- exeKey := this.configPrefix . "Exe"
+ AutoDetectValues() {
+ detectedValues := super.AutoDetectValues()
+ exeKey := "Exe"
if (!detectedValues.Has(exeKey)) {
detectedValues[exeKey] := this["Exe"] ? this["Exe"] : this.Id . ".exe"
}
- if (!detectedValues.Has(this.configPrefix . "ProcessId") || !detectedValues[this.configPrefix . "ProcessId"]) {
- detectedValues[this.configPrefix . "ProcessId"] := detectedValues[exeKey]
+ if (!detectedValues.Has("ProcessId") || !detectedValues["ProcessId"]) {
+ detectedValues["ProcessId"] := detectedValues[exeKey]
}
- if (detectedValues.Has(this.configPrefix . "ProcessType")) {
- detectedValues[this.configPrefix . "LoadingWindowProcessType"] := detectedValues[this.configPrefix . "ProcessType"]
+ if (detectedValues.Has("ProcessType")) {
+ detectedValues["LoadingWindowProcessType"] := detectedValues["ProcessType"]
}
if (!this["LoadingWindowProcessId"]) {
- detectedValues[this.configPrefix . "LoadingWindowProcessId"] := detectedValues[exeKey]
+ detectedValues["LoadingWindowProcessId"] := detectedValues[exeKey]
}
if (this.ShouldDetectShortcutSrc(detectedValues)) {
- basePath := this["AssetsDir"] . "\" . this.Id
+ basePath := this.ParentEntity["AssetsDir"] . "\" . this.Id
shortcutSrc := ""
if (FileExist(basePath . ".lnk")) {
@@ -111,7 +98,7 @@ class ManagedGameEntity extends ManagedEntityBase {
}
if (shortcutSrc != "") {
- detectedValues[this.configPrefix . "ShortcutSrc"] := shortcutSrc
+ detectedValues["ShortcutSrc"] := shortcutSrc
}
}
diff --git a/Lib/Launchpad/Entity/ManagedEntityBase.ahk b/Lib/Launchpad/Entity/LaunchProcessEntity.ahk
similarity index 76%
rename from Lib/Launchpad/Entity/ManagedEntityBase.ahk
rename to Lib/Launchpad/Entity/LaunchProcessEntity.ahk
index 976c6489..a102f336 100644
--- a/Lib/Launchpad/Entity/ManagedEntityBase.ahk
+++ b/Lib/Launchpad/Entity/LaunchProcessEntity.ahk
@@ -1,9 +1,10 @@
-class ManagedEntityBase extends AppEntityBase {
- defaultType := "Default"
+class LaunchProcessEntity extends FieldableEntity {
defaultClass := "Default"
- DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer) {
- return container.Get("entity_manager.launcher")[id]
+ DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer, parentEntity := "") {
+ this.parentEntityObj := parentEntity
+ ? parentEntity
+ : container.Get("entity_manager.launcher")[id]
}
GetDefaultFieldGroups() {
@@ -48,21 +49,10 @@ class ManagedEntityBase extends AppEntityBase {
)
)
- definitions["EntityType"] := Map(
- "default", this.defaultType,
- "description", "The key of the managed type to load settings and defaults from.",
- "required", true,
- "storageKey", this.configPrefix . "Type",
- "widget", "select",
- "selectOptionsCallback", ObjBindMethod(this, "ListEntityTypes"),
- "group", "general"
- )
-
- definitions["EntityClass"] := Map(
+ definitions["ProcessClass"] := Map(
"default", this.defaultClass,
- "description", "The name of the AHK class that will be used to control the managed entity.",
+ "description", "The name of the AHK class that will be used to control the process.",
"formField", false,
- "storageKey", this.configPrefix . "Class",
"required", true,
"group", "advanced",
"modes", Map(
@@ -73,11 +63,10 @@ class ManagedEntityBase extends AppEntityBase {
definitions["SearchDirs"] := Map(
"type", "directory",
"mustExist", false,
- "storageKey", this.configPrefix . "SearchDirs",
"default", [A_ProgramFiles],
"description", "Possible parent directories where the game's launcher might exist, to be used for auto-detection.",
"help", "These should be as specific as possible to reduce detection time.",
- "multiple", true,
+ "cardinality", 1, ; Change to another number once widgets for multiple values are worked out
"group", "locations",
"modes", Map(
"simple", Map("formField", false)
@@ -87,7 +76,6 @@ class ManagedEntityBase extends AppEntityBase {
definitions["InstallDir"] := Map(
"type", "directory",
"mustExist", false,
- "storageKey", this.configPrefix . "InstallDir",
"group", "locations",
"modes", Map(
"simple", Map("group", "general")
@@ -99,7 +87,6 @@ class ManagedEntityBase extends AppEntityBase {
"type", "file",
"fileMask", "*.exe",
"mustExist", false,
- "storageKey", this.configPrefix . "Exe",
"description", "This can be the full path on the system to the launcher's .exe file, or simply the name of the .exe file itself.",
"help", "If the .exe doesn't include the absolute path, auto-detection will be used by searching the DestinationDirs.",
"group", "locations",
@@ -113,7 +100,6 @@ class ManagedEntityBase extends AppEntityBase {
; - BlizzardProductDb (will search Battle.net's product.db file if it can be located for the installation directory, and the file will be found from there
; - Registry (will get a directory from the registry key specified by LocateRegKey and search for the file within it)
definitions["LocateMethod"] := Map(
- "storageKey", this.configPrefix . "LocateMethod",
"default", "SearchDirs",
"description", "How to search for the .exe if it isn't a full path already",
"group", "general",
@@ -122,16 +108,14 @@ class ManagedEntityBase extends AppEntityBase {
),
"widget", "select",
"selectOptionsCallback", ObjBindMethod(this, "ListLocateMethods"),
- "help", "Search: Searches a list of possible directories (Defaulting to some common possibilities) for the .exe file and uses that directory`nRegistry: Looks for the provided registry key and uses its value as the install path if present`nBlizzardProductDb: Searches for LauncherSpecificId within the Blizzard product.db file if present"
+ "help", "Search: Searches a list of possible directories (Defaulting to some common possibilities) for the .exe file and uses that directory`nRegistry: Looks for the provided registry key and uses its value as the install path if present`nBlizzardProductDb: Searches for PlatformRef within the Blizzard product.db file if present"
)
definitions["WindowTitle"] := Map(
- "storageKey", this.configPrefix . "WindowTitle",
"group", "process"
)
definitions["LocateRegView"] := Map(
- "storageKey", this.configPrefix . "LocateRegView",
"default", 64,
"group", "registry",
"widget", "select",
@@ -143,7 +127,7 @@ class ManagedEntityBase extends AppEntityBase {
)
definitions["LocateRegKey"] := Map(
- "storageKey", this.configPrefix . "LocateRegKey",
+ "title", "Registry Locator - Key",
"group", "registry",
"description", "The registry key to look up the install dir within.",
"help", "Path parts should be separated with backslashes and must start with one of: HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_USER, HKEY_CLASSES_ROOT, HKEY_CURRENT_CONFIG, or the abbreviation of one of those. To read from a remote registry, prefix the root path with two backslashes and the computer name.`n`nSimple example: HKLM\Path\To\Key`nRemote example: \\OTHERPC\HKLM\Path\To\Key",
@@ -153,7 +137,7 @@ class ManagedEntityBase extends AppEntityBase {
)
definitions["LocateRegValue"] := Map(
- "storageKey", this.configPrefix . "LocateRegValue",
+ "title", "Registry Locator - Value",
"group", "registry",
"description", "The name of the registry value to look up within the specified key.",
"help", "Example: InstallPath",
@@ -163,7 +147,7 @@ class ManagedEntityBase extends AppEntityBase {
)
definitions["LocateRegRemovePrefix"] := Map(
- "storageKey", this.configPrefix . "LocateRegRemovePrefix",
+ "title", "Registry Locator - Remove Prefix",
"group", "registry",
"modes", Map(
"simple", Map("formField", false)
@@ -171,7 +155,7 @@ class ManagedEntityBase extends AppEntityBase {
)
definitions["LocateRegRemoveSuffix"] := Map(
- "storageKey", this.configPrefix . "LocateRegRemoveSuffix",
+ "title", "Registry Locator - Remove Suffix",
"group", "registry",
"modes", Map(
"simple", Map("formField", false)
@@ -179,7 +163,7 @@ class ManagedEntityBase extends AppEntityBase {
)
definitions["LocateRegStripQuotes"] := Map(
- "storageKey", this.configPrefix . "LocateRegStripQuotes",
+ "title", "Registry Locator - Strip Quotes",
"default", false,
"group", "registry",
"description", "Strip quotes from registry value",
@@ -188,17 +172,17 @@ class ManagedEntityBase extends AppEntityBase {
)
)
- definitions["LauncherSpecificId"] := Map(
- "storageKey", this.configPrefix . "LauncherSpecificId",
+ definitions["PlatformRef"] := Map(
+ "title", "Platform Reference",
"description", "If the item is known to the launcher by a specific ID, it should be stored here.",
"group", "general"
)
definitions["WorkingDir"] := Map(
+ "title", "Working Directory",
"type", "directory",
"description", "The directory that the launcher should be run from.",
"help", "If not set, it will be run without setting an explicit working directory, which is usually sufficient.",
- "storageKey", this.configPrefix . "WorkingDir",
"group", "locations",
"modes", Map(
"simple", Map("formField", false)
@@ -210,7 +194,6 @@ class ManagedEntityBase extends AppEntityBase {
definitions["RunType"] := Map(
"description", "Which method to use for launching this item.",
"help", "This is only needed for launchers that have to manage their own process.",
- "storageKey", this.configPrefix . "RunType",
"default", "Command",
"group", "process",
"widget", "select",
@@ -220,15 +203,13 @@ class ManagedEntityBase extends AppEntityBase {
definitions["UsesShortcut"] := Map(
"type", "boolean",
"description", "Whether a shortcut file will be used when starting the internally-managed game launcher",
- "formField", false,
- "storageKey", this.configPrefix . "UsesShortcut"
+ "formField", false
)
definitions["ReplaceProcess"] := Map(
"type", "boolean",
"description", "Kill and re-launch the game process immediately after it is detected.",
"help", "This can be used to force Launchpad to own the game process, but won't for for every game.",
- "storageKey", this.configPrefix . "ReplaceProcess",
"default", false,
"group", "process"
)
@@ -238,9 +219,9 @@ class ManagedEntityBase extends AppEntityBase {
; - The path of an .exe file on the system to which a shortcut will be created in AssetsDir if it doesn't already exist. Using this option
; is usually not necessary, since you can run the .exe directly instead.
definitions["ShortcutSrc"] := Map(
+ "title", "Shortcut Source",
"description", "The shortcut file used to launch the game launcher itself.",
"help", "This is typically only needed if the Shortcut LauncherRunType is selected.",
- "storageKey", this.configPrefix . "ShortcutSrc",
"group", "locations",
"modes", Map(
"simple", Map("group", "general")
@@ -252,7 +233,6 @@ class ManagedEntityBase extends AppEntityBase {
; - Scheduled (Creates an immediate scheduled task that runs the game, then waits until the window opens (if needed) and then closes)
definitions["RunMethod"] := Map(
"description", "Which method to use to run the RunCmd",
- "storageKey", this.configPrefix . "RunMethod",
"default", "Run",
"group", "process",
"widget", "select",
@@ -265,7 +245,6 @@ class ManagedEntityBase extends AppEntityBase {
definitions["ProcessType"] := Map(
"description", "Which method to use to wait for the game to close.",
"help", "This is not needed if the GameRunType is RunWait",
- "storageKey", this.configPrefix . "ProcessType",
"default", "Exe",
"group", "process",
"widget", "select",
@@ -277,7 +256,6 @@ class ManagedEntityBase extends AppEntityBase {
; - Class - This value should be set to the game's window class
definitions["ProcessId"] := Map(
"help", "This value's type is dependent on the ProcessType above. It can often be detected from other values, and is not needed if the GameRunType is RunWait.",
- "storageKey", this.configPrefix . "ProcessId",
"group", "process",
"modes", Map(
"simple", Map("formField", false)
@@ -286,7 +264,6 @@ class ManagedEntityBase extends AppEntityBase {
definitions["ProcessTimeout"] := Map(
"description", "The number of seconds to wait before giving up when waiting for a process.",
- "storageKey", this.configPrefix . "ProcessTimeout",
"default", 30,
"group", "process",
"modes", Map(
@@ -295,45 +272,26 @@ class ManagedEntityBase extends AppEntityBase {
)
definitions["RunCmd"] := Map(
+ "title", "Run Command",
"description", "The command that will be used to run the game's launcher.",
"help", "Typically only used if LauncherRunType is Command.",
- "storageKey", this.configPrefix . "RunCmd",
"group", "process"
)
return definitions
}
- GetData() {
- if (!this.ParentEntity) {
- throw EntityException("A parent entity is required on type " . Type(this))
- }
-
- return this.ParentEntity.GetData()
- }
-
- _createEntityData() {
- return ""
- }
-
- DiscoverDataSourceItemKey() {
- return this["EntityType"]
- }
-
- AutoDetectValues(recurse := true) {
- detectedValues := super.AutoDetectValues(recurse)
+ AutoDetectValues() {
+ detectedValues := super.AutoDetectValues()
processId := ""
- usesShortcut := false
- if (this.GetData().HasValue(this.configPrefix . "UsesShortcut")) {
- usesShortcut := this.GetData().GetValue(this.configPrefix . "UsesShortcut")
- } else {
- usesShortcut := (this["RunType"] == "Shortcut" || this["ShortcutSrc"] != "" || this["RunCmd"] == "")
- }
+ usesShortcut := (this.GetData().HasValue("UsesShortcut"))
+ ? this.GetData().GetValue("UsesShortcut")
+ : (this["RunType"] == "Shortcut" || this["ShortcutSrc"] != "" || this["RunCmd"] == "")
- detectedValues[this.configPrefix . "UsesShortcut"] := usesShortcut
- detectedValues[this.configPrefix . "RunType"] := usesShortcut ? "Shortcut" : "Command"
- detectedValues[this.configPrefix . "InstallDir"] := this.LocateInstallDir() ; This needs to run to expand exes without a dir
+ detectedValues["UsesShortcut"] := usesShortcut
+ detectedValues["RunType"] := usesShortcut ? "Shortcut" : "Command"
+ detectedValues["InstallDir"] := this.LocateInstallDir() ; This needs to run to expand exes without a dir
if (this["ProcessType"] == "Exe") {
SplitPath(this["Exe"], &processId)
@@ -341,37 +299,12 @@ class ManagedEntityBase extends AppEntityBase {
processId := this["WindowTitle"] ? this["WindowTitle"] : this.Id
}
- detectedValues[this.configPrefix . "ProcessId"] := processId
- detectedValues[this.configPrefix . "WorkingDir"] := this["InstallDir"]
+ detectedValues["ProcessId"] := processId
+ detectedValues["WorkingDir"] := this["InstallDir"]
return detectedValues
}
- ListEntityTypes() {
- types := []
- dataSources := this.GetAllDataSources()
- dsPath := this.GetDataSourceItemPath()
-
- for index, dataSource in dataSources {
- for listingIndex, listingItem in dataSource.ReadListing(dsPath) {
- exists := false
-
- for index, item in types {
- if (item == listingItem) {
- exists := true
- break
- }
- }
-
- if (!exists) {
- types.Push(listingItem)
- }
- }
- }
-
- return types
- }
-
ListRunTypes() {
return [
"Command",
@@ -381,19 +314,25 @@ class ManagedEntityBase extends AppEntityBase {
ListProcessTypes() {
return [
- "Exe", "Title", "Class"
+ "Exe",
+ "Title",
+ "Class"
]
}
ListRunMethods() {
return [
- "Run", "Scheduled", "RunWait"
+ "Run",
+ "Scheduled",
+ "RunWait"
]
}
ListLocateMethods() {
return [
- "Search", "Registry", "BlizzardProductDb"
+ "Search",
+ "Registry",
+ "BlizzardProductDb" ; TODO Move this to the Blizzard module
]
}
@@ -422,18 +361,20 @@ class ManagedEntityBase extends AppEntityBase {
validateResult["invalidFields"].push("RunCmd")
}
- ; TODO: Perform more launcher and game type validation here
+ ; TODO: Perform more validation here
return validateResult
}
ShortcutFileExists() {
- shortcutSrc := this["ShortcutSrc"] != "" ? this["ShortcutSrc"] : this.GetAssetPath(this.Id . ".lnk")
+ shortcutSrc := (this["ShortcutSrc"] != "")
+ ? this["ShortcutSrc"]
+ : this["AssetsDir"] . "\" . this.Id . ".lnk"
exists := FileExist(shortcutSrc)
if (!exists) {
- shortcutSrc := this.GetAssetPath(this.Id . ".url")
+ shortcutSrc := this["AssetsDir"] . "\" . this.Id . ".url"
exists := FileExist(shortcutSrc)
}
@@ -443,8 +384,7 @@ class ManagedEntityBase extends AppEntityBase {
LocateInstallDir() {
installDir := ""
- ; TODO: Add additional methods to detect the install dir
-
+ ; TODO Move BlizzardProductDb method to an event handled by the Blizzard module
if (this["LocateMethod"] == "BlizzardProductDb") {
blizzardDir := this.GetBlizzardProductDir()
@@ -453,6 +393,8 @@ class ManagedEntityBase extends AppEntityBase {
}
}
+ ; TODO: Add additional methods to detect the install dir
+
return installDir
}
@@ -504,6 +446,7 @@ class ManagedEntityBase extends AppEntityBase {
}
}
} else if (this["LocateMethod"] == "BlizzardProductDb") {
+ ; TODO Move BlizzardProductDb method to an event handled by the Blizzard module
blizzardDir := this.GetBlizzardProductDir()
if (blizzardDir != "") {
@@ -543,18 +486,14 @@ class ManagedEntityBase extends AppEntityBase {
return path
}
- GetBlizzardProductKey() {
- return "bna" ; Default to the Battle.net client itself
- }
-
+ ; TODO Move this method to the Blizzard module and call it from an eevent
GetBlizzardProductDir() {
- path := ""
- productCode := this.GetBlizzardProductKey()
-
- if (productCode != "" && this.app.Services.Has("BlizzardProductDb")) {
- path := this.app.Service("BlizzardProductDb").GetProductInstallPath(productCode)
- }
+ productCode (HasBase(this, GameProcessEntity.Prototype))
+ ? productCode := this["PlatformRef"]
+ : "bna" ; Default to the Battle.net client itself
- return path
+ return (productCode != "" && this.app.Services.Has("BlizzardProductDb"))
+ ? this.app["BlizzardProductDb"].GetProductInstallPath(productCode)
+ : ""
}
}
diff --git a/Lib/Launchpad/Entity/LauncherEntity.ahk b/Lib/Launchpad/Entity/LauncherEntity.ahk
index edaec377..b5d4c554 100644
--- a/Lib/Launchpad/Entity/LauncherEntity.ahk
+++ b/Lib/Launchpad/Entity/LauncherEntity.ahk
@@ -1,7 +1,5 @@
-class LauncherEntity extends AppEntityBase {
- dataSourcePath := "games"
- configPrefix := "Launcher"
- additionalManagedLauncherDefaults := Map()
+class LauncherEntity extends FieldableEntity {
+ additionalLauncherProcessDefaults := Map()
IsBuilt {
get => this.LauncherExists(false)
@@ -29,6 +27,11 @@ class LauncherEntity extends AppEntityBase {
"weight", 80
)
+ groups["advanced"] := Map(
+ "name", "Advanced",
+ "weight", 100
+ )
+
return groups
}
@@ -47,18 +50,12 @@ class LauncherEntity extends AppEntityBase {
definitions["id"]["modes"]["wizard"]["formField"] := true
definitions["id"]["modes"]["wizard"]["widget"] := "combo"
- definitions["id"]["modes"]["wizard"]["selectOptionsCallback"] := ObjBindMethod(this, "ListKnownGames")
+ definitions["id"]["modes"]["wizard"]["selectOptionsCallback"] := ObjBindMethod(this, "ListEntities", false, true)
definitions["id"]["modes"]["wizard"]["description"] := "Select an existing game from the API, or enter a custom game key to create your own."
definitions["name"]["description"] := "You can change the display name of the game if it differs from the key."
definitions["name"]["help"] := "The launcher filename will still be created using the key."
- if (definitions.Has("DataSourceItemKey")) {
- definitions["DataSourceItemKey"]["default"] := ""
- definitions["DataSourceItemKey"]["description"] := "The key to use when looking this item up in its data source(s)."
- definitions["DataSourceItemKey"]["help"] := "By default, this is the same as the main key."
- }
-
definitions["Platform"] := Map(
"type", "entity_reference",
"entityType", "platform",
@@ -68,10 +65,10 @@ class LauncherEntity extends AppEntityBase {
"showDefaultCheckbox", false
)
- definitions["ManagedLauncher"] := Map(
+ definitions["LauncherProcess"] := Map(
"type", "entity_reference",
"required", true,
- "entityType", "managed_launcher",
+ "entityType", "launcher_process",
"child", true,
"weight", -25,
"widget", "entity_select",
@@ -87,13 +84,13 @@ class LauncherEntity extends AppEntityBase {
),
"default", this.idVal,
"showDefaultCheckbox", false,
- "valueType", EntityFieldBase.VALUE_TYPE_DEFAULT
+ "storeEntityData", true
)
- definitions["ManagedGame"] := Map(
+ definitions["GameProcess"] := Map(
"type", "entity_reference",
"required", true,
- "entityType", "managed_game",
+ "entityType", "game_process",
"child", true,
"weight", -20,
"widget", "entity_select",
@@ -109,7 +106,7 @@ class LauncherEntity extends AppEntityBase {
),
"default", this.idVal,
"showDefaultCheckbox", false,
- "valueType", EntityFieldBase.VALUE_TYPE_DEFAULT
+ "storeEntityData", true
)
definitions["DestinationDir"] := Map(
@@ -189,7 +186,7 @@ class LauncherEntity extends AppEntityBase {
definitions["RunBefore"] := Map(
"type", "entity_reference",
"entityType", "task",
- "multiple", true,
+ "cardinality", 1, ; Change to another number once widgets for multiple values are worked out
"group", "tasks",
"modes", Map(
"simple", Map("formField", false)
@@ -201,7 +198,7 @@ class LauncherEntity extends AppEntityBase {
definitions["CloseBefore"] := Map(
"type", "entity_reference",
"entityType", "task",
- "multiple", true,
+ "cardinality", 1, ; Change to another number once widgets for multiple values are worked out
"group", "tasks",
"modes", Map(
"simple", Map("formField", false)
@@ -213,7 +210,7 @@ class LauncherEntity extends AppEntityBase {
definitions["RunAfter"] := Map(
"type", "entity_reference",
"entityType", "task",
- "multiple", true,
+ "cardinality", 1, ; Change to another number once widgets for multiple values are worked out
"group", "tasks",
"modes", Map(
"simple", Map("formField", false)
@@ -225,7 +222,7 @@ class LauncherEntity extends AppEntityBase {
definitions["CloseAfter"] := Map(
"type", "entity_reference",
"entityType", "task",
- "multiple", true,
+ "cardinality", 1, ; Change to another number once widgets for multiple values are worked out
"group", "tasks",
"modes", Map(
"simple", Map("formField", false)
@@ -295,6 +292,15 @@ class LauncherEntity extends AppEntityBase {
"help", "If the Steam Overlay attaches within this time, and the Force option is not active, then the Launchpad Overlay will not be used."
)
+ definitions["AssetsDir"] := Map(
+ "type", "directory",
+ "description", "The directory where any required assets for this launcher will be saved.",
+ "default", this.app.Config["assets_dir"] . "\" . this.Id,
+ "group", "advanced",
+ "formField", false,
+ "modes", Map("simple", Map("formField", false))
+ )
+
return definitions
}
@@ -305,13 +311,6 @@ class LauncherEntity extends AppEntityBase {
return (FileExist(this.GetLauncherFile(this.Id, checkSourceFile)) != "")
}
- ListKnownGames() {
- return this.container
- .Get("manager.data_source")
- .GetDefaultDataSource()
- .ReadListing("game-keys")
- }
-
LauncherIsOutdated() {
outdated := true
@@ -320,7 +319,7 @@ class LauncherEntity extends AppEntityBase {
if (filePath && FileExist(filePath)) {
launcherVersion := FileGetVersion(this.GetLauncherFile(this.Id))
- if (launcherVersion && !this.app.Service("version_checker").VersionIsOutdated(this.app.Version, launcherVersion)) {
+ if (launcherVersion && !this.app["version_checker"].VersionIsOutdated(this.app.Version, launcherVersion)) {
outdated := false
}
@@ -330,7 +329,7 @@ class LauncherEntity extends AppEntityBase {
if (!buildInfo["Version"] || !buildInfo["Timestamp"]) {
outdated := true
} else {
- if (configInfo["Version"] && this.app.Service("version_checker").VersionIsOutdated(configInfo["Version"], buildInfo["Version"])) {
+ if (configInfo["Version"] && this.app["version_checker"].VersionIsOutdated(configInfo["Version"], buildInfo["Version"])) {
outdated := true
} else if (configInfo["Timestamp"] && DateDiff(configInfo["Timestamp"], buildInfo["Timestamp"], "S") > 0) {
outdated := true
@@ -379,76 +378,17 @@ class LauncherEntity extends AppEntityBase {
return ValidateResult
}
- SaveModifiedData() {
- super.SaveModifiedData()
+ SaveEntity(recurse := true) {
+ super.SaveEntity(recurse)
this.app.State.SetLauncherConfigInfo(this.Id)
}
- DiscoverDataSourceItemKey() {
- if (!this["DataSourceItemKey"]) {
- dataSources := this.GetAllDataSources()
-
- for index, dataSource in dataSources {
- platform := this["Platform"] ? this["Platform"]["id"] : ""
- apiPath := "lookup/" this.Id
-
- if (platform) {
- apiPath .= "/" . platform
- }
-
- dsData := dataSource.ReadJson(apiPath)
-
- if (dsData != "" && dsData.Has("id") && dsData["id"]) {
- this["DataSourceItemKey"] := dsData["id"]
- break
- }
- }
- }
-
- if (this["DataSourceItemKey"]) {
- return this["DataSourceItemKey"]
- } else {
- return ""
- }
- }
-
IconFileExists() {
- iconSrc := this["IconSrc"] != "" ? this["IconSrc"] : this.GetAssetPath(this.Id . ".ico")
- return FileExist(iconSrc)
- }
-
- MergeAdditionalDataSourceDefaults(defaults, dataSourceData) {
- launcherType := this.DetectLauncherType(defaults, dataSourceData)
-
- checkType := (launcherType == "") ? "Default" : launcherType
- if (dataSourceData.Has("Launchers") && dataSourceData["Launchers"].Has(checkType) && HasBase(dataSourceData["Launchers"][checkType], Map.Prototype)) {
- this.additionalManagedLauncherDefaults := this.merger.Merge(dataSourceData["Launchers"][checkType], this.additionalManagedLauncherDefaults)
- defaults := this.merger.Merge(defaults, dataSourceData["Launchers"][checkType])
- }
-
- defaults["ManagedLauncher"] := launcherType
+ iconSrc := (this["IconSrc"] != "")
+ ? this["IconSrc"]
+ : this["AssetsDir"] . "\" . this.Id . ".ico"
- return defaults
- }
-
- DetectLauncherType(defaults, dataSourceData := "") {
- launcherType := ""
-
- if (this.UnmergedFieldData.Has("LauncherType")) {
- launcherType := this.UnmergedFieldData["LauncherType"]
- } else if (defaults.Has("LauncherType")) {
- launcherType := defaults["LauncherType"]
- }
-
- if (launcherType == "") {
- launcherType := "Default"
- }
-
- if (dataSourceData != "" && dataSourceData.Has("Launchers")) {
- launcherType := this._dereferenceKey(launcherType, dataSourceData["Launchers"])
- }
-
- return launcherType
+ return FileExist(iconSrc)
}
_dereferenceKey(key, map) {
@@ -459,19 +399,19 @@ class LauncherEntity extends AppEntityBase {
return key
}
- AutoDetectValues(recurse := true) {
- detectedValues := super.AutoDetectValues(recurse)
+ AutoDetectValues() {
+ detectedValues := super.AutoDetectValues()
if (!detectedValues.Has("IconSrc")) {
checkPath := this["AssetsDir"] . "\" . this.Id . ".ico"
if (FileExist(checkPath)) {
detectedValues["IconSrc"] := checkPath
- } else if (this.Has("ManagedGame", false) && this["ManagedGame"].Has("Exe", false)) {
- detectedValues["IconSrc"] := this["ManagedGame"].LocateExe()
+ } else if (this.Has("GameProcess", false) && this["GameProcess"].Has("Exe", false)) {
+ detectedValues["IconSrc"] := this["GameProcess"].LocateExe()
} else {
theme := this.container.Get("manager.theme").GetComponent()
- detectedValues["IconSrc"] := theme.GetIconPath("Game")
+ detectedValues["IconSrc"] := theme.GetIconPath("game")
}
}
diff --git a/Lib/Launchpad/Entity/ManagedLauncherEntity.ahk b/Lib/Launchpad/Entity/LauncherProcessEntity.ahk
similarity index 79%
rename from Lib/Launchpad/Entity/ManagedLauncherEntity.ahk
rename to Lib/Launchpad/Entity/LauncherProcessEntity.ahk
index c060cb91..ee21fcc1 100644
--- a/Lib/Launchpad/Entity/ManagedLauncherEntity.ahk
+++ b/Lib/Launchpad/Entity/LauncherProcessEntity.ahk
@@ -1,16 +1,13 @@
-class ManagedLauncherEntity extends ManagedEntityBase {
- configPrefix := "Launcher"
- defaultType := "Default"
+class LauncherProcessEntity extends LaunchProcessEntity {
defaultClass := "SimpleLauncher"
- dataSourcePath := "launcher-types"
BaseFieldDefinitions() {
definitions := super.BaseFieldDefinitions()
- definitions["ManagedGame"] := Map(
+ definitions["GameProcess"] := Map(
"type", "entity_reference",
"required", true,
- "entityType", "managed_game",
+ "entityType", "game_process",
"child", false,
"formField", false,
"editable", false
@@ -18,14 +15,14 @@ class ManagedLauncherEntity extends ManagedEntityBase {
definitions["CloseBeforeRun"] := Map(
"type", "boolean",
- "storageKey", this.configPrefix . "CloseBeforeRun",
+ "storageKey", "CloseBeforeRun",
"default", false,
"description", "whether or not the launcher should be closed (if it is running) before starting the game"
)
definitions["CloseAfterRun"] := Map(
"type", "boolean",
- "storageKey", this.configPrefix . "CloseAfterRun",
+ "storageKey", "CloseAfterRun",
"default", false,
"description", "Indicates whether the launcher should be closed after the game stops"
)
@@ -34,7 +31,7 @@ class ManagedLauncherEntity extends ManagedEntityBase {
"type", "time_offset",
"timeUnits", "s",
"min", 0,
- "storageKey", this.configPrefix . "ClosePreDelay",
+ "storageKey", "ClosePreDelay",
"default", 0,
"required", true,
"group", "advanced",
@@ -48,7 +45,7 @@ class ManagedLauncherEntity extends ManagedEntityBase {
"type", "time_offset",
"timeUnits", "s",
"min", 0,
- "storageKey", this.configPrefix . "ClosePostDelay",
+ "storageKey", "ClosePostDelay",
"default", 0,
"required", true,
"group", "advanced",
@@ -65,7 +62,7 @@ class ManagedLauncherEntity extends ManagedEntityBase {
; - "AutoPolite" - Automatically attempt to politely close the launcher, or fail if it can't be closed politely
; - "AutoKill" - Automatically attempts to end the launcher process, forcefully if needed
definitions["CloseMethod"] := Map(
- "storageKey", this.configPrefix . "CloseMethod",
+ "storageKey", "CloseMethod",
"default", "Prompt",
"description", "How to attempt to close the launcher if running",
"widget", "select",
@@ -77,7 +74,7 @@ class ManagedLauncherEntity extends ManagedEntityBase {
"type", "time_offset",
"timeUnits", "s",
"min", 0,
- "storageKey", this.configPrefix . "RecheckDelay",
+ "storageKey", "RecheckDelay",
"default", 10,
"required", true,
"group", "advanced",
@@ -91,7 +88,7 @@ class ManagedLauncherEntity extends ManagedEntityBase {
"type", "time_offset",
"timeUnits", "s",
"min", 0,
- "storageKey", this.configPrefix . "WaitTimeout",
+ "storageKey", "WaitTimeout",
"default", 30,
"required", true,
"group", "advanced",
@@ -105,11 +102,11 @@ class ManagedLauncherEntity extends ManagedEntityBase {
"type", "time_offset",
"timeUnits", "ms",
"min", 0,
- "storageKey", this.configPrefix . "KillPreDelay",
+ "storageKey", "KillPreDelay",
"default", 10,
"required", true,
"group", "advanced",
- "description", "If killing a managed launcher forcefully, ending the process will be delayed by this number of seconds.",
+ "description", "If killing a launch process forcefully, ending the process will be delayed by this number of seconds.",
"modes", Map(
"simple", Map("formField", false)
)
@@ -119,11 +116,11 @@ class ManagedLauncherEntity extends ManagedEntityBase {
"type", "time_offset",
"timeUnits", "ms",
"min", 0,
- "storageKey", this.configPrefix . "KillPostDelay",
+ "storageKey", "KillPostDelay",
"default", 5,
"required", true,
"group", "advanced",
- "description", "If killing a managed launcher forcefully, the launcher will wait this number of seconds after trying to end the process before reporting success.",
+ "description", "If killing a launch process forcefully, the launcher will wait this number of seconds after trying to end the process before reporting success.",
"modes", Map(
"simple", Map("formField", false)
)
@@ -133,7 +130,7 @@ class ManagedLauncherEntity extends ManagedEntityBase {
"type", "time_offset",
"timeUnits", "s",
"min", 0,
- "storageKey", this.configPrefix . "PoliteCloseWait",
+ "storageKey", "PoliteCloseWait",
"required", true,
"default", 10,
"group", "advanced",
diff --git a/Lib/Launchpad/Entity/PlatformEntity.ahk b/Lib/Launchpad/Entity/PlatformEntity.ahk
index 43a4a1b8..77060a1a 100644
--- a/Lib/Launchpad/Entity/PlatformEntity.ahk
+++ b/Lib/Launchpad/Entity/PlatformEntity.ahk
@@ -1,7 +1,5 @@
-class PlatformEntity extends AppEntityBase {
+class PlatformEntity extends FieldableEntity {
platformObj := ""
- configPrefix := ""
- dataSourcePath := "platforms"
Platform {
get => this.GetPlatform()
@@ -136,8 +134,8 @@ class PlatformEntity extends AppEntityBase {
}
}
- AutoDetectValues(recurse := true) {
- detectedValues := super.AutoDetectValues(recurse)
+ AutoDetectValues() {
+ detectedValues := super.AutoDetectValues()
detectedValues["IsInstalled"] := this.Platform.IsInstalled()
detectedValues["InstalledVersion"] := this.Platform.GetInstalledVersion()
detectedValues["InstallDir"] := this.Platform.GetInstallDir()
diff --git a/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk b/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk
index d5b3f944..7c25316b 100644
--- a/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk
+++ b/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk
@@ -87,7 +87,7 @@ class GamePlatformBase {
}
NeedsUpdate() {
- return this.app.Service("version_checker").VersionIsOutdated(this.GetLatestVersion(), this.GetInstalledVersion())
+ return this.app["version_checker"].VersionIsOutdated(this.GetLatestVersion(), this.GetInstalledVersion())
}
GetInstalledVersion() {
@@ -138,7 +138,7 @@ class GamePlatformBase {
return []
}
- GetLauncherSpecificId(key) {
+ GetPlatformRef(key) {
return key
}
@@ -164,8 +164,8 @@ class GamePlatformBase {
locator := GameExeLocator(installDir)
possibleExes := locator.Locate("")
exeName := this.DetermineMainExe(key, possibleExes)
- launcherSpecificId := this.GetLauncherSpecificId(key)
- detectedGameObj := DetectedGame(key, this, this.launcherType, this.gameType, installDir, exeName, launcherSpecificId, possibleExes)
+ platformRef := this.GetPlatformRef(key)
+ detectedGameObj := DetectedGame(key, this, this.launcherType, this.gameType, installDir, exeName, platformRef, possibleExes)
if (this.installDir) {
detectedGameObj.launcherInstallDir := this["InstallDir"]
@@ -179,35 +179,48 @@ class GamePlatformBase {
}
DetermineMainExe(key, possibleExes) {
- dataSource := this.app.Service("manager.data_source").GetDefaultDataSource()
- dsData := this.GetDataSourceDefaults(dataSource, key)
-
mainExe := ""
if (possibleExes.Length == 1) {
mainExe := possibleExes[1]
- } else if (possibleExes.Length > 1 && dsData.Has("GameExe")) {
- for index, possibleExe in possibleExes {
- SplitPath(possibleExe, &fileName)
-
- if (dsData["GameExe"] == fileName) {
- mainExe := possibleExe
- break
+ } else if (possibleExes.Length > 1) {
+ ; @todo move the API functionality into an event in an event in the WebServicesEventSubscriber
+ if (this.app.Services.Has("web_services.adapter_manager")) {
+ resultData := this.app["web_services.adapter_manager"].AdapterRequest(
+ Map("id", key),
+ Map(
+ "dataType", "entity_data",
+ "entityType", "launcher",
+ "tags", "defaults"
+ ),
+ "read",
+ true
+ )
+
+ for key, data in resultData {
+ if (
+ data
+ && HasBase(data, Map.Prototype)
+ && data.Has("defaults")
+ && data["defaults"]
+ && data["defaults"].Has("GameExe")
+ && data["defaults"]["GameExe"]
+ ) {
+ for index, possibleExe in possibleExes {
+ SplitPath(possibleExe, &fileName)
+
+ if (data["defaults"]["GameExe"] == fileName) {
+ mainExe := possibleExe
+ break 2
+ }
+ }
+ }
}
}
- }
- return mainExe
- }
-
- GetDataSourceDefaults(dataSource, key) {
- defaults := Map()
- dsData := dataSource.ReadJson(key, "Games")
-
- if (dsData != "" && dsData.Has("data") && dsData["data"].Has("defaults")) {
- defaults := this.merger.Merge(dsData["data"]["defaults"], defaults)
+
}
- return defaults
+ return mainExe
}
}
diff --git a/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk b/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk
index 79b752ba..11299d17 100644
--- a/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk
+++ b/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk
@@ -3,26 +3,31 @@
defaults := super.GetDefaultConfig(container, config)
defaults["title"] := "Account Info"
defaults["buttons"] := "*&Save|&Cancel|&Logout"
+ defaults["webService"] := ""
return defaults
}
Controls() {
super.Controls()
- if (this.app.Services.Has("Auth")) {
- info := this.app.Service("Auth").GetStatusInfo()
+ if (this.app.Services.Has("entity_manager.web_service")) {
+ entityMgr := this.app.Services["entity_manager.web_service"]
+
+ webService := this.config["webService"]
+
+ if (!webService) {
+ throw AppException("Opened AccountInfoWindow without a webService specified.")
+ }
+
+ info := webService.GetStatusInfo()
if (info) {
opts := "w" . this.windowSettings["contentWidth"] . " x" . this.margin . " y+" . this.margin
this.guiObj.AddPicture("x" . this.margin . " y+" . this.margin, info["photo"])
- this.guiObj.AddText(opts, "Email: " . info["email"])
+ this.guiObj.AddText(opts, "Account: " . info["account"])
}
}
- this.AddHeading("Player Name")
- this.AddEdit("PlayerName", this.app.Config["player_name"], "", 250)
- this.guiObj.AddText("w" . this.windowSettings["contentWidth"], "Note: Player name is stored locally and not synced with your online Launchpad account yet.")
-
position := "Wrap x" . this.margin . " y+" . this.margin
options := position . " w" . this.windowSettings["contentWidth"] . " +0x200 c" . this.themeObj.GetColor("textLink")
this.guiObj.AddLink(options, 'Manage your account at launchpad.games.')
@@ -30,11 +35,17 @@
ProcessResult(result, submittedData := "") {
if (result == "Logout") {
- if (this.app.Services.Has("Auth")) {
- this.app.Service("Auth").Logout()
+ webService := this.config["webService"]
+
+ if (webService) {
+ webService.Logout()
+ }
+ } else if (result == "Login") {
+ webService := this.config["webService"]
+
+ if (webService) {
+ webService.Login()
}
- } else if (result == "Save" && submittedData) {
- this.app.Config["player_name"] := submittedData.PlayerName
}
return result
diff --git a/Lib/Launchpad/Gui/Dialog/LoginWindow.ahk b/Lib/Launchpad/Gui/Dialog/LoginWindow.ahk
index 66b256e0..7edc0d12 100644
--- a/Lib/Launchpad/Gui/Dialog/LoginWindow.ahk
+++ b/Lib/Launchpad/Gui/Dialog/LoginWindow.ahk
@@ -2,7 +2,6 @@
entityObj := ""
entityManager := ""
missingFields := Map()
- dataSource := ""
GetDefaultConfig(container, config) {
defaults := super.GetDefaultConfig(container, config)
diff --git a/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk b/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk
index 04119844..3aee2ba9 100644
--- a/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk
+++ b/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk
@@ -2,10 +2,7 @@
detectedGameObj := ""
newValues := Map()
missingFields := Map()
- dataSource := ""
knownGames := ""
- launcherTypes := ""
- gameTypes := ""
__New(container, themeObj, config, detectedGameObj) {
this.detectedGameObj := detectedGameObj
@@ -22,11 +19,45 @@
Create() {
super.Create()
- this.dataSource := this.app.Service("manager.data_source").GetDefaultDataSource()
- this.knownPlatforms := this.dataSource.ReadListing("platforms")
- this.knownGames := this.dataSource.ReadListing("game-keys")
- this.launcherTypes := this.dataSource.ReadListing("launcher-types")
- this.gameTypes := this.dataSource.ReadListing("game-types")
+
+ this.knownPlatforms := []
+ this.knownGames := []
+
+ ; @todo replace this, or at least refactor it to live somewhere else
+ if (this.container.Has("web_services.adapter_manager")) {
+ knownMap := Map(
+ "platform", "knownPlatforms",
+ "launcher", "knownGames",
+ )
+
+ for entityTypeId, varName in knownMap {
+ results := this.container["web_services.adapter_manager"].AdapterRequest("", Map(
+ "dataType", "entity_list",
+ "entityType", entityTypeId
+ ), "read", true)
+
+ if (results) {
+ for , idList in results {
+ if (idList) {
+ for , id in idList {
+ exists := false
+
+ for , item in %varName% {
+ if (item == id) {
+ exists := true
+ break
+ }
+ }
+
+ if (!exists) {
+ this.%varName%.Push(id)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
GetTitle() {
@@ -35,12 +66,10 @@
Controls() {
super.Controls()
- this.Add("ComboBoxControl", "vId", "Id", this.detectedGameObj.Id, this.knownGames, "OnIdChange", "You can change the detected game key here, which will become the name of your launcher. Your existing launchers, and all launchers known about via the API, can be selected to match this game up with one of those items.")
- this.Add("SelectControl", "vLauncherType", "Launcher Type", this.detectedGameObj.launcherType, this.launcherTypes, "OnLauncherTypeChange", "This tells " . this.app.appName . " how to interact with any launcher your game might require. If your game's launcher isn't listed, or your game doesn't have a launcher, start with `"Default`".`n`nYou can customize the details of the launcher type after it is added.")
- this.Add("SelectControl", "vGameType", "Game Type", this.detectedGameObj.gameType, this.gameTypes, "OnGameTypeChange", "This tells " . this.app.appName . " how to launch your game. Most games can use 'default', but launchers can support different game types.`n`nYou can customize the details of the game type after it is added.")
+ this.Add("ComboBoxControl", "vId", "Id", this.detectedGameObj.key, this.knownGames, "OnIdChange", "You can change the detected game key here, which will become the name of your launcher. Your existing launchers, and all launchers known about via the API, can be selected to match this game up with one of those items.")
this.Add("LocationBlock", "", "Install Dir", this.detectedGameObj.installDir, "InstallDir", "", true, "This is the directory that the game is installed in, if it could be detected.")
this.Add("ComboBoxControl", "vExe", "Exe", this.detectedGameObj.exeName, this.detectedGameObj.possibleExeNames, "OnExeChange", "The main Exe, if detected, should be pre-selected. You may change it to be the name (or path) of another exe, or select another one of the detected .exe files from the list (if more than one was found).")
- this.AddTextBlock("Launcher-Specific ID", "LauncherSpecificId", this.detectedGameObj.launcherSpecificId, "This is typically the ID which the game platform or launcher uses when referring to the game internally. Changing this value could cause issues with game launching.")
+ this.AddTextBlock("Launcher-Specific ID", "PlatformRef", this.detectedGameObj.platformRef, "This is typically the ID which the game platform or launcher uses when referring to the game internally. Changing this value could cause issues with game launching.")
}
AddTextBlock(heading, field, existingVal := "", helpText := "") {
@@ -61,16 +90,6 @@
this.newValues["key"] := ctl.Text
}
- OnLauncherTypeChange(ctl, info) {
- this.guiObj.Submit(false)
- this.newValues["launcherType"] := ctl.Text
- }
-
- OnGameTypeChange(ctl, info) {
- this.guiObj.Submit(false)
- this.newValues["gameType"] := ctl.Text
- }
-
GetValue(key) {
val := this.detectedGameObj.%key%
@@ -117,9 +136,9 @@
this.newValues["exeName"] := ctl.Text
}
- OnLauncherSpecificIdChange(ctl, info) {
+ OnPlatformRefChange(ctl, info) {
this.guiObj.Submit(false)
- this.newValues["launcherSpecificId"] := ctl.Text
+ this.newValues["platformRef"] := ctl.Text
}
ProcessResult(result, submittedData := "") {
diff --git a/Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk b/Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk
index 82c7b008..de956383 100644
--- a/Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk
+++ b/Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk
@@ -29,7 +29,7 @@
GetLauncherConfig() {
platformKey := Trim(this.guiObj["Platform"].Text)
config := Map("Platform", platformKey)
- platform := this.app.Service("entity_manager.platform")[platformKey]
+ platform := this.app["entity_manager.platform"][platformKey]
if (platform) {
config["LauncherType"] := platform.Platform.launcherType
diff --git a/Lib/Launchpad/Gui/Form/LauncherCreateFormBase.ahk b/Lib/Launchpad/Gui/Form/LauncherCreateFormBase.ahk
index dccde664..78d55ef9 100644
--- a/Lib/Launchpad/Gui/Form/LauncherCreateFormBase.ahk
+++ b/Lib/Launchpad/Gui/Form/LauncherCreateFormBase.ahk
@@ -1,10 +1,12 @@
class LauncherCreateFormBase extends FormGuiBase {
knownGames := ""
knownPlatforms := ""
- dataSource := ""
+ launcherMgr := ""
+ platformMgr := ""
__New(container, themeObj, config) {
- this.dataSource := container.Get("manager.data_source").GetDefaultDataSource()
+ this.launcherMgr := container.Get("entity_manager.launcher")
+ this.platformMgr := container.Get("entity_manager.platform")
super.__New(container, themeObj, config)
}
@@ -17,8 +19,8 @@
Create() {
super.Create()
- this.knownGames := this.dataSource.ReadListing("game-keys")
- this.knownPlatforms := this.dataSource.ReadListing("platforms")
+ this.knownGames := this.launcherMgr.ListEntities(false, true)
+ this.knownPlatforms := this.platformMgr.ListEntities(false, true)
}
ProcessResult(result, submittedData := "") {
diff --git a/Lib/Launchpad/Gui/Form/LauncherWizard.ahk b/Lib/Launchpad/Gui/Form/LauncherWizard.ahk
index 1f1889b7..97bcd11a 100644
--- a/Lib/Launchpad/Gui/Form/LauncherWizard.ahk
+++ b/Lib/Launchpad/Gui/Form/LauncherWizard.ahk
@@ -25,7 +25,7 @@
GetLauncherConfig() {
platformKey := Trim(this.guiObj["Platform"].Text)
config := Map("Platform", platformKey, "GameInstallDir", this.installDir, "GameExe", this.exe)
- platform := this.app.Service("entity_manager.platform")[platformKey]
+ platform := this.app["entity_manager.platform"][platformKey]
if (platform) {
config["LauncherType"] := platform.Platform.launcherType
diff --git a/Lib/Launchpad/Gui/Form/SettingsWindow.ahk b/Lib/Launchpad/Gui/Form/SettingsWindow.ahk
index e6482347..ecf2c0cd 100644
--- a/Lib/Launchpad/Gui/Form/SettingsWindow.ahk
+++ b/Lib/Launchpad/Gui/Form/SettingsWindow.ahk
@@ -122,14 +122,6 @@
ctl := this.guiObj.AddDDL("vlogging_level xs y+m Choose" . chosen . " w200 c" . this.themeObj.GetColor("editText"), this.container.Get("logger").GetLogLevels())
ctl.OnEvent("Change", "OnLoggingLevelChange")
- this.AddConfigLocationBlock("API Endpoint", "api_endpoint")
-
- this.AddHeading("API Settings")
- ctl := this.AddConfigCheckBox("Enable API login for enhanced functionality", "api_authentication")
- ctl.ctl.NeedsRestart := true
- ctl := this.AddConfigCheckBox("Automatically initiate API login when needed", "api_auto_login")
- ctl.ctl.NeedsRestart := true
-
tabs.UseTab()
closeW := 100
@@ -137,11 +129,11 @@
}
OnManageBackups(btn, info) {
- this.app.Service("entity_type.backup").OpenManageWindow()
+ this.app["entity_type.backup"].OpenManageWindow()
}
OnManagePlatforms(btn, info) {
- this.app.Service("entity_type.platform").OpenManageWindow()
+ this.app["entity_type.platform"].OpenManageWindow()
}
AddConfigLocationBlock(heading, settingName, extraButton := "", helpText := "") {
@@ -194,7 +186,7 @@
} else if (btn == "OpenLauncherFile") {
this.app.Config.OpenLauncherFile()
} else if (btn == "ReloadLauncherFile") {
- this.app.Service("entity_manager.launcher").LoadComponents(true)
+ this.app["entity_manager.launcher"].LoadComponents(true)
}
}
@@ -205,7 +197,7 @@
} else if (btn == "OpenBackupsFile") {
this.app.Config.OpenBackupsFile()
} else if (btn == "ReloadBackupsFile") {
- this.app.Service("entity_manager.backup").LoadComponents(true)
+ this.app["entity_manager.backup"].LoadComponents(true)
}
}
@@ -216,7 +208,7 @@
} else if (btn == "OpenPlatformsFile") {
this.app.Config.OpenPlatformsFile()
} else if (btn == "ReloadPlatformsFile") {
- this.app.Service("entity_manager.platform").LoadComponents(true)
+ this.app["entity_manager.platform"].LoadComponents(true)
}
}
@@ -246,34 +238,24 @@
}
}
- OnApiEndpointMenuClick(btn) {
- if (btn == "ChangeApiEndpoint") {
- this.app.Service("manager.data_source").GetDefaultDataSource().ChangeApiEndpoint("", "")
- this.SetText("ApiEndpoint", this.app.Config["api_endpoint"], "Bold")
- this.needsRestart := true
- } else if (btn == "OpenApiEndpoint") {
- this.app.Service("manager.data_source").GetDefaultDataSource().Open()
- }
- }
-
OnCacheDirMenuClick(btn) {
if (btn == "ChangeCacheDir") {
- this.app.Service("manager.cache").ChangeCacheDir()
+ this.app["manager.cache"].ChangeCacheDir()
this.SetText("CacheDir", this.app.Config["cache_dir"], "Bold")
} else if (btn == "OpenCacheDir") {
- this.app.Service("manager.cache").OpenCacheDir()
+ this.app["manager.cache"].OpenCacheDir()
} else if (btn == "FlushCacheDir") {
- this.app.Service("manager.cache").FlushCaches(true, true)
+ this.app["manager.cache"].FlushCaches(true, true)
}
}
OnBackupDirMenuClick(btn) {
if (btn == "ChangeBackupDir") {
- this.app.Service("entity_manager.backup").ChangeBackupDir()
+ this.app["entity_manager.backup"].ChangeBackupDir()
this.SetText("BackupDir", this.app.Config["backup_dir"], "Bold")
this.needsRestart := true
} else if (btn == "OpenBackupDir") {
- this.app.Service("entity_manager.backup").OpenBackupDir()
+ this.app["entity_manager.backup"].OpenBackupDir()
}
}
@@ -318,7 +300,7 @@
this.app.Config.SaveConfig()
if (this.needsRestart) {
- response := this.app.Service("manager.gui").Dialog(Map(
+ response := this.app["manager.gui"].Dialog(Map(
"title", "Restart " . this.app.appName . "?",
"text", "One or more settings that have been changed require restarting " . this.app.appName . " to fully take effect.`n`nWould you like to restart " . this.app.appName . " now?"
))
@@ -328,8 +310,8 @@
}
}
- if (this.app.Service("manager.gui").Has("MainWindow")) {
- this.app.Service("manager.gui")["MainWindow"].UpdateListView()
+ if (this.app["manager.gui"].Has("MainWindow")) {
+ this.app["manager.gui"]["MainWindow"].UpdateListView()
}
return result
diff --git a/Lib/Launchpad/Gui/Form/SetupWindow.ahk b/Lib/Launchpad/Gui/Form/SetupWindow.ahk
index 1ebd5784..0be2ca76 100644
--- a/Lib/Launchpad/Gui/Form/SetupWindow.ahk
+++ b/Lib/Launchpad/Gui/Form/SetupWindow.ahk
@@ -16,7 +16,7 @@
Create() {
super.Create()
- this.availableThemes := this.app.Service("manager.theme").GetAvailableThemes()
+ this.availableThemes := this.app["manager.theme"].GetAvailableThemes()
}
AddDescription(text) {
@@ -58,7 +58,7 @@
}
GetInstalledPlatforms() {
- platformMgr := this.app.Service("entity_manager.platform")
+ platformMgr := this.app["entity_manager.platform"]
platformQuery := platformMgr.EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES)
.Condition(IsTrueCondition(), "IsInstalled")
return platformQuery.Execute()
@@ -80,7 +80,7 @@
this.guiObj.Submit(false)
len := StrLen("PlatformToggle")
name := SubStr(chk.Name, len + 1)
- platformMgr := this.app.Service("entity_manager.platform")
+ platformMgr := this.app["entity_manager.platform"]
if (platformMgr.Has(name)) {
platform := platformMgr[name]
@@ -115,8 +115,8 @@
ProcessResult(result, submittedData := "") {
if (result == "Start") {
- this.app.Service("config.app").SaveConfig()
- this.app.Service("entity_manager.platform").SaveModifiedEntities()
+ this.app["config.app"].SaveConfig()
+ this.app["entity_manager.platform"].SaveModifiedEntities()
if (submittedData.DetectGames) {
result := "Detect"
diff --git a/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk
index e78ab041..6f8aa4ba 100644
--- a/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk
+++ b/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk
@@ -12,9 +12,7 @@
this.detectedGames := detectedGames
this.state := container.Get("state.app")
this.launcherManager := container.Get("entity_manager.launcher")
- this.knownGames := container.Get("manager.data_source")
- .GetDefaultDataSource()
- .ReadListing("game-keys")
+ this.knownGames := this.launcherManager.ListEntities(false, true)
super.__New(container, themeObj, config)
}
@@ -83,7 +81,7 @@
GetListViewImgList(lv, large := false) {
IL := IL_Create(this.detectedGames.Count, 1, large)
- defaultIcon := this.themeObj.GetIconPath("Game")
+ defaultIcon := this.themeObj.GetIconPath("game")
iconNum := 1
for key, detectedGameObj in this.detectedGames {
@@ -200,7 +198,7 @@
detectedGameObj := this.detectedGames[key]
- result := this.app.Service("manager.gui").Dialog(Map(
+ result := this.app["manager.gui"].Dialog(Map(
"type", "DetectedGameEditor",
"ownerOrParent", this.guiId,
"child", true
@@ -233,7 +231,7 @@
menuItems := []
menuItems.Push(Map("label", "Edit", "name", "EditDetectedGame"))
- result := this.app.Service("manager.gui").Menu(menuItems, this)
+ result := this.app["manager.gui"].Menu(menuItems, this)
if (result == "EditDetectedGame") {
this.EditDetectedGame(key)
diff --git a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk
index 25dfa92e..3f897337 100644
--- a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk
+++ b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk
@@ -3,6 +3,7 @@
launcherManager := ""
platformManager := ""
showDetailsPane := true
+ lvResizeOpts := "h"
__New(container, themeObj, config) {
this.launcherManager := container.Get("entity_manager.launcher")
@@ -17,7 +18,7 @@
defaults["child"] := false
defaults["title"] := container.GetApp().appName
defaults["titleIsMenu"] := true
- defaults["showStatusIndicator"] := !!(container.Get("config.app").Has("api_authentication") && container.Get("config.app")["api_authentication"])
+ defaults["showStatusIndicator"] := container.Has("entity_manager.web_service")
return defaults
}
@@ -90,144 +91,12 @@
}
}
- _getToolsMenuEntityTypes() {
- entityTypes := Map()
-
- for key, entityType in this.container["manager.entity_type"] {
- if (entityType.definition["manager_link_in_tools_menu"]) {
- entityTypes[key] := entityType
- }
- }
-
- return entityTypes
- }
-
ShowTitleMenu() {
- menuEntityTypes := this._getToolsMenuEntityTypes()
- toolsItems := []
-
- for key, entityType in menuEntityTypes {
- menuLinkText := entityType.definition["manager_menu_link_text"]
-
- if (!menuLinkText) {
- menuLinkText := "&" . entityType.definition["name_plural"]
- }
-
- toolsItems.Push(Map("label", menuLinkText, "name", "manage_" . key))
- }
-
- toolsItems.Push(Map("label", "&Modules", "name", "ManageModules"))
- toolsItems.Push(Map("label", "&Flush Cache", "name", "FlushCache"))
-
- launchersItems := []
- launchersItems.Push(Map("label", "&Clean Launchers", "name", "CleanLaunchers"))
- launchersItems.Push(Map("label", "&Reload Launchers", "name", "ReloadLaunchers"))
-
- aboutItems := []
- aboutItems.Push(Map("label", "&About Launchpad", "name", "About"))
- aboutItems.Push(Map("label", "&Open Website", "name", "OpenWebsite"))
-
- menuItems := []
- menuItems.Push(Map("label", "&Tools", "name", "ToolsMenu", "childItems", toolsItems))
- menuItems.Push(Map("label", "&Launchers", "name", "LaunchersMenu", "childItems", launchersItems))
- menuItems.Push("")
- menuItems.Push(Map("label", "&About", "name", "About", "childItems", aboutItems))
- menuItems.Push("")
- menuItems.Push(Map("label", "&Settings", "name", "Settings"))
- menuItems.Push(Map("label", "Check for &Updates", "name", "CheckForUpdates"))
- menuItems.Push("")
- menuItems.Push(Map("label", "Provide &Feedback", "name", "ProvideFeedback"))
- menuItems.Push("")
- menuItems.Push(Map("label", "&Restart", "name", "Reload"))
- menuItems.Push(Map("label", "E&xit", "name", "Exit"))
-
- result := this.container["manager.gui"].Menu(menuItems, this, this.guiObj["WindowTitleText"])
-
- if (result == "ManageModules") {
- this.container["manager.gui"].OpenWindow("ManageModulesWindow")
- } else if (result == "FlushCache") {
- this.container["manager.cache"].FlushCaches(true, true)
- } else if (result == "CleanLaunchers") {
- this.container["manager.builder"].CleanLaunchers()
- } else if (result == "ReloadLaunchers") {
- this.launcherManager.LoadComponents(true)
- this.UpdateListView()
- } else if (result == "About") {
- this.container["manager.gui"].Dialog(Map("type", "AboutWindow"))
- } else if (result == "OpenWebsite") {
- this.app.OpenWebsite()
- } else if (result == "ProvideFeedback") {
- this.app.ProvideFeedback()
- } else if (result == "Settings") {
- this.container["manager.gui"].Dialog(Map("type", "SettingsWindow", "unique", false))
- } else if (result == "CheckForUpdates") {
- this.app.CheckForUpdates()
- } else if (result == "Reload") {
- this.app.restartApp()
- } else if (result == "Exit") {
- this.app.ExitApp()
- } else {
- for key, entityType in menuEntityTypes {
- if (result == "manage_" . key) {
- this.container["entity_type." . key].OpenManageWindow()
- break
- }
- }
- }
- }
-
- GetStatusInfo() {
- info := ""
-
- if (this.container.Has("Auth")) {
- info := this.container["Auth"].GetStatusInfo()
- }
-
- return info
- }
-
- OnStatusIndicatorClick(btn, info) {
- menuItems := []
-
- if (this.container.Has("Auth")) {
- if (this.container["Auth"].IsAuthenticated()) {
- menuItems.Push(Map("label", "Account Details", "name", "AccountDetails"))
- menuItems.Push(Map("label", "Logout", "name", "Logout"))
- } else {
- menuItems.Push(Map("label", "Login", "name", "Login"))
- }
- }
-
- result := this.container["manager.gui"].Menu(menuItems, this, btn)
-
- if (result == "AccountDetails") {
- accountResult := this.container["manager.gui"].Dialog(Map(
- "type", "AccountInfoWindow",
- "ownerOrParent", this.guiId,
- "child", true
- ))
-
- if (accountResult == "OK") {
- this.UpdateStatusIndicator()
- }
- } else if (result == "Logout") {
- if (this.container.Has("Auth")) {
- this.container["Auth"].Logout()
- }
- } else if (result == "Login") {
- if (this.container.Has("Auth")) {
- this.container["Auth"].Login()
- }
- }
- }
-
- StatusWindowIsOnline() {
- isOnline := false
-
- if (this.container.Has("Auth")) {
- isOnline := this.container["Auth"].IsAuthenticated()
- }
- return isOnline
+ this.app.MainMenu(
+ this,
+ this.guiObj["WindowTitleText"],
+ false
+ )
}
FormatDate(timestamp) {
@@ -262,7 +131,9 @@
}
status := launcher.GetStatus()
- apiStatus := launcher["DataSourceItemKey"] ? "Linked" : "Not linked"
+
+ ; @todo Move the API data to an event in the LaunchpadApi module
+ apiStatus := (launcher.HasField["web_service_launchpad_api_ref"] && launcher["web_service_launchpad_api_ref"]) ? "Linked" : "Not linked"
created := this.FormatDate(this.app.State.GetLauncherCreated(key))
updated := this.FormatDate(this.app.State.GetLauncherInfo("Config")["Timestamp"])
built := this.FormatDate(this.app.State.GetLauncherInfo("Build")["Timestamp"])
@@ -438,7 +309,9 @@
}
status := launcher.GetStatus()
- apiStatus := launcher["DataSourceItemKey"] ? "Linked" : "Not linked"
+
+ ; @todo Move the API code to the LaunchpadApi module
+ apiStatus := (launcher.HasField("web_service_launchpad_api_ref") && launcher["web_service_launchpad_api_ref"]) ? "Linked" : "Not linked"
created := this.FormatDate(this.app.State.GetLauncherCreated(key))
updated := this.FormatDate(this.app.State.GetLauncherInfo(key, "Config")["Timestamp"])
built := this.FormatDate(this.app.State.GetLauncherInfo(key, "Build")["Timestamp"])
@@ -513,7 +386,7 @@
GetListViewImgList(lv, large := false) {
IL := IL_Create(this.launcherManager.Count(true), 1, large)
- defaultIcon := this.themeObj.GetIconPath("Game")
+ defaultIcon := this.themeObj.GetIconPath("game")
iconNum := 1
for key, launcher in this.launcherManager {
@@ -533,7 +406,7 @@
GetItemImage(launcher) {
iconSrc := launcher["IconSrc"]
assetIcon := launcher["AssetsDir"] . "\" . launcher.Id . ".ico"
- defaultIcon := this.themeObj.GetIconPath("Game")
+ defaultIcon := this.themeObj.GetIconPath("game")
if ((!iconSrc || !FileExist(iconSrc)) && FileExist(assetIcon)) {
iconSrc := assetIcon
@@ -581,7 +454,7 @@
}
entity.SaveEntity()
- entity.UpdateDataSourceDefaults()
+ entity.UpdateDefaults()
this.UpdateListView()
}
}
diff --git a/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk
index a73baba9..be595c3e 100644
--- a/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk
+++ b/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk
@@ -40,7 +40,7 @@ class ManageBackupsWindow extends ManageWindowBase {
GetListViewImgList(lv, large := false) {
IL := IL_Create(this.backupManager.Count(true), 1, large)
- defaultIcon := this.themeObj.GetIconPath("Backup")
+ defaultIcon := this.themeObj.GetIconPath("backup")
iconNum := 1
for key, backup in this.backupManager {
@@ -109,7 +109,7 @@ class ManageBackupsWindow extends ManageWindowBase {
menuItems.Push(Map("label", "Restore", "name", "RestoreBackup"))
menuItems.Push(Map("label", "Delete", "name", "DeleteBackup"))
- result := this.app.Service("manager.gui").Menu(menuItems, this)
+ result := this.app["manager.gui"].Menu(menuItems, this)
if (result == "EditBackup") {
this.EditBackup(key)
diff --git a/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk
index bc961de2..7597c34b 100644
--- a/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk
+++ b/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk
@@ -1,5 +1,5 @@
class ManageModulesWindow extends ManageWindowBase {
- listViewColumns := Array("NAME", "ENABLED", "SOURCE", "VERSION")
+ listViewColumns := Array("NAME", "CATEGORY", "ENABLED", "SOURCE", "VERSION")
moduleManager := ""
needsRestart := false
@@ -26,9 +26,8 @@ class ManageModulesWindow extends ManageWindowBase {
for name, module in this.moduleManager.All("", false, true) {
enabledText := module.IsEnabled() ? "Yes" : "No"
- ; TODO Define source
- source := ""
- data[name] := [name, enabledText, source, module.GetVersion()]
+ source := module.IsCore() ? "Built-in" : "Third-party"
+ data[name] := [name, module.moduleInfo["category"], enabledText, source, module.GetVersion()]
}
return data
@@ -44,7 +43,7 @@ class ManageModulesWindow extends ManageWindowBase {
GetListViewImgList(lv, large := false) {
IL := IL_Create(this.lvCount, 1, large)
- defaultIcon := this.themeObj.GetIconPath("Module")
+ defaultIcon := this.themeObj.GetIconPath("module")
iconNum := 1
for key, module in this.moduleManager.All("", false, true) {
@@ -85,7 +84,7 @@ class ManageModulesWindow extends ManageWindowBase {
this.Submit(false)
}
- response := this.app.Service("manager.gui").Dialog(Map(
+ response := this.app["manager.gui"].Dialog(Map(
"title", "Restart " . this.app.appName . "?",
"text", "One or more module changes require restarting " . this.app.appName . " to fully take effect.`n`nWould you like to restart " . this.app.appName . " now?"
))
@@ -154,7 +153,7 @@ class ManageModulesWindow extends ManageWindowBase {
menuItems.Push(Map("label", "Delete", "name", "DeleteModule"))
}
- result := this.app.Service("manager.gui").Menu(menuItems, this)
+ result := this.app["manager.gui"].Menu(menuItems, this)
if (result == "EnableModule") {
this.EnableModule(key)
diff --git a/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk
index 51a526af..b3b54fe7 100644
--- a/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk
+++ b/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk
@@ -4,7 +4,7 @@ class PlatformsWindow extends ManageWindowBase {
__New(container, themeObj, config) {
this.platformManager := container.Get("entity_manager.platform")
- this.lvCount := this.platformManager.Count(true)
+ this.lvCount := this.platformManager.Count(true) - 1
super.__New(container, themeObj, config)
}
@@ -24,10 +24,12 @@ class PlatformsWindow extends ManageWindowBase {
data := Map()
for key, platform in this.platformManager {
- enabledText := platform["IsEnabled"] ? "Yes" : "No"
- detectGamesText := platform["DetectGames"] ? "Yes" : "No"
- installedText := platform["IsInstalled"] ? "Yes" : "No"
- data[key] := [platform.GetName(), enabledText, detectGamesText, installedText, platform["InstalledVersion"]]
+ if (key != "Basic") {
+ enabledText := platform["IsEnabled"] ? "Yes" : "No"
+ detectGamesText := platform["DetectGames"] ? "Yes" : "No"
+ installedText := platform["IsInstalled"] ? "Yes" : "No"
+ data[key] := [platform.GetName(), enabledText, detectGamesText, installedText, platform["InstalledVersion"]]
+ }
}
return data
@@ -43,18 +45,21 @@ class PlatformsWindow extends ManageWindowBase {
GetListViewImgList(lv, large := false) {
IL := IL_Create(this.platformManager.Count(true), 1, large)
- defaultIcon := this.themeObj.GetIconPath("Platform")
+ defaultIcon := this.themeObj.GetIconPath("platform")
iconNum := 1
for key, platform in this.platformManager {
- iconSrc := platform["IconSrc"]
- if (!iconSrc or !FileExist(iconSrc)) {
- iconSrc := defaultIcon
- }
+ if (key != "Basic") {
+ iconSrc := platform["IconSrc"]
- IL_Add(IL, iconSrc)
- iconNum++
+ if (!iconSrc or !FileExist(iconSrc)) {
+ iconSrc := defaultIcon
+ }
+
+ IL_Add(IL, iconSrc)
+ iconNum++
+ }
}
return IL
@@ -110,7 +115,7 @@ class PlatformsWindow extends ManageWindowBase {
menuItems.Push(Map("label", "Install", "name", "InstallPlatform"))
}
- result := this.app.Service("manager.gui").Menu(menuItems, this)
+ result := this.app["manager.gui"].Menu(menuItems, this)
if (result == "EditPlatform") {
this.EditPlatform(key)
diff --git a/Lib/Launchpad/Includes.ahk b/Lib/Launchpad/Includes.ahk
index cfacabc5..cd69a6ec 100644
--- a/Lib/Launchpad/Includes.ahk
+++ b/Lib/Launchpad/Includes.ahk
@@ -21,12 +21,11 @@
#Include Config\LaunchpadConfig.ahk
#Include Config\PlatformsConfig.ahk
#Include ConfigMigrator\LaunchpadIniMigrator.ahk
-#Include DataSource\LocalDataSource.ahk
#Include DetectedGame\DetectedGame.ahk
+#Include Entity\GameProcessEntity.ahk
#Include Entity\LauncherEntity.ahk
-#Include Entity\ManagedEntityBase.ahk
-#Include Entity\ManagedGameEntity.ahk
-#Include Entity\ManagedLauncherEntity.ahk
+#Include Entity\LauncherProcessEntity.ahk
+#Include Entity\LaunchProcessEntity.ahk
#Include Entity\PlatformEntity.ahk
#Include GamePlatform\BasicPlatform.ahk
#Include GamePlatform\GamePlatformBase.ahk
diff --git a/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk b/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk
index 5d326086..097d4a3e 100644
--- a/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk
+++ b/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk
@@ -22,22 +22,22 @@ class BlizzardPlatform extends RegistryLookupGamePlatformBase {
productInstalls := []
if (this.app.Services.Has("BlizzardProductDb")) {
- productInstalls := this.app.Service("BlizzardProductDb").GetProductInstalls()
+ productInstalls := this.app["BlizzardProductDb"].GetProductInstalls()
}
games := []
for index, productData in productInstalls {
- launcherSpecificId := productData["productCode"]
+ platformRef := productData["productCode"]
- if (launcherSpecificId != "agent" && launcherSpecificId != "bna" && productData.Has("settings") && productData["settings"].Has("installPath")) {
+ if (platformRef != "agent" && platformRef != "bna" && productData.Has("settings") && productData["settings"].Has("installPath")) {
installPath := productData["settings"]["installPath"]
installPath := StrReplace(installPath, "/", "\")
SplitPath(installPath, &key)
locator := GameExeLocator(installPath)
possibleExes := locator.Locate("")
mainExe := this.DetermineMainExe(key, possibleExes)
- games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installPath, mainExe, launcherSpecificId, possibleExes))
+ games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installPath, mainExe, platformRef, possibleExes))
}
}
diff --git a/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk b/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk
index 06e768d5..1cdb2845 100644
--- a/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk
+++ b/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk
@@ -3,12 +3,12 @@ class EpicPlatform extends RegistryLookupGamePlatformBase {
displayName := "Epic Store"
launcherType := "Epic"
gameType := "Epic"
- installDirRegView := 32
- installDirRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{0E63B233-DC24-442C-BD38-0B91D90FEC5B}"
- versionRegView := 32
- versionRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{0E63B233-DC24-442C-BD38-0B91D90FEC5B}"
- uninstallCmdRegView := 32
- uninstallCmdRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{0E63B233-DC24-442C-BD38-0B91D90FEC5B}"
+ installDirRegView := 64
+ installDirRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\B4B4F9022FD3528499604D6D8AE00CE9\InstallProperties"
+ versionRegView := 64
+ versionRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\B4B4F9022FD3528499604D6D8AE00CE9\InstallProperties"
+ uninstallCmdRegView := 64
+ uninstallCmdRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\B4B4F9022FD3528499604D6D8AE00CE9\InstallProperties"
Install() {
Run("https://www.epicgames.com/store/en-US/download")
@@ -41,15 +41,29 @@ class EpicPlatform extends RegistryLookupGamePlatformBase {
}
if (isGame) {
- key := obj["Name"]
+ key := obj.Has("Name") ? obj["Name"] : ""
+
+ if (!key && obj.Has("DisplayName")) {
+ key := obj["DisplayName"]
+ }
+
+ if (!key && obj.Has("MandatoryAppFolderName")) {
+ key := obj["MandatoryAppFolderName"]
+ }
+
+ if (!key) {
+ throw AppException("Could not determine detected game key.")
+ }
+
+ displayName := obj.Has("DisplayName") ? obj["DisplayName"] : ""
installDir := obj["InstallLocation"]
- launcherSpecificId := obj["AppName"]
+ platformRef := obj["AppName"]
;exeName := obj["LaunchExecutable"]
;possibleExes := [obj["LaunchExecutable"]]
locator := GameExeLocator(installDir)
possibleExes := locator.Locate("")
mainExe := this.DetermineMainExe(key, possibleExes)
- games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installDir, mainExe, launcherSpecificId, possibleExes))
+ games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installDir, mainExe, platformRef, possibleExes, displayName))
}
}
}
diff --git a/Lib/Launchpad/Modules/Riot/GamePlatform/RiotPlatform.ahk b/Lib/Launchpad/Modules/Riot/GamePlatform/RiotPlatform.ahk
index ef37ab9e..091b1bf5 100644
--- a/Lib/Launchpad/Modules/Riot/GamePlatform/RiotPlatform.ahk
+++ b/Lib/Launchpad/Modules/Riot/GamePlatform/RiotPlatform.ahk
@@ -66,7 +66,7 @@ class RiotPlatform extends RegistryLookupGamePlatformBase {
return dirs
}
- GetLauncherSpecificId(key) {
+ GetPlatformRef(key) {
if (key == "VALORANT") {
key := "valorant"
} else if (key == "Legends of Runeterra") {
diff --git a/Lib/Launchpad/Modules/Steam/GamePlatform/SteamPlatform.ahk b/Lib/Launchpad/Modules/Steam/GamePlatform/SteamPlatform.ahk
index 3d88dfad..644f96ff 100644
--- a/Lib/Launchpad/Modules/Steam/GamePlatform/SteamPlatform.ahk
+++ b/Lib/Launchpad/Modules/Steam/GamePlatform/SteamPlatform.ahk
@@ -64,7 +64,7 @@ class SteamPlatform extends RegistryLookupGamePlatformBase {
if (IsObject(obj) && obj.Has("AppState")) {
gameState := obj["AppState"]
- launcherSpecificId := gameState["appid"]
+ platformRef := gameState["appid"]
key := gameState["name"]
installDir := dir . "\common\" . gameState["installdir"]
installDir := StrReplace(installDir, "/", "\")
@@ -76,7 +76,7 @@ class SteamPlatform extends RegistryLookupGamePlatformBase {
}
mainExe := this.DetermineMainExe(key, possibleExes)
- games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installDir, mainExe, launcherSpecificId, possibleExes))
+ games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installDir, mainExe, platformRef, possibleExes))
}
}
}
diff --git a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk
index 7397b273..6dffed31 100644
--- a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk
+++ b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk
@@ -2,12 +2,9 @@ class LaunchpadBuilder extends AppBase {
GetParameterDefinitions(config) {
parameters := super.GetParameterDefinitions(config)
parameters["config_path"] := this.appDir . "\Launchpad.build.json"
- parameters["config.api_endpoint"] := "https://api.launchpad.games/v1"
- parameters["config.data_source_key"] := ""
- parameters["config.api_authentication"] := true
parameters["config.dist_dir"] := this.appDir . "\Dist"
parameters["config.build_dir"] := this.appDir . "\Build"
- parameters["config.icon_file"] := this.appDir . "\Resources\Graphics\Launchpad.ico"
+ parameters["config.icon_file"] := this.appDir . "\Resources\Graphics\launchpad.ico"
parameters["config.github_username"] := ""
parameters["config.github_token"] := ""
parameters["config.github_repo"] := "VolantisDev/Launchpad"
@@ -33,11 +30,6 @@ class LaunchpadBuilder extends AppBase {
"arguments", [AppRef(), this.appDir . "\" . this.appName . ".ini"]
)
- services["manager.data_source"] := Map(
- "class", "DataSourceManager",
- "arguments", [ContainerRef(), ServiceRef("manager.event"), ServiceRef("notifier"), ParameterRef("config.data_source_key")]
- )
-
services["FileHasher"] := "FileHasher"
services["GitTagVersionIdentifier"] := Map(
@@ -50,8 +42,8 @@ class LaunchpadBuilder extends AppBase {
RunApp(config) {
super.RunApp(config)
- version := this.Service("GitTagVersionIdentifier").IdentifyVersion()
- buildInfo := this.Service("manager.gui").Dialog(Map(
+ version := this["GitTagVersionIdentifier"].IdentifyVersion()
+ buildInfo := this["manager.gui"].Dialog(Map(
"type", "BuildSettingsForm",
"version", version
))
@@ -60,8 +52,12 @@ class LaunchpadBuilder extends AppBase {
this.ExitApp()
}
- if (buildInfo.DeployToApi && this.Services.Has("Auth")) {
- this.Service("Auth").Login()
+ if (buildInfo.DeployToApi && this.Services.Has("entity_manager.web_service")) {
+ entityMgr := this.Services["entity_manager.web_service"]
+
+ if (entityMgr.Has("launchpad_api") && entityMgr["launchpad_api"]["Enabled"]) {
+ entityMgr["launchpad_api"].Login()
+ }
}
version := buildInfo.Version
@@ -80,7 +76,7 @@ class LaunchpadBuilder extends AppBase {
}
if (buildInfo.DeployToGitHub || buildInfo.DeployToApi || buildInfo.DeployToChocolatey) {
- releaseInfo := this.Service("manager.gui").Dialog(Map("type", "ReleaseInfoForm"))
+ releaseInfo := this["manager.gui"].Dialog(Map("type", "ReleaseInfoForm"))
if (!releaseInfo) {
this.ExitApp()
@@ -145,7 +141,7 @@ class LaunchpadBuilder extends AppBase {
if (!this.GetCmdOutput("git show-ref " . version)) {
RunWait("git tag " . version, this.appDir)
- response := this.Service("manager.gui").Dialog(Map(
+ response := this["manager.gui"].Dialog(Map(
"title", "Push git tag?",
"text", "Would you like to push the git tag that was just created (" . version . ") to origin?"
))
diff --git a/Lib/LaunchpadBuilder/AppBuilder/ChocoPkgBuilder.ahk b/Lib/LaunchpadBuilder/AppBuilder/ChocoPkgBuilder.ahk
index 8ea2644b..a855d436 100644
--- a/Lib/LaunchpadBuilder/AppBuilder/ChocoPkgBuilder.ahk
+++ b/Lib/LaunchpadBuilder/AppBuilder/ChocoPkgBuilder.ahk
@@ -9,7 +9,7 @@ class ChocoPkgBuilder extends AppBuilderBase {
throw AppException("Installer file doesn't exist, cannot build chocolatey package.")
}
- hash := this.app.Service("FileHasher").Hash(installer, FileHasher.HASH_TYPE_SHA256)
+ hash := this.app["FileHasher"].Hash(installer, FileHasher.HASH_TYPE_SHA256)
if (!hash) {
throw AppException("Failed to create an SHA256 hash of the installer file.")
diff --git a/Lib/LaunchpadBuilder/BuildDeployer/ApiBuildDeployer.ahk b/Lib/LaunchpadBuilder/BuildDeployer/ApiBuildDeployer.ahk
index 330209b9..5173dc95 100644
--- a/Lib/LaunchpadBuilder/BuildDeployer/ApiBuildDeployer.ahk
+++ b/Lib/LaunchpadBuilder/BuildDeployer/ApiBuildDeployer.ahk
@@ -2,7 +2,7 @@ class ApiBuildDeployer extends BuildDeployerBase {
Deploy(deployInfo) {
apiUrl := "https://api.launchpad.games/v1/release-info"
- this.app.Service("manager.gui").Dialog(Map(
+ this.app["manager.gui"].Dialog(Map(
"title", "Not Yet Available",
"text", "Release info pushing is not yet available. Please update release info manually."
))
diff --git a/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk b/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk
index ec563367..7bc3a131 100644
--- a/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk
+++ b/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk
@@ -39,7 +39,7 @@ class LaunchpadLauncher extends AppBase {
)
services["Game"] := Map(
- "class", config["gameConfig"]["GameClass"],
+ "class", config["gameConfig"]["ProcessClass"],
"arguments", [
AppRef(),
ParameterRef("launcher_key"),
@@ -48,7 +48,7 @@ class LaunchpadLauncher extends AppBase {
)
services["Launcher"] := Map(
- "class", config["launcherConfig"]["LauncherClass"],
+ "class", config["launcherConfig"]["ProcessClass"],
"arguments", [
ParameterRef("launcher_key"),
ServiceRef("manager.gui"),
@@ -64,11 +64,11 @@ class LaunchpadLauncher extends AppBase {
RunApp(config) {
super.RunApp(config)
- this.Service("Launcher").LaunchGame()
+ this["Launcher"].LaunchGame()
}
RestartApp() {
- game := this.Service("Game")
+ game := this["Game"]
if (game) {
game.StopOverlay()
@@ -78,7 +78,7 @@ class LaunchpadLauncher extends AppBase {
}
ExitApp() {
- game := this.Service("Game")
+ game := this["Game"]
if (game) {
game.StopOverlay()
diff --git a/Lib/LaunchpadLauncher/Condition/SteamConditionBase.ahk b/Lib/LaunchpadLauncher/Condition/SteamConditionBase.ahk
index 43f0ebbe..765a5369 100644
--- a/Lib/LaunchpadLauncher/Condition/SteamConditionBase.ahk
+++ b/Lib/LaunchpadLauncher/Condition/SteamConditionBase.ahk
@@ -11,7 +11,7 @@ class SteamConditionBase extends ConditionBase {
GetSteamPath(app) {
steamPath := ""
- platforms := app.Parameter("platforms")
+ platforms := app.Parameter["platforms"]
if (platforms.Has("Steam") && platforms["Steam"].Has("InstallDir")) {
steamPath := platforms["Steam"]["InstallDir"]
diff --git a/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk b/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk
index 15a0fc83..c88ed59b 100644
--- a/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk
+++ b/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk
@@ -4,10 +4,10 @@ class BlizzardGame extends SimpleGame {
playButtonColors := ["0074E0", "148EFF"]
GetRunCmd() {
- launcherPath := this.app.Service("Launcher").config["LauncherInstallDir"] . "\" . this.app.Service("Launcher").config["LauncherExe"]
+ launcherPath := this.app["Launcher"].config["LauncherInstallDir"] . "\" . this.app["Launcher"].config["LauncherExe"]
if (launcherPath != "") {
- gameKey := this.config["GameLauncherSpecificId"]
+ gameKey := this.config["GamePlatformRef"]
launcherPath .= " --game=" . gameKey . " --gamepath=`"" . this.config["GameInstallDir"] . "`" --productcode=" . gameKey
}
@@ -16,7 +16,7 @@ class BlizzardGame extends SimpleGame {
RunGameRun() {
pid := super.RunGameRun()
- winTitle := this.app.Service("Launcher").config["LauncherWindowTitle"]
+ winTitle := this.app["Launcher"].config["LauncherWindowTitle"]
if (!WinExist(winTitle)) {
WinWait(winTitle)
@@ -53,7 +53,7 @@ class BlizzardGame extends SimpleGame {
}
CleanupAfterRun(progress := "") {
- winTitle := this.app.Service("Launcher").config["LauncherWindowTitle"]
+ winTitle := this.app["Launcher"].config["LauncherWindowTitle"]
if (WinExist(winTitle)) {
WinClose("ahk_id" . WinGetID(winTitle))
}
diff --git a/Lib/LaunchpadLauncher/Game/GameBase.ahk b/Lib/LaunchpadLauncher/Game/GameBase.ahk
index f50dd053..e943ab16 100644
--- a/Lib/LaunchpadLauncher/Game/GameBase.ahk
+++ b/Lib/LaunchpadLauncher/Game/GameBase.ahk
@@ -23,7 +23,7 @@ class GameBase {
config := Map()
}
- this.launcherConfig := app.Service("config.app")
+ this.launcherConfig := app["config.app"]
InvalidParameterException.CheckTypes("GameBase", "app", app, "AppBase", "key", key, "", "config", config, "Map")
this.app := app
this.key := key
@@ -33,7 +33,7 @@ class GameBase {
Log(message, level := "Debug") {
if (this.app.Services.Has("logger") && this.launcherConfig["LoggingLevel"] != "None") {
- this.app.Service("logger").Log(this.key . ": " . message, level)
+ this.app["logger"].Log(this.key . ": " . message, level)
}
}
@@ -180,14 +180,14 @@ class GameBase {
StartOverlay() {
SetTimer(this.overlayCallbackObj, 0)
this.Log("Starting Launchpad Overlay...")
- this.app.Service("manager.overlay").Start(this.launcherConfig["OverlayHotkey"])
+ this.app["manager.overlay"].Start(this.launcherConfig["OverlayHotkey"])
this.overlayStarted := true
}
StopOverlay() {
if (this.overlayStarted) {
this.Log("Shutting down Launchpad Overlay...")
- this.app.Service("manager.overlay").Close()
+ this.app["manager.overlay"].Close()
}
}
@@ -198,7 +198,7 @@ class GameBase {
}
this.Log("Closing overlay if running...")
- this.app.Service("manager.overlay").Close()
+ this.app["manager.overlay"].Close()
this.Log("Cleaning up scheduled task(s)...")
this.CleanupScheduledTask()
}
diff --git a/Lib/LaunchpadLauncher/Game/RiotGame.ahk b/Lib/LaunchpadLauncher/Game/RiotGame.ahk
index 0a63e8ec..1d63a6d2 100644
--- a/Lib/LaunchpadLauncher/Game/RiotGame.ahk
+++ b/Lib/LaunchpadLauncher/Game/RiotGame.ahk
@@ -1,9 +1,9 @@
class RiotGame extends SimpleGame {
GetRunCmd() {
- launcherPath := "`"" . this.app.Service("Launcher").config["LauncherInstallDir"] . "\" . this.app.Service("Launcher").config["LauncherExe"] . "`""
+ launcherPath := "`"" . this.app["Launcher"].config["LauncherInstallDir"] . "\" . this.app["Launcher"].config["LauncherExe"] . "`""
if (launcherPath != "") {
- gameKey := this.config["GameLauncherSpecificId"]
+ gameKey := this.config["GamePlatformRef"]
launcherPath .= " --launch-product=" . gameKey . " --launch-patchline=live"
}
diff --git a/Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk b/Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk
index 21754d91..900a7adb 100644
--- a/Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk
+++ b/Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk
@@ -189,7 +189,7 @@ class LauncherBase {
}
LaunchGameAction() {
- this.Log("Calling managed game's RunGame action")
+ this.Log("Calling game process's RunGame action")
return this.game.RunGame(this.progress)
}
diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk
index 9cfe560e..faff2db3 100644
--- a/Lib/Shared/Includes.ahk
+++ b/Lib/Shared/Includes.ahk
@@ -1,10 +1,28 @@
; Automatically-generated file. Manual edits will be overwritten.
-#Include Modules\Auth\AuthInfo\AuthInfo.ahk
-#Include Modules\Auth\AuthInfo\JwtAuthInfo.ahk
-#Include Modules\Auth\AuthProvider\AuthProviderBase.ahk
-#Include Modules\Auth\AuthProvider\JwtAuthProvider.ahk
-#Include Modules\LaunchpadApi\AuthProvider\LaunchpadApiAuthProvider.ahk
-#Include Modules\LaunchpadApi\DataSource\ApiDataSource.ahk
+#Include Modules\LaunchpadApi\EventSubscriber\LaunchpadApiSubscriber.ahk
+#Include Modules\WebServices\ComponentManager\WebServiceAdapterManager.ahk
+#Include Modules\WebServices\Entity\WebServiceEntity.ahk
+#Include Modules\WebServices\Entity\WebServiceProviderEntity.ahk
+#Include Modules\WebServices\Event\WebServicesEntityDataParamsEvent.ahk
+#Include Modules\WebServices\Event\WebServicesRequestEvent.ahk
+#Include Modules\WebServices\Event\WebServicesResponseEvent.ahk
+#Include Modules\WebServices\Events\WebServicesEvents.ahk
+#Include Modules\WebServices\EventSubscriber\WebServicesEventSubscriber.ahk
+#Include Modules\WebServices\Factory\WebServiceAdapterFactory.ahk
+#Include Modules\WebServices\Gui\AuthenticationGui\LaunchpadLoginWindow.ahk
+#Include Modules\WebServices\Gui\Form\FeedbackWindow.ahk
+#Include Modules\WebServices\Gui\ManageWindow\ManageWebServicesWindow.ahk
+#Include Modules\WebServices\LayerSource\WebServiceAdapterLayerSource.ahk
+#Include Modules\WebServices\WebServiceAdapter\FileWebServiceAdapter.ahk
+#Include Modules\WebServices\WebServiceAdapter\JsonWebServiceAdapter.ahk
+#Include Modules\WebServices\WebServiceAdapter\WebServiceAdapterBase.ahk
+#Include Modules\WebServices\WebServiceAuthenticator\JwtWebServiceAuthenticator.ahk
+#Include Modules\WebServices\WebServiceAuthenticator\WebServiceAuthenticatorBase.ahk
+#Include Modules\WebServices\WebServiceRequest\BasicWebServiceRequest.ahk
+#Include Modules\WebServices\WebServiceRequest\WebServiceRequestBase.ahk
+#Include Modules\WebServices\WebServiceResponse\CachedWebServiceResponse.ahk
+#Include Modules\WebServices\WebServiceResponse\HttpReqWebServiceResponse.ahk
+#Include Modules\WebServices\WebServiceResponse\WebServiceResponseBase.ahk
#Include Vendor\Gdip_All.ahk
#Include Vendor\LV_Constants.ahk
#Include Volantis.App\App\AppBase.ahk
@@ -23,13 +41,6 @@
#Include Volantis.App\Config\AppConfig.ahk
#Include Volantis.App\Container\ServiceComponentContainer.ahk
#Include Volantis.App\Container\WindowContainer.ahk
-#Include Volantis.App\DataSource\DataSourceBase.ahk
-#Include Volantis.App\DataSourceItem\DataSourceItemBase.ahk
-#Include Volantis.App\DataSourceItem\DSAssetFile.ahk
-#Include Volantis.App\DataSourceItem\DSFile.ahk
-#Include Volantis.App\DataSourceItem\DSJson.ahk
-#Include Volantis.App\DataSourceItem\DSListing.ahk
-#Include Volantis.App\Entity\AppEntityBase.ahk
#Include Volantis.App\Entity\BackupEntity.ahk
#Include Volantis.App\Entity\TaskEntity.ahk
#Include Volantis.App\Event\AlterComponentsEvent.ahk
@@ -38,7 +49,10 @@
#Include Volantis.App\Event\ComponentInfoEvent.ahk
#Include Volantis.App\Event\DefineComponentsEvent.ahk
#Include Volantis.App\Event\LoadComponentEvent.ahk
+#Include Volantis.App\Event\MenuItemsEvent.ahk
+#Include Volantis.App\Event\MenuResultEvent.ahk
#Include Volantis.App\Event\RegisterComponentsEvent.ahk
+#Include Volantis.App\Event\ReleaseInfoEvent.ahk
#Include Volantis.App\Event\ServiceDefinitionsEvent.ahk
#Include Volantis.App\Events\Events.ahk
#Include Volantis.App\Exception\AppException.ahk
@@ -53,7 +67,6 @@
#Include Volantis.App\Gui\Dialog\UpdateAvailableWindow.ahk
#Include Volantis.App\Gui\EntityEditor\EntityEditorBase.ahk
#Include Volantis.App\Gui\EntityEditor\SimpleEntityEditor.ahk
-#Include Volantis.App\Gui\Form\FeedbackWindow.ahk
#Include Volantis.App\Gui\Form\FormGuiBase.ahk
#Include Volantis.App\Gui\Form\IconSelector.ahk
#Include Volantis.App\Gui\ManageWindow\ManageEntitiesWindow.ahk
@@ -83,12 +96,10 @@
#Include Volantis.App\Installer\InstallerComponent\GitHubReleaseInstallerComponent.ahk
#Include Volantis.App\Installer\InstallerComponent\InstallerComponentBase.ahk
#Include Volantis.App\Service\AppServiceBase.ahk
-#Include Volantis.App\Service\AuthService.ahk
#Include Volantis.App\Service\EventManager.ahk
#Include Volantis.App\Service\LoggerService.ahk
#Include Volantis.App\Service\NotificationService.ahk
#Include Volantis.App\Service\ComponentManager\CacheManager.ahk
-#Include Volantis.App\Service\ComponentManager\DataSourceManager.ahk
#Include Volantis.App\Service\ComponentManager\GuiManager.ahk
#Include Volantis.App\Service\ComponentManager\InstallerManager.ahk
#Include Volantis.App\Service\ComponentManager\ThemeManager.ahk
@@ -96,6 +107,7 @@
#Include Volantis.App\State\AppState.ahk
#Include Volantis.App\State\CacheState.ahk
#Include Volantis.App\State\JsonState.ahk
+#Include Volantis.App\State\ParameterState.ahk
#Include Volantis.App\State\StateBase.ahk
#Include Volantis.Base\CLR\CLR.ahk
#Include Volantis.Base\Event\EventBase.ahk
@@ -210,16 +222,20 @@
#Include Volantis.Entity\EntityManager\EntityManagerBase.ahk
#Include Volantis.Entity\EntityStorage\ConfigEntityStorage.ahk
#Include Volantis.Entity\EntityStorage\EntityStorageBase.ahk
+#Include Volantis.Entity\EntityStorage\NullEntityStorage.ahk
#Include Volantis.Entity\EntityType\BasicEntityType.ahk
#Include Volantis.Entity\EntityType\EntityTypeBase.ahk
#Include Volantis.Entity\Event\EntityDataProcessorsEvent.ahk
+#Include Volantis.Entity\Event\EntityDetectValuesEvent.ahk
#Include Volantis.Entity\Event\EntityEvent.ahk
#Include Volantis.Entity\Event\EntityFieldDefinitionsEvent.ahk
#Include Volantis.Entity\Event\EntityFieldGroupsEvent.ahk
#Include Volantis.Entity\Event\EntityLayersEvent.ahk
+#Include Volantis.Entity\Event\EntityLayerSourcesEvent.ahk
+#Include Volantis.Entity\Event\EntityListEvent.ahk
+#Include Volantis.Entity\Event\EntityParentEvent.ahk
#Include Volantis.Entity\Event\EntityReferenceEvent.ahk
#Include Volantis.Entity\Event\EntityRefreshEvent.ahk
-#Include Volantis.Entity\Event\EntityStorageEvent.ahk
#Include Volantis.Entity\Event\EntityValidateEvent.ahk
#Include Volantis.Entity\Events\EntityEvents.ahk
#Include Volantis.Entity\Exception\EntityException.ahk
@@ -230,6 +246,7 @@
#Include Volantis.Entity\Factory\EntityTypeFactory.ahk
#Include Volantis.Entity\LayeredData\EntityData.ahk
#Include Volantis.Entity\LayerSource\EntityStorageLayerSource.ahk
+#Include Volantis.Entity\LayerSource\ParentEntityLayerSource.ahk
#Include Volantis.Entity\Query\EntityQuery.ahk
#Include Volantis.Entity\Validator\BasicValidator.ahk
#Include Volantis.Entity\Validator\ValidatorBase.ahk
diff --git a/Lib/Shared/Includes.test.ahk b/Lib/Shared/Includes.test.ahk
index 2fe8682e..f157d4f8 100644
--- a/Lib/Shared/Includes.test.ahk
+++ b/Lib/Shared/Includes.test.ahk
@@ -1,5 +1,4 @@
; Automatically-generated file. Manual edits will be overwritten.
-#Include Modules\Auth\AuthInfo\AuthInfo.test.ahk
#Include Volantis.App\App\AppBase.test.ahk
#Include Volantis.Base\Event\EventBase.test.ahk
#Include Volantis.Utility\Debugger\Debugger.test.ahk
diff --git a/Lib/Shared/Modules/Auth/Auth.module.json b/Lib/Shared/Modules/Auth/Auth.module.json
deleted file mode 100644
index 27e65909..00000000
--- a/Lib/Shared/Modules/Auth/Auth.module.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "module": {
- "name": "Authentication",
- "type": "AppModule",
- "icon": "",
- "category": "Web Services",
- "tags": ["Launchpad", "LaunchpadBuilder"],
- "description": "Authenticate with remote accounts, such as the Launchpad API",
- "author": {
- "name": "Ben McClure, Volantis Dev",
- "url": "https://volantisdev.com"
- },
- "website": "https://launchpad.games",
- "version": "{{VERSION}}",
- "appVersion": "",
- "dependencies": []
- },
- "parameters": {
- "config.auth_service": ""
- },
- "services": {
- "Auth": {
- "class": "AuthService",
- "arguments": ["@{App}", "@@config.auth_service", "@state.app"]
- }
- }
-}
diff --git a/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.ahk b/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.ahk
deleted file mode 100644
index 3428b9e0..00000000
--- a/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.ahk
+++ /dev/null
@@ -1,40 +0,0 @@
-class AuthInfo {
- isAuthenticated := false
- secureData := Map()
- persistentData := Map()
- userIdField := "userId"
-
- Authenticated {
- get => this.isAuthenticated
- set => this.isAuthenticated := !!(value)
- }
-
- UserId {
- get => this.Get(this.userIdField)
- }
-
- __New() {
-
- }
-
- Get(key) {
- value := ""
-
- if (this.secureData.Has(key)) {
- value := this.secureData[key]
- } else if (this.persistentData.Has(key)) {
- value := this.persistentData[key]
- }
-
- return value
- }
-
- Set(key, value, persist := false) {
- mapObj := persist ? this.persistentData : this.secureData
- mapObj[key] := value
- }
-
- GetPersistentData() {
- return this.persistentData
- }
-}
diff --git a/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.test.ahk b/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.test.ahk
deleted file mode 100644
index dfbcde79..00000000
--- a/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.test.ahk
+++ /dev/null
@@ -1,103 +0,0 @@
-class AuthInfoTest extends AppTestBase {
- TestAuthenticated() {
- authInfoObj := AuthInfo()
-
- authInfoObj.isAuthenticated := false
-
- this.AssertFalse(
- authInfoObj.Authenticated,
- "Test setting isAuthenticated to false"
- )
-
- authInfoObj.Authenticated := true
-
- this.AssertTrue(
- authInfoObj.Authenticated,
- "Test changing Authenticated to true"
- )
- }
-
- TestUserId() {
- authInfoObj := AuthInfo()
-
- this.AssertEquals(
- "",
- authInfoObj.UserId,
- "Assert that user ID is blank initially"
- )
-
- userIds := [
- 123,
- "456",
- "1234-5678-9012-3456",
- "r@nd0m"
- ]
-
- for userId in userIds {
- authInfoObj.Set(authInfoObj.userIdField, userId)
-
- this.AssertEquals(
- userId,
- authInfoObj.UserId,
- "Test changing user ID to " . userId
- )
- }
- }
-
- TestGet() {
- authInfoObj := AuthInfo()
-
- this.AssertEmpty(
- authInfoObj.Get("nonexistantValue"),
- "Getting a non-existent value returns an empty string"
- )
-
- authInfoObj.Set("testValue", "persistent", true)
-
- this.AssertEquals(
- "persistent",
- authInfoObj.Get("testValue"),
- "Getting a persistent value is possible"
- )
-
- authInfoObj.Set("testValue", "overridden", false)
-
- this.AssertEquals(
- "overridden",
- authInfoObj.Get("testValue"),
- "Getting a secure value overrides a persistent one"
- )
- }
-
- TestSet() {
- authInfoObj := AuthInfo()
-
- authInfoObj.Set("testValue", "persistent", true)
-
- this.AssertEquals(
- "persistent",
- authInfoObj.Get("testValue"),
- "Setting a persistent value is possible"
- )
-
- authInfoObj.Set("testValue", "overridden", false)
-
- this.AssertEquals(
- "overridden",
- authInfoObj.Get("testValue"),
- "Setting a secure value overrides a persistent one"
- )
- }
-
- TestGetPersistentData() {
- persistentData := Map("testValue", "persistent")
- authInfoObj := AuthInfo()
- authInfoObj.persistentData := persistentData
-
- this.AssertEquals(
- persistentData,
- authInfoObj.GetPersistentData(),
- "GetPersistentData returns all persistent data"
- )
- }
-}
diff --git a/Lib/Shared/Modules/Auth/AuthInfo/JwtAuthInfo.ahk b/Lib/Shared/Modules/Auth/AuthInfo/JwtAuthInfo.ahk
deleted file mode 100644
index 57b560ca..00000000
--- a/Lib/Shared/Modules/Auth/AuthInfo/JwtAuthInfo.ahk
+++ /dev/null
@@ -1,42 +0,0 @@
-class JwtAuthInfo extends AuthInfo {
- __New(userInfo) {
- super.__New()
-
- this.Authenticated := false
-
- added := Map()
-
- if (userInfo.Has("user_id")) {
- this.Authenticated := !!(userInfo["user_id"])
- this.Set("userId", userInfo["user_id"], true)
- added["user_id"] := true
- }
-
- if (userInfo.Has("refresh_token")) {
- this.Set("refresh", userInfo["refresh_token"], true)
- added["refresh_token"] := true
- }
-
- if (userInfo.Has("expires_in")) {
- timestamp := DateAdd(A_Now, userInfo["expires_in"], "S")
- this.Set("expires", timestamp, true)
- added["expires_in"] := true
- }
-
- if (userInfo.Has("id_token")) {
- this.Set("authToken", userInfo["id_token"])
- added["id_token"] := true
- }
-
- if (userInfo.Has("access_token")) {
- this.Set("accessToken", userInfo["access_token"])
- added["authToken"] := true
- }
-
- for key, value in userInfo {
- if (!added.Has(key) || !added[key]) {
- this.Set(key, value)
- }
- }
- }
-}
diff --git a/Lib/Shared/Modules/Auth/AuthProvider/AuthProviderBase.ahk b/Lib/Shared/Modules/Auth/AuthProvider/AuthProviderBase.ahk
deleted file mode 100644
index 475bc81f..00000000
--- a/Lib/Shared/Modules/Auth/AuthProvider/AuthProviderBase.ahk
+++ /dev/null
@@ -1,37 +0,0 @@
-class AuthProviderBase {
- __New(persistentData := "") {
-
- }
-
- Login() {
-
- }
-
- Logout(authInfoObj) {
-
- }
-
- NeedsRefresh(authInfoObj) {
-
- }
-
- RefreshAuthentication(authInfoObj) {
-
- }
-
- AddRefreshInfoToRequest(authInfoObj, httpReqObj) {
- ; Add refresh token to the request
- }
-
- AddLoginInfoToRequest(authToken, httpReqObj) {
- ; Add auth info needed to complete login to the request
- }
-
- AddAuthInfoToRequest(authInfoObj, httpReqObj) {
- ; Add auth info from authInfoObj to httpReqObj as needed
- }
-
- ExtractAuthInfoFromResponse(httpReqObj) {
-
- }
-}
diff --git a/Lib/Shared/Modules/Auth/AuthProvider/JwtAuthProvider.ahk b/Lib/Shared/Modules/Auth/AuthProvider/JwtAuthProvider.ahk
deleted file mode 100644
index 5fde6ad2..00000000
--- a/Lib/Shared/Modules/Auth/AuthProvider/JwtAuthProvider.ahk
+++ /dev/null
@@ -1,150 +0,0 @@
-class JwtAuthProvider extends AuthProviderBase {
- dataSourceObj := ""
- authEndpointUrl := ""
- webApiKey := ""
- refreshPath := "token"
- authToken := ""
- refreshToken := ""
-
- __New(dataSourceObj, authEndpointUrl, webApiKey, persistentData := "") {
- this.dataSourceObj := dataSourceObj
- this.authEndpointUrl := authEndpointUrl
- this.webApiKey := webApiKey
-
- if (persistentData != "" and persistentData.Has("authToken")) {
- this.authToken := persistentData["authToken"]
- }
- }
-
- Login(isRetry := false) {
- refreshToken := this.refreshToken
-
- if (!refreshToken) {
- refreshToken := this.ShowLoginWindow()
-
- if (refreshToken) {
- this.refreshToken := refreshToken
- }
- }
-
- userInfo := ""
-
- if (refreshToken) {
- url := this.GetAuthUrl(this.refreshPath)
- request := WinHttpReq(url)
- payload := Map("grant_type", "refresh_token", "refresh_token", refreshToken)
- response := request.Send("POST", payload)
-
- if (request.GetStatusCode() == 200) {
- userInfo := this.ExtractAuthInfoFromResponse(request)
- } else {
- this.refreshToken := ""
-
- if (isRetry) {
- ; TODO: Log user out instead of throwing an exception
- throw OperationFailedException("Login failed.")
- } else {
- return this.Login(true)
- }
- }
- }
-
- return userInfo
- }
-
- GetAuthUrl(path) {
- return this.authEndpointUrl . "/" . path . "?key=" . this.webApiKey
- }
-
- ShowLoginWindow() {
- return ""
- }
-
- Logout(authInfoObj) {
- this.authToken := ""
- this.refreshToken := ""
- return true
- }
-
- RefreshAuthentication(authInfoObj) {
- refreshToken := authInfoObj.Get("refresh")
-
- if (refreshToken) {
- this.refreshToken := refreshToken
- }
-
- return this.Login()
- }
-
- IsAuthenticationValid(authInfoObj) {
- isValid := false
-
- if (authInfoObj && authInfoObj.Authenticated) {
- isValid := !this.IsAuthenticationExpired(authInfoObj)
- }
-
- return isValid
- }
-
- AddAuthInfoToRequest(authInfoObj, httpReqObj) {
- authToken := authInfoObj.Get("authToken")
-
- if (authToken) {
- this.AddAuthorizationHeader(authToken, httpReqObj)
- }
- }
-
- AddAuthorizationHeader(bearerToken, httpReqObj) {
- if (bearerToken) {
- httpReqObj.requestHeaders["Authorization"] := "Bearer " . bearerToken
- }
- }
-
- ExtractAuthInfoFromResponse(httpReqObj) {
- responseData := Trim(httpReqObj.GetResponseData())
-
- userInfo := Map()
-
- if (responseData) {
- data := JsonData()
- userInfo := data.FromString(&responseData)
- }
-
- return JwtAuthInfo(userInfo)
- }
-
- IsAuthenticationExpired(authInfoObj) {
- expired := true
-
- if (authInfoObj and authInfoObj.Authenticated) {
- expires := authInfoObj.Get("expires")
-
- if (expires) {
- diff := DateDiff(A_Now, expires, "S")
- expired := (diff >= 0)
- }
- }
-
- return expired
- }
-
- NeedsRefresh(authInfoObj) {
- needsRefresh := false
- thresholdSeconds := -600
-
- if (!this.IsAuthenticationValid(authInfoObj)) {
- needsRefresh := true
- } else {
- expires := authInfoObj.Get("expires")
-
- if (expires) {
- diff := DateDiff(A_Now, expires, "S")
- needsRefresh := (diff >= thresholdSeconds)
- } else {
- needsRefresh := true
- }
- }
-
- return needsRefresh
- }
-}
diff --git a/Lib/Shared/Modules/LaunchpadApi/AuthProvider/LaunchpadApiAuthProvider.ahk b/Lib/Shared/Modules/LaunchpadApi/AuthProvider/LaunchpadApiAuthProvider.ahk
deleted file mode 100644
index ff44127d..00000000
--- a/Lib/Shared/Modules/LaunchpadApi/AuthProvider/LaunchpadApiAuthProvider.ahk
+++ /dev/null
@@ -1,30 +0,0 @@
-class LaunchpadApiAuthProvider extends JwtAuthProvider {
- app := ""
-
- __New(app, stateObj) {
- this.app := app
-
- persistentData := ""
-
- if (stateObj) {
- state := stateObj.GetState()
-
- if (state.Has("Authentication")) {
- persistentData := state["Authentication"]
- }
- }
-
- authEndpointUrl := "https://securetoken.googleapis.com/v1"
- webApiKey := "AIzaSyCbwzOWJjTft77P96dV5VB3dAx9TjdDowQ"
- super.__New(app.Service("manager.data_source").GetDefaultDataSource(), authEndpointUrl, webApiKey, persistentData)
- }
-
- ShowLoginWindow() {
- return this.app.Service("manager.gui").Dialog(Map("type", "LoginWindow"))
- }
-
- ExtractAuthInfoFromResponse(httpReqObj) {
- authInfoObj := super.ExtractAuthInfoFromResponse(httpReqObj)
- return authInfoObj
- }
-}
diff --git a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk b/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk
deleted file mode 100644
index bf0cf3aa..00000000
--- a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk
+++ /dev/null
@@ -1,142 +0,0 @@
-class ApiDataSource extends DataSourceBase {
- endpointUrl := ""
- app := ""
-
- __New(app, cacheManager, cacheName, endpointUrl) {
- this.app := app
- InvalidParameterException.CheckTypes("ApiDataSource", "endpointUrl", endpointUrl, "", "cacheManager", cacheManager, "CacheManager")
- this.endpointUrl := endpointUrl
- super.__New(cacheManager, cacheName)
- }
-
- ItemExists(path) {
- return super.ItemExists(path) || this.ItemExistsInApi(path)
- }
-
- ItemExistsInApi(path) {
- exists := (this.cache.ItemExists(path) && !this.cache.ItemNeedsUpdate(path))
-
- if (!exists) {
- request := this.SendHttpReq(path, "HEAD")
-
- exists := (request.GetStatusCode() == 200)
-
- if (!exists) {
- this.cache.SetNotFound(path)
- }
- }
-
- return exists
- }
-
- GetHttpReq(path, private := false) {
- request := WinHttpReq(this.GetRemoteLocation(path))
-
- if (private) {
- request.requestHeaders["Cache-Control"] := "no-cache"
-
- if (this.app.Config["api_authentication"] && this.app.Services.Has("Api")) {
- this.app.Service("Auth").AlterApiRequest(request)
- }
- }
-
- return request
- }
-
- SendHttpReq(path, method := "GET", data := "", private := false) {
- request := this.GetHttpReq(path, private)
- returnCode := request.Send(method, data)
- return request
- }
-
- GetRemoteLocation(path) {
- return this.endpointUrl . "/" . path
- }
-
- RetrieveItem(path, private := false, maxCacheAge := "") {
- if (maxCacheAge == "") {
- maxCacheAge := this.maxCacheAge
- }
-
- exists := (!private && this.cache.ItemExists(path) && !this.cache.ItemNeedsUpdate(path, maxCacheAge))
-
- if (!exists) {
- request := this.SendHttpReq(path, "GET", "", private)
-
- if (request.GetStatusCode() != 200) {
- return ""
- }
-
- responseBody := Trim(request.GetResponseData())
-
- if (responseBody == "") {
- return ""
- }
-
- this.cache.WriteItem(path, responseBody)
- }
-
- return this.cache.ItemExists(path) ? this.cache.ReadItem(path) : ""
- }
-
- GetStatus() {
- path := "status"
- statusExpire := 5 ;60
-
- status := Map("authenticated", false, "email", "", "photo", "")
-
- if (this.app.Config["api_authentication"] && this.app.Service("Auth").IsAuthenticated()) {
- statusResult := this.ReadItem(path, true)
-
- if (statusResult) {
- json := JsonData()
- status := json.FromString(&statusResult)
- }
- }
-
- return status
- }
-
- GetExt(path) {
-
- }
-
- Open() {
- Run(this.endpointUrl)
- }
-
- ChangeApiEndpoint(existingEndpoint := "", owner := "", parent := "") {
- if (existingEndpoint == "") {
- existingEndpoint := this.app.Config["api_endpoint"]
- }
-
- ownerOrParent := ""
-
- if (parent) {
- ownerOrParent := parent
- } else if (owner) {
- ownerOrParent := owner
- }
-
- apiEndpointUrl := this.app.Service("manager.gui").Dialog(Map(
- "type", "SingleInputBox",
- "title", "API Endpoint URL",
- "text", "Enter the base URL of the API endpoint you would like Launchpad to connect to. Leave blank to revert to the default.",
- "defaultValue", existingEndpoint,
- "ownerOrParent", ownerOrParent,
- "child", !!(parent)
- ))
-
- if (apiEndpointUrl != existingEndpoint) {
- this.app.Config["api_endpoint"] := apiEndpointUrl
- apiEndpointUrl := this.app.Config["api_endpoint"]
-
- if (apiEndpointUrl != existingEndpoint) {
- this.endpointUrl := apiEndpointUrl
- this.cache.FlushCache()
- }
- }
-
- return apiEndpointUrl
- }
-}
diff --git a/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk b/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk
new file mode 100644
index 00000000..1d349909
--- /dev/null
+++ b/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk
@@ -0,0 +1,24 @@
+class LaunchpadApiSubscriber extends EventSubscriberBase {
+ GetEventSubscribers() {
+ return Map(
+ WebServicesEvents.ENTITY_DATA_PARAMS, [
+ ObjBindMethod(this, "EntityDataParams")
+ ]
+ )
+ }
+
+ EntityDataParams(event, extra, eventName, hwnd) {
+ if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) {
+ return
+ }
+
+ ; TODO figure out how to access these values while the data layers are still being loaded
+ if (event.WebService["id"] == "launchpad_api") {
+ if (HasBase(event.Entity, LauncherEntity.Prototype)) {
+ event.Params["platformId"] := "Blizzard" ;event.Entity["Platform"]["id"]
+ } else if (HasBase(event.Entity, LaunchProcessEntity.Prototype)) {
+ event.Params["platformId"] := "Blizzard" ;event.Entity.ParentEntity["Platform"]["id"]
+ }
+ }
+ }
+}
diff --git a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json
index bd3b74bc..3bb1307d 100644
--- a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json
+++ b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json
@@ -12,30 +12,153 @@
"website": "https://launchpad.games",
"version": "{{VERSION}}",
"appVersion": "",
- "dependencies": ["Auth"]
+ "dependencies": ["WebServices"]
},
- "services": {
- "data_source.api": {
- "class": "ApiDataSource",
- "arguments": ["@{App}", "@manager.cache", "api", "@@config.api_endpoint"]
+ "parameters": {
+ "app.supports_update_check": true,
+ "web_services.adapters.launchpad_api.account_info": {
+ "dataType": "account_info",
+ "requestPath": "/status",
+ "cacheResponse": false,
+ "readAuth": true,
+ "dataMap": {
+ "email": "account"
+ }
+ },
+ "web_services.adapters.launchpad_api.hello": {
+ "dataType": "availability_check",
+ "requestPath": "/hello",
+ "cacheResponse": false
+ },
+ "web_services.adapters.launchpad_api.game_process_launcher_defaults": {
+ "dataType": "entity_data",
+ "requestPath": "/games/{id}",
+ "dataSelector": "gameProcessDefaults",
+ "entityType": "game_process",
+ "requiredParams": ["id"],
+ "tags": ["defaults"],
+ "weight": 5
+ },
+ "web_services.adapters.launchpad_api.game_process_platform_defaults": {
+ "dataType": "entity_data",
+ "requestPath": "/platforms/{platformId}",
+ "dataSelector": "data.gameProcessDefaults",
+ "entityType": "game_process",
+ "requiredParams": ["platformId"],
+ "tags": ["defaults"],
+ "weight": 2
+ },
+ "web_services.adapters.launchpad_api.launcher_defaults": {
+ "dataType": "entity_data",
+ "requestPath": "/games/{id}",
+ "dataSelector": "defaults",
+ "entityType": "launcher",
+ "requiredParams": ["id"],
+ "tags": ["defaults"],
+ "weight": 5
+ },
+ "web_services.adapters.launchpad_api.launcher_list": {
+ "dataType": "entity_list",
+ "requestPath": "/game-keys",
+ "entityType": "launcher"
+ },
+ "web_services.adapters.launchpad_api.launcher_lookup": {
+ "dataType": "entity_lookup",
+ "requestPath": "/lookup/{id}/{platformId}",
+ "dataSelector": "id",
+ "entityType": "launcher",
+ "requiredParams": ["id"]
+ },
+ "web_services.adapters.launchpad_api.launcher_platform_defaults": {
+ "dataType": "entity_data",
+ "requestPath": "/platforms/{platformId}",
+ "dataSelector": "data.launcherDefaults",
+ "entityType": "launcher",
+ "requiredParams": ["platformId"],
+ "tags": ["defaults"],
+ "weight": 2
+ },
+ "web_services.adapters.launchpad_api.launcher_process_launcher_defaults": {
+ "dataType": "entity_data",
+ "requestPath": "/games/{id}",
+ "dataSelector": "launcherProcessDefaults",
+ "entityType": "launcher_process",
+ "requiredParams": ["id"],
+ "tags": ["defaults"],
+ "weight": 5
+ },
+ "web_services.adapters.launchpad_api.launcher_process_platform_defaults": {
+ "dataType": "entity_data",
+ "requestPath": "/platforms/{platformId}",
+ "dataSelector": "data.launcherProcessDefaults",
+ "entityType": "launcher_process",
+ "requiredParams": ["platformId"],
+ "tags": ["defaults"],
+ "weight": 2
},
- "auth_provider.launchpad_api": {
- "class": "LaunchpadApiAuthProvider",
- "arguments": ["@{App}", "@state.app"]
+ "web_services.adapters.launchpad_api.platform_defaults": {
+ "dataType": "entity_data",
+ "requestPath": "/platforms/{id}",
+ "dataSelector": "data.defaults",
+ "entityType": "platform",
+ "requiredParams": ["id"],
+ "tags": ["defaults"]
},
- "cache_state.api": {
+ "web_services.adapters.launchpad_api.platform_list": {
+ "dataType": "entity_list",
+ "requestPath": "/platforms",
+ "entityType": "platform"
+ },
+ "web_services.adapters.launchpad_api.release_info": {
+ "dataType": "release_info",
+ "requestPath": "/release-info/{version}",
+ "cacheMaxAge": 1800
+ },
+ "web_services.adapters.launchpad_api.submit_error": {
+ "dataType": "error_submission",
+ "requestPath": "/submit-error",
+ "cacheResponse": false,
+ "readAllow": false,
+ "createAllow": true,
+ "createAuth": false
+ },
+ "web_services.adapters.launchpad_api.submit_feedback": {
+ "dataType": "feedback_submission",
+ "requestPath": "/submit-feedback",
+ "cacheResponse": false,
+ "readAllow": false,
+ "createAllow": true,
+ "createAuth": false
+ },
+ "web_services.providers.launchpad_api": {
+ "name": "Launchpad API",
+ "EndpointUrl": "https://api.launchpad.games/v1",
+ "AuthenticationEndpointUrl": "https://securetoken.googleapis.com/v1",
+ "AuthenticationRefreshPath": "token",
+ "IconSrc": "logo",
+ "SupportsAuthentication": true,
+ "Authenticator": "jwt",
+ "AppKey": "AIzaSyCbwzOWJjTft77P96dV5VB3dAx9TjdDowQ",
+ "LoginWindow": "LaunchpadLoginWindow"
+ },
+ "web_services.services.launchpad_api": {
+ "name": "Launchpad API",
+ "Provider": "launchpad_api"
+ }
+ },
+ "services": {
+ "cache_state.launchpad_api": {
"class": "CacheState",
"arguments": ["@{App}", "@@config.cache_dir", "API.json"]
},
- "cache.api": {
+ "cache.launchpad_api": {
"class": "FileCache",
- "arguments": ["@{App}", "@cache_state.api", "@@config.cache_dir", "API"]
+ "arguments": ["@{App}", "@cache_state.launchpad_api", "@@config.cache_dir", "API"]
+ },
+ "event_subscriber.launchpad_api": {
+ "class": "LaunchpadApiSubscriber",
+ "arguments": ["@{}"],
+ "tags": ["event_subscriber"]
}
- },
- "parameters": {
- "config.data_source_key": "api",
- "config.api_endpoint": "https://api.launchpad.games/v1",
- "config.api_authentication": true,
- "config.api_auto_login": false
}
}
diff --git a/Lib/Shared/Modules/WebServices/ComponentManager/WebServiceAdapterManager.ahk b/Lib/Shared/Modules/WebServices/ComponentManager/WebServiceAdapterManager.ahk
new file mode 100644
index 00000000..b4e81fd3
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/ComponentManager/WebServiceAdapterManager.ahk
@@ -0,0 +1,287 @@
+class WebServiceAdapterManager {
+ container := ""
+ parameterPrefix := ""
+ adapterFactory := ""
+ entityTypeMgr := ""
+ eventMgr := ""
+ adapters := Map()
+
+ __New(container, parameterPrefix, adapterFactory, entityTypeMgr, eventMgr) {
+ this.container := container
+ this.parameterPrefix := parameterPrefix
+ this.adapterFactory := adapterFactory
+ this.entityTypeMgr := entityTypeMgr
+ this.eventMgr := eventMgr
+ }
+
+ AdapterRequest(params, filters, operation := "read", multiple := false, webService := "") {
+ if (!params) {
+ params := Map()
+ }
+
+ if (!filters) {
+ filters := Map()
+ }
+
+ if (Type(filters) == "String") {
+ filters := Map("dataType", filters)
+ }
+
+ results := Map()
+
+ for adapterKey, adapter in this.GetAdapters(filters, operation, 0, webService) {
+ result := adapter.SendRequest(operation, params)
+
+ if (result) {
+ if (!multiple) {
+ results := result
+
+ break
+ }
+
+ results[adapterKey] := result
+ }
+
+ if (IsNumber(multiple) && results.Count >= multiple) {
+ break
+ }
+ }
+
+ return results
+ }
+
+ HasAdapters(filters := "", operation := "", webService := "") {
+ return !!(this.GetAdapterIds(filters, operation, 1, webService).Length)
+ }
+
+ GetAdapters(filters := "", operation := "", limit := 0, webService := "") {
+ adapterIds := this.GetAdapterIds(filters, operation, limit, webService)
+
+ adapters := Map()
+
+ for , adapterId in adapterIds {
+ adapters[adapterId] := this.GetAdapter(adapterId)
+ }
+
+ return adapters
+ }
+
+ GetAdapter(id) {
+ adapter := ""
+
+ if (this.adapters.Has(id)) {
+ adapter := this.adapters[id]
+ }
+
+ if (!adapter && InStr(id, ".")) {
+ idParts := StrSplit(id, ".")
+ webServiceId := idParts[1]
+ adapterKey := idParts[2]
+
+ webService := this.entityTypeMgr.GetManager("web_service")[webServiceId]
+
+ if (webService["Enabled"]) {
+ param := this.parameterPrefix . webService["Provider"]["id"] . "." . adapterKey
+
+ if (this.container.HasParameter(param)) {
+ adapter := this.adapterFactory.CreateWebServiceAdapter(webService, this.container.GetParameter(param))
+ this.adapters[id] := adapter
+ }
+ }
+ }
+
+ return adapter
+ }
+
+ HasAdapter(id) {
+ exists := this.adapters.Has(id)
+
+ if (!exists) {
+ idParts := StrSplit(id, ".")
+ webServiceId := idParts[1]
+ adapterKey := idParts[2]
+ webService := this.entityTypeMgr.GetManager("web_service")[webServiceId]
+ param := this.parameterPrefix . webService["Provider"]["id"] . "." . id
+ exists := this.container.HasParameter(param)
+ }
+
+ return exists
+ }
+
+ GetAdapterIds(filters := "", operation := "", limit := 0, webService := "") {
+ if (!filters) {
+ filters := Map()
+ }
+
+ if (Type(filters) == "String") {
+ filters := Map("dataType", filters)
+ }
+
+ adapterIds := []
+ weights := this._getFilterWeights(filters)
+
+ for webServiceId, webService in this._getWebServicesForOperation(webService) {
+ providerId := webService["Provider"]["id"]
+ paramKey := "web_services.adapters." . providerId
+
+ if (this.container.HasParameter(paramKey)) {
+ adapterData := this.container.GetParameter(this.parameterPrefix . providerId)
+
+ for weightIndex, weight in weights {
+ filters["weight"] := weight
+
+ for key, definition in adapterData {
+ adapterId := webServiceId . "." . key
+ adapter := this.GetAdapter(adapterId)
+ definition := adapter.definition
+ include := (!operation || adapter.SupportsOperation(operation))
+
+ if (include) {
+ for filterKey, filterVal in filters {
+ if (!definition.Has(filterKey)) {
+ include := false
+
+ break
+ }
+
+ include := this._filterValue(definition[filterKey], filterVal)
+
+ if (!include) {
+ break
+ }
+ }
+ }
+
+ if (include) {
+ adapterIds.Push(adapterId)
+
+ if (limit && adapterIds.Length >= limit) {
+ break 2
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return adapterIds
+ }
+
+ _getWebServicesForOperation(webService) {
+ webServices := ""
+
+ if (webService) {
+ webServices := Type(webService == "String") ? Map(webService["id"], webService) : webService
+ } else {
+ webServices := this.entityTypeMgr.GetManager("web_service")
+ .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES)
+ .Condition(IsTrueCondition(), "Enabled")
+ .Execute()
+ }
+
+ return webServices
+ }
+
+ _getFilterWeights(filters) {
+ weights := filters.Has("weight")
+ ? filters["weight"]
+ : ""
+
+ if (!weights) {
+ weights := []
+
+ startingWeight := -10
+ maxWeight := 10
+
+ Loop (maxWeight - startingWeight) {
+ weights.Push(startingWeight + A_Index - 1)
+ }
+ }
+
+ if (Type(weights) == "String") {
+ weights := [weights]
+ }
+
+ return weights
+ }
+
+ _filterArrayValues(definitionArray, filterArray) {
+ include := !filterArray || !!definitionArray
+
+ if (include) {
+ if (Type(filterArray) == "String") {
+ filterArray := [filterArray]
+ }
+
+ if (Type(definitionArray) == "String") {
+ definitionArray := [definitionArray]
+ }
+
+ for , val in filterArray {
+ definitionHasVal := false
+
+ for , definitionVal in definitionArray {
+ definitionHasVal := this._filterValue(definitionVal, val)
+
+ if (definitionVal == val) {
+ definitionHasVal := true
+
+ break
+ }
+ }
+
+ if (!definitionHasVal) {
+ include := false
+
+ break
+ }
+ }
+ }
+
+ return include
+ }
+
+ _filterMapValues(definitionMap, filterMap) {
+ include := !filterMap || !!definitionMap
+
+ if (include) {
+ if (Type(filterMap) == "String") {
+ filterMap := [filterMap]
+ }
+
+ if (Type(definitionMap) == "String") {
+ definitionMap := [definitionMap]
+ }
+
+ for key, val in filterMap {
+ exists := definitionMap.Has(key)
+
+ if (exists) {
+ exists := this._filterValue(definitionMap[key], val)
+ }
+
+ if (!exists) {
+ include := false
+
+ break
+ }
+ }
+ }
+
+ return include
+ }
+
+ _filterValue(definitionVal, filterVal) {
+ include := false
+
+ if (Type(filterVal) == "Array") {
+ include := this._filterArrayValues(definitionVal, filterVal)
+ } else if (Type(filterVal) == "Map") {
+ include := this._filterMapValues(definitionVal, filterVal)
+ } else {
+ include := (definitionVal == filterVal)
+ }
+
+ return include
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk
new file mode 100644
index 00000000..d0caf51b
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk
@@ -0,0 +1,309 @@
+class WebServiceEntity extends FieldableEntity {
+ cacheObj := ""
+ stateObj := ""
+ persistentStateObj := ""
+ statusIndicators := []
+ isWebServiceEntity := true
+
+ Authenticated {
+ get => this.IsAuthenticated()
+ }
+
+ UserId {
+ get => this.AuthData["user_id"]
+ }
+
+ AuthData[key] {
+ get => this.GetAuthData(key, true)
+ set => this.SetAuthData(key, value)
+ }
+
+ __New(id, entityTypeId, container, cacheObj, stateObj, persistentStateObj, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer, autoLoad := true, parentEntity := "", parentEntityStorage := false) {
+ this.cacheObj := cacheObj
+ this.stateObj := stateObj
+ this.persistentStateObj := persistentStateObj
+
+ super.__New(id, entityTypeId, container, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer, autoLoad, parentEntity, parentEntityStorage)
+ }
+
+ static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, autoLoad := true, parentEntity := "", parentEntityStorage := false) {
+ className := this.Prototype.__Class
+
+ return %className%(
+ id,
+ entityTypeId,
+ container,
+ container.Get("cache.web_services"),
+ container.Get("state.web_services_tmp"),
+ container.Get("state.web_services"),
+ container.Get("entity_field_factory." . entityTypeId),
+ container.Get("entity_widget_factory." . entityTypeId),
+ eventMgr,
+ storageObj,
+ idSanitizer,
+ autoLoad,
+ parentEntity,
+ parentEntityStorage
+ )
+ }
+
+ BaseFieldDefinitions() {
+ definitions := super.BaseFieldDefinitions()
+
+ if (this.idVal == "launchpad_api" && definitions.Has("name")) {
+ definitions["name"]["editable"] := false
+ }
+
+ definitions["Provider"] := Map(
+ "type", "entity_reference",
+ "entityType", "web_service_provider",
+ "required", true,
+ "editable", false
+ )
+
+ definitions["AutoLogin"] := Map(
+ "type", "boolean",
+ "description", "Automatically authenticate with this service when Launchpad starts.",
+ "required", false,
+ "default", (this.idVal == "launchpad_api")
+ )
+
+ definitions["Enabled"] := Map(
+ "type", "boolean",
+ "required", false,
+ "default", true
+ )
+
+ definitions["StatusIndicator"] := Map(
+ "type", "boolean",
+ "required", false,
+ "default", (this.idVal == "launchpad_api")
+ )
+
+ definitions["StatusIndicatorExpanded"] := Map(
+ "type", "boolean",
+ "required", false,
+ "default", (this.idVal == "launchpad_api")
+ )
+
+ definitions["ResponseCache"] := Map(
+ "type", "boolean",
+ "required", false,
+ "default", true
+ )
+
+ definitions["ResponseCacheDefaultExpireSeconds"] := Map(
+ "title", "Response Cache - Default Expiration (seconds)",
+ "type", "string",
+ "required", false,
+ "default", 3600
+ )
+
+ return definitions
+ }
+
+ GetStatusIndicators() {
+ return this.statusIndicators
+ }
+
+ AddStatusIndicator(statusIndicatorCtl) {
+ this.statusIndicators.Push(statusIndicatorCtl)
+ }
+
+ UpdateStatusIndicators() {
+ for , statusIndicatorCtl in this.statusIndicators {
+ statusIndicatorCtl.UpdateStatusIndicator()
+ }
+ }
+
+ IsAuthenticated() {
+ isAuthenticated := false
+
+ if (this["Provider"] && this["Provider"]["SupportsAuthentication"]) {
+ isAuthenticated := this["Provider"]["Authenticator"].IsAuthenticated(this)
+ }
+
+ return isAuthenticated
+ }
+
+ Login() {
+ if (this["Provider"] && this["Provider"]["SupportsAuthentication"]) {
+ this["Provider"]["Authenticator"].Login(this)
+ }
+ }
+
+ Logout() {
+ if (this["Provider"] && this["Provider"]["SupportsAuthentication"]) {
+ this["Provider"]["Authenticator"].Logout(this)
+ }
+ }
+
+ Request(path, method := "", data := "", useAuthentication := -1, cacheResponse := true) {
+ if (!method) {
+ method := this["Provider"]["DefaultMethod"]
+ }
+
+ if (useAuthentication == -1) {
+ useAuthentication := this["Provider"]["AuthenticateRequestsByDefault"]
+ }
+
+ return BasicWebServiceRequest(this.eventMgr, this, this.cacheObj, method, path, data, useAuthentication, cacheResponse)
+ }
+
+ GetAuthData(key := "", includePersistent := true) {
+ val := this._getStateData(this.stateObj, key)
+
+ if (!val && includePersistent) {
+ val := this._getStateData(this.persistentStateObj, key)
+ }
+
+ return val
+ }
+
+ SetAuthData(keyOrMap, value, persist := false) {
+ result := this._setStateData(this.stateObj, keyOrMap, value)
+
+ if (persist) {
+ this._setStateData(this.persistentStateObj, keyOrMap, value)
+ }
+
+ return this
+ }
+
+ ResetAuthData(newData := "", persist := false) {
+ if (!newData) {
+ newData := Map()
+ }
+
+ if (!newData.Has("authenticated")) {
+ newData["authenticated"] := false
+ }
+
+ this._createStateParents(this.stateObj)
+ this.stateObj.State["WebServices"][this.Id]["AuthData"] := newData
+ this.stateObj.SaveState()
+
+ if (persist) {
+ this._createStateParents(this.persistentStateObj)
+ this.persistentStateObj.State["WebServices"][this.Id]["AuthData"] := Map(
+ "authenticated", newData["authenticated"]
+ )
+ }
+
+ return this
+ }
+
+ DeleteAuthData(key, persist := false) {
+ this._deleteStateData(this.stateObj, key)
+
+ if (persist) {
+ this._deleteStateData(this.persistentStateObj, key)
+ }
+
+ return this
+ }
+
+ _getStateData(stateObj, key := "") {
+ save := this._createStateParents(stateObj)
+
+ if (save) {
+ stateObj.SaveState()
+ }
+
+ authData := stateObj.State["WebServices"][this.Id]["AuthData"]
+
+ if (key) {
+ authData := (authData.Has(key) ? authData[key] : "")
+ }
+
+ return authData
+ }
+
+ _setStateData(stateObj, key, value) {
+ this._createStateParents(stateObj)
+ stateObj.State["WebServices"][this.Id]["AuthData"][key] := value
+ stateObj.SaveState()
+
+ return this
+ }
+
+ _deleteStateData(stateObj, key) {
+ created := this._createStateParents(stateObj)
+ save := created
+
+ if (!created) {
+ parent := this._getStateData(stateObj)
+
+ if (HasBase(parent, Map.Prototype) && parent.Has(key)) {
+ parent.Delete(key)
+ save := true
+ }
+ }
+
+ if (save) {
+ stateObj.SaveState()
+ }
+
+ return this
+ }
+
+ _createStateParents(stateObj) {
+ modified := false
+
+ if (!stateObj.State.Has("WebServices")) {
+ stateObj.State["WebServices"] := Map()
+ modified := true
+ }
+
+ if (!stateObj.State["WebServices"].Has(this.Id)) {
+ stateObj.State["WebServices"][this.Id] := Map()
+ modified := true
+ }
+
+ if (!stateObj.State["WebServices"][this.Id].Has("AuthData")) {
+ stateObj.State["WebServices"][this.Id]["AuthData"] := Map()
+ modified := true
+ }
+
+ return modified
+ }
+
+ GetStatusInfo() {
+ ; @todo fix this data
+ statusText := "Not logged in"
+ imgPath := ""
+ email := ""
+
+ if (this.Authenticated) {
+ email := this.AuthData["email"]
+
+ if (email) {
+ statusText := email
+ } else {
+ statusText := "Logged in"
+ }
+
+ imgPath := this.AuthData["photo"]
+
+ if (SubStr(imgPath, 1, 4) == "http") {
+ cachePath := "account--profile.jpg"
+ imgPath := this.app["manager.cache"]["file"].GetCachedDownload(cachePath, imgPath)
+ }
+ }
+
+ return Map("name", statusText, "email", email, "photo", imgPath)
+ }
+
+ ShowAccountDetails() {
+ accountResult := this.container["manager.gui"].Dialog(Map(
+ "type", "AccountInfoWindow",
+ "ownerOrParent", this.guiId,
+ "child", true,
+ "webService", this
+ ))
+
+ if (accountResult == "OK" || accountResult == "Logout" || accountResult == "Login") {
+ this.UpdateStatusIndicators()
+ }
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk
new file mode 100644
index 00000000..5c54ae3a
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk
@@ -0,0 +1,92 @@
+class WebServiceProviderEntity extends FieldableEntity {
+ isWebServiceEntity := true
+
+ BaseFieldDefinitions() {
+ definitions := super.BaseFieldDefinitions()
+
+ definitions["EndpointUrl"] := Map(
+ "default", "",
+ "required", true
+ )
+
+ definitions["AuthenticationEndpointUrl"] := Map(
+ "default", "",
+ "required", false
+ )
+
+ definitions["AuthenticationRefreshPath"] := Map(
+ "default", "",
+ "required", false
+ )
+
+ definitions["IconSrc"] := Map(
+ "type", "icon_file",
+ "default", "webhook",
+ "required", true
+ )
+
+ definitions["SupportsAuthentication"] := Map(
+ "type", "boolean",
+ "required", false,
+ "default", false
+ )
+
+ definitions["Authenticator"] := Map(
+ "type", "service_reference",
+ "servicePrefix", "web_services_authenticator.",
+ "default", "",
+ "required", false
+ )
+
+ definitions["DefaultMethod"] := Map(
+ "default", "GET",
+ "required", false
+ )
+
+ definitions["AuthenticateRequestsByDefault"] := Map(
+ "type", "boolean",
+ "default", false,
+ "required", false
+ )
+
+ definitions["LoginWindow"] := Map(
+ "default", "",
+ "required", false
+ )
+
+ definitions["AppKey"] := Map(
+ "default", "",
+ "required", false
+ )
+
+ return definitions
+ }
+
+ Url(path, queryParams := "") {
+ if (InStr(path, "/") != 1) {
+ path := "/" . path
+ }
+
+ return UrlObj(this["EndpointUrl"] . path)
+ .AddQueryParams(queryParams)
+ }
+
+ FullPath(path) {
+ url := this.Url(path)
+ return url.Path
+ }
+
+ GetAuthenticationRefreshUrl(queryParams := "") {
+ endpointUrl := this["AuthenticationEndpointUrl"]
+ ? this["AuthenticationEndpointUrl"]
+ : this["EndpointUrl"]
+ refreshPath := this["AuthenticationRefreshPath"]
+
+ if (refreshPath && InStr(refreshPath, "/") != 1) {
+ refreshPath := "/" . refreshPath
+ }
+
+ return UrlObj(endpointUrl . refreshPath)
+ .AddQueryParams(queryParams)
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/Event/WebServicesEntityDataParamsEvent.ahk b/Lib/Shared/Modules/WebServices/Event/WebServicesEntityDataParamsEvent.ahk
new file mode 100644
index 00000000..ad6ba482
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/Event/WebServicesEntityDataParamsEvent.ahk
@@ -0,0 +1,20 @@
+class WebServicesEntityDataParamsEvent extends EntityEvent {
+ _webService := ""
+ _params := ""
+
+ __New(eventName, entityTypeId, entityObj, webService, params) {
+ this._webService := webService
+ this._params := params
+
+ super.__New(eventName, entityTypeId, entityObj)
+ }
+
+ WebService {
+ get => this._webService
+ }
+
+ Params {
+ get => this._params
+ set => this._params := value
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/Event/WebServicesRequestEvent.ahk b/Lib/Shared/Modules/WebServices/Event/WebServicesRequestEvent.ahk
new file mode 100644
index 00000000..d52a8c42
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/Event/WebServicesRequestEvent.ahk
@@ -0,0 +1,17 @@
+class WebServicesRequestEvent extends EventBase {
+ _requestObj := ""
+
+ __New(eventName, requestObj) {
+ this._requestObj := requestObj
+
+ super.__New(eventName)
+ }
+
+ Request {
+ get => this.requestObj
+ }
+
+ HttpReq {
+ get => this.Request.GetHttpReq()
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/Event/WebServicesResponseEvent.ahk b/Lib/Shared/Modules/WebServices/Event/WebServicesResponseEvent.ahk
new file mode 100644
index 00000000..80010b44
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/Event/WebServicesResponseEvent.ahk
@@ -0,0 +1,20 @@
+class WebServicesResponseEvent extends EventBase {
+ _requestObj := ""
+ _responseObj := ""
+
+ __New(eventName, requestObj, responseObj) {
+ this._requestObj := requestObj
+ this._responseObj := responseObj
+
+ super.__New(eventName)
+ }
+
+ Request {
+ get => this._requestObj
+ }
+
+ Response {
+ get => this._responseObj
+ set => this._responseObj := value
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk b/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk
new file mode 100644
index 00000000..3e7a0598
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk
@@ -0,0 +1,322 @@
+class WebServicesEventSubscriber extends EventSubscriberBase {
+ adapterMgr := ""
+
+ __New(container, adapterMgr) {
+ this.adapterMgr := adapterMgr
+
+ super.__New(container)
+ }
+
+ GetEventSubscribers() {
+ return Map(
+ Events.APP_POST_STARTUP, [
+ ObjBindMethod(this, "OnPostStartup")
+ ],
+ Events.APP_MENU_ITEMS_LATE, [
+ ObjBindMethod(this, "OnMenuItemsLate")
+ ],
+ Events.APP_MENU_PROCESS_RESULT, [
+ ObjBindMethod(this, "OnMenuProcessResult")
+ ],
+ EntityEvents.ENTITY_DATA_LAYERS, [
+ ObjBindMethod(this, "EntityDataLayers")
+ ],
+ EntityEvents.ENTITY_LAYER_SOURCES, [
+ ObjBindMethod(this, "EntityLayerSources")
+ ],
+ Events.APP_GET_RELEASE_INFO, [
+ ObjBindMethod(this, "GetReleaseInfo")
+ ],
+ EntityEvents.ENTITY_FIELD_GROUPS, [
+ ObjBindMethod(this, "EntityFieldGroups")
+ ],
+ EntityEvents.ENTITY_FIELD_DEFINITIONS, [
+ ObjBindMethod(this, "EntityFieldDefinitions")
+ ],
+ EntityEvents.ENTITY_DETECT_VALUES, [
+ ObjBindMethod(this, "EntityDetectValues")
+ ],
+ EntityEvents.ENTITY_LIST_ENTITIES, [
+ ObjBindMethod(this, "ListEntities")
+ ]
+ )
+ }
+
+ OnPostStartup(event, extra, eventName, hwnd) {
+ webServices := this.container["entity_manager.web_service"]
+ .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES)
+ .Condition(IsTrueCondition(), "Enabled")
+ .Condition(IsTrueCondition(), "AutoLogin")
+ .Execute()
+
+ for key, webService in webServices {
+ webService.Login()
+ }
+ }
+
+ OnMenuItemsLate(event, extra, eventName, hwnd) {
+ event.MenuItems.Push(Map(
+ "label", "Provide &Feedback",
+ "name", "ProvideFeedback"
+ ))
+ }
+
+ OnMenuProcessResult(event, extra, eventName, hwnd) {
+ if (!event.IsFinished) {
+ if (event.Result == "ProvideFeedback") {
+ this.container["manager.gui"].Dialog(Map("type", "FeedbackWindow"))
+ event.IsFinished := true
+ }
+ }
+ }
+
+ _getEntityLayerKey(webService, adapterId) {
+ return webService["id"] . "." . adapterId
+ }
+
+ EntityDataLayers(event, extra, eventName, hwnd) {
+ if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) {
+ return
+ }
+
+ webServices := this.container["entity_manager.web_service"]
+ .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES)
+ .Condition(IsTrueCondition(), "Enabled")
+ .Execute()
+
+ for webServiceId, webService in webServices {
+ adapterIds := this.adapterMgr.GetAdapterIds(Map(
+ "dataType", "entity_data",
+ "entityType", event.EntityTypeId,
+ "tags", "defaults"
+ ), "", 0, webService)
+
+ for , adapterId in adapterIds {
+ adapter := this.adapterMgr.GetAdapter(adapterId)
+ layerExists := false
+ layerKey := this._getEntityLayerKey(webService, adapterId)
+
+ for , existingLayerKey in event.Layers {
+ if (existingLayerKey == layerKey) {
+ layerExists := true
+
+ break
+ }
+ }
+
+ if (!layerExists) {
+ event.Layers.Push(layerKey)
+ }
+ }
+ }
+ }
+
+ EntityLayerSources(event, extra, eventName, hwnd) {
+ if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) {
+ return
+ }
+
+ webServices := this.container["entity_manager.web_service"]
+ .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES)
+ .Condition(IsTrueCondition(), "Enabled")
+ .Execute()
+
+ for webServiceId, webService in webServices {
+ adapters := this.adapterMgr.GetAdapters(Map(
+ "dataType", "entity_data",
+ "entityType", event.EntityTypeId
+ ), "", 0, webService)
+
+ paramsEvent := WebServicesEntityDataParamsEvent(
+ WebServicesEvents.ENTITY_DATA_PARAMS,
+ event.EntityTypeId,
+ event.Entity,
+ webService,
+ Map("id", event.Entity.Id)
+ )
+ this.container["manager.event"].DispatchEvent(paramsEvent)
+
+ for key, adapter in adapters {
+ layerKey := this._getEntityLayerKey(webService, key)
+
+ if (!event.LayerSources.Has(layerKey)) {
+ event.LayerSources[layerKey] := WebServiceAdapterLayerSource(adapter, paramsEvent.Params)
+ }
+ }
+ }
+ }
+
+ GetReleaseInfo(event, extra, eventName, hwnd) {
+ if (!event.ReleaseInfo.Count && this.container.GetApp().Version != "{{VERSION}}") {
+ releaseInfo := this.adapterMgr.AdapterRequest("", "release_info")
+
+ if (releaseInfo && releaseInfo.Count) {
+ event.ReleaseInfo := releaseInfo
+ }
+ }
+ }
+
+ EntityFieldGroups(event, extra, eventName, hwnd) {
+ if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) {
+ return
+ }
+
+ if (!event.FieldGroups.Has("web_services")) {
+ webServices := this.container["entity_manager.web_service"]
+ .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES)
+ .Condition(IsTrueCondition(), "Enabled")
+ .Execute()
+
+ addGroup := false
+
+ for key, webService in webServices {
+ filters := Map(
+ "dataType", "entity_data",
+ "entityType", event.EntityTypeId
+ )
+ operation := "read"
+
+ if (this.adapterMgr.HasAdapters(filters, operation, webService)) {
+ addGroup := true
+
+ break
+ }
+ }
+
+ if (addGroup) {
+ event.FieldGroups["web_services"] := Map(
+ "name", "Web Services",
+ "weight", 100
+ )
+ }
+ }
+ }
+
+ EntityFieldDefinitions(event, extra, eventName, hwnd) {
+ if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) {
+ return
+ }
+
+ webServices := this.container["entity_manager.web_service"]
+ .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES)
+ .Condition(IsTrueCondition(), "Enabled")
+ .Execute()
+
+ for key, webService in webServices {
+ filters := Map(
+ "dataType", "entity_data",
+ "entityType", event.EntityTypeId
+ )
+ operation := "read"
+
+ if (this.adapterMgr.HasAdapters(filters, operation, webService)) {
+ event.FieldDefinitions["web_service_" . webService["id"] . "_ref"] := Map(
+ "title", webService["name"] . " Reference",
+ "description", "The key that is used to look up the entity's data from the " . webService["name"] . " web service.",
+ "help", "It defaults to the entity ID, but it can be overridden by setting this value.`n`nAddtionally, multiple copies of the same entity can exist by giving them different IDs but using the same " . webService["name"] . " reference.",
+ "group", "web_services",
+ "processValue", false,
+ "modes", Map("simple", Map("formField", false))
+ )
+
+ break
+ }
+ }
+ }
+
+ EntityDetectValues(event, extra, eventName, hwnd) {
+ if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) {
+ return
+ }
+
+ webServices := this.container["entity_manager.web_service"]
+ .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES)
+ .Condition(IsTrueCondition(), "Enabled")
+ .Execute()
+
+ for key, webService in webServices {
+ fieldId := "web_service_" . webService["id"] . "_ref"
+ filters := Map(
+ "dataType", "entity_data",
+ "entityType", event.EntityTypeId
+ )
+
+ if (
+ this.adapterMgr.HasAdapters(filters, "read", webService)
+ && (!event.Values.Has(fieldId) || !event.Values[fieldId])
+ && event.Entity.HasField(fieldId)
+ && (!event.Entity.RawData.Has(fieldId) || !event.Entity.RawData[fieldId])
+ ) {
+ paramsEvent := WebServicesEntityDataParamsEvent(
+ WebServicesEvents.ENTITY_DATA_PARAMS,
+ event.EntityTypeId,
+ event.Entity,
+ webService,
+ Map("id", event.Entity.Id)
+ )
+ this.container["manager.event"].DispatchEvent(paramsEvent)
+
+ result := this.adapterMgr.AdapterRequest(
+ paramsEvent.Params,
+ Map(
+ "dataType", "entity_lookup",
+ "entityType", event.EntityTypeId
+ ),
+ "read",
+ false,
+ webService
+ )
+
+ if (!result || Type(result) != "String") {
+ result := "" ; TODO decide whether to default to blank or the entity ID
+ }
+
+ event.Values[fieldId] := result
+ }
+ }
+ }
+
+ ListEntities(event, extra, eventName, hwnd) {
+ if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") {
+ return
+ }
+
+ if (event.includeExtended) {
+ entityMgr := this.container["entity_manager." . event.EntityTypeId]
+ results := this.adapterMgr.AdapterRequest("", Map(
+ "dataType", "entity_list",
+ "entityType", event.EntityTypeId
+ ), "read", true)
+
+ if (results && HasBase(results, Array.Prototype)) {
+ managedIds := event.includeManaged
+ ? []
+ : entityMgr.EntityQuery(EntityQuery.RESULT_TYPE_IDS).Execute()
+
+ for index, id in results {
+ exists := false
+
+ for , existingId in event.EntityList {
+ if (existingId == id) {
+ exists := true
+ break
+ }
+ }
+
+ if (!exists && !event.includeManaged) {
+ for , managedId in managedIds {
+ if (managedId == id) {
+ exists := true
+ break
+ }
+ }
+ }
+
+ if (!exists) {
+ event.EntityList.Push(id)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/Events/WebServicesEvents.ahk b/Lib/Shared/Modules/WebServices/Events/WebServicesEvents.ahk
new file mode 100644
index 00000000..bde6b479
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/Events/WebServicesEvents.ahk
@@ -0,0 +1,8 @@
+class WebServicesEvents {
+ static HTTP_REQ_ALTER := 0x4200
+ static REQUEST_PRESEND := 0x4210
+ static CACHED_RESPONSE_CREATED := 0x4215
+ static HTTP_RESPONSE_CREATED := 0x4217
+ static RESPONSE_ALTER := 0x4220
+ static ENTITY_DATA_PARAMS := 0x4225
+}
diff --git a/Lib/Shared/Modules/WebServices/Factory/WebServiceAdapterFactory.ahk b/Lib/Shared/Modules/WebServices/Factory/WebServiceAdapterFactory.ahk
new file mode 100644
index 00000000..d1c423a4
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/Factory/WebServiceAdapterFactory.ahk
@@ -0,0 +1,41 @@
+class WebServiceAdapterFactory {
+ container := ""
+
+ __New(container) {
+ this.container := container
+ }
+
+ CreateWebServiceAdapter(webService, definition) {
+ adapterTypes := this.container.GetParameter("web_services.adapter_types")
+
+ if (!definition.Has("adapterType") || !definition["adapterType"]) {
+ definition["adapterType"] := "json"
+ }
+
+ if (adapterTypes.Has(definition["adapterType"])) {
+ defaults := adapterTypes[definition["adapterType"]]
+
+ if (Type(defaults) == "String") {
+ defaults := Map("class", defaults)
+ }
+
+ for key, val in defaults {
+ if (!definition.Has(key)) {
+ definition[key] := val
+ }
+ }
+ }
+
+ if (!definition.Has("class")) {
+ throw AppException("Adapter class not known.")
+ }
+
+ adapterClass := definition["class"]
+
+ if (!HasMethod(%adapterClass%)) {
+ throw AppException("Adapter class " . adapterClass . " was not found.")
+ }
+
+ return %adapterClass%.Create(this.container, webService, definition)
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk
new file mode 100644
index 00000000..0145f0cb
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk
@@ -0,0 +1,32 @@
+class LaunchpadLoginWindow extends FormGuiBase {
+ entityObj := ""
+ entityManager := ""
+ missingFields := Map()
+
+ GetDefaultConfig(container, config) {
+ defaults := super.GetDefaultConfig(container, config)
+ defaults["title"] := "Login"
+ defaults["text"] := "Logging in allows enhanced features such as online backup, restore, personalization, and sharing with the community.`n`nIf you'd like to log in, click the `"Get token`" button to go to the launchpad.games site to retrieve a valid login token and then paste it below."
+ defaults["buttons"] := "*&Login|&Cancel"
+ return defaults
+ }
+
+ Controls() {
+ super.Controls()
+ this.Add("ButtonControl", "xs y+m vGetAuthToken w150 h30", "Get Token")
+ this.AddHeading("Login Token")
+ this.guiObj.AddEdit("vAuthToken xs y+m r1 w" . this.windowSettings["contentWidth"] . " c" . this.themeObj.GetColor("editText"))
+ }
+
+ OnGetAuthToken(btn, info) {
+ Run("https://launchpad.games/profile")
+ }
+
+ ProcessResult(result, submittedData := "") {
+ if (result == "Login") {
+ result := this.guiObj["AuthToken"].Text
+ }
+
+ return result
+ }
+}
diff --git a/Lib/Shared/Volantis.App/Gui/Form/FeedbackWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk
similarity index 56%
rename from Lib/Shared/Volantis.App/Gui/Form/FeedbackWindow.ahk
rename to Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk
index ba27dc1c..b790b0dc 100644
--- a/Lib/Shared/Volantis.App/Gui/Form/FeedbackWindow.ahk
+++ b/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk
@@ -1,11 +1,9 @@
class FeedbackWindow extends DialogBox {
errorObj := ""
notifierObj := ""
- apiEndpointUrl := ""
__New(container, themeObj, config) {
this.notifierObj := container.Get("notifier").notifierObj
- this.apiEndpointUrl := container.Get("config.app")["api_endpoint"]
super.__New(container, themeObj, config)
}
@@ -38,20 +36,50 @@ class FeedbackWindow extends DialogBox {
SendFeedback() {
global appVersion
- if (this.apiEndpointUrl) {
- endpoint := this.apiEndpointUrl . "/submit-feedback"
+ filters := "feedback_submission"
+ operation := "create"
+ if (
+ this.container.Has("web_services.adapter_manager")
+ && this.container["web_services.adapter_manager"].HasAdapters(filters, operation)
+ ) {
body := Map()
body["email"] := this.guiObj["Email"].Text
body["version"] := appVersion
body["feedback"] := this.guiObj["Feedback"].Text
- request := WinHttpReq(endpoint)
- response := request.Send("POST", body)
- success := !!(request.GetStatusCode() == 200)
+ results := this.container["web_services.adapter_manager"].AdapterRequest(
+ Map("data", body),
+ filters,
+ operation,
+ true
+ )
- notification := success ? "Successfully sent feedback to Volantis Development" : "Failed to send feedback to Volantis Development"
- this.notifierObj.Notify(notification, "Feedback Sent", success ? "info" : "error")
+ success := false
+
+ for adapterId, adapterResult in results {
+ if (adapterResult) {
+ success := true
+
+ break
+ }
+ }
+
+ message := ""
+
+ if (success) {
+ message := "Successfully sent feedback"
+ } else if (results.Count) {
+ message := "Failed to send feedback"
+ } else {
+ message := "No feedback adapters are enabled"
+ }
+
+ this.notifierObj.Notify(
+ message,
+ "Feedback Submission",
+ success ? "info" : "error"
+ )
}
}
}
diff --git a/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk
new file mode 100644
index 00000000..873a9596
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk
@@ -0,0 +1,87 @@
+class ManageWebServicesWindow extends ManageEntitiesWindow {
+ listViewColumns := Array("SERVICE", "PROVIDER", "USER", "AUTHENTICATED", "AUTO-LOGIN")
+
+ GetListViewData(lv) {
+ data := Map()
+
+ for key, webService in this.entityMgr {
+ data[key] := [
+ webService["name"],
+ webService["Provider"]["name"],
+ "",
+ webService.Authenticated ? "Yes" : "No",
+ webService["AutoLogin"] ? "Yes" : "No"
+ ]
+ }
+
+ return data
+ }
+
+ GetEntityIconSrc(entityObj) {
+ return entityObj["Provider"]["IconSrc"]
+ }
+
+ GetContextMenuItems(entityObj) {
+ menuItems := super.GetContextMenuItems(entityObj)
+
+ if (entityObj["Provider"]["SupportsAuthentication"]) {
+ if (entityObj.Authenticated) {
+ menuItems.InsertAt(1, Map("label", "&Logout", "name", "WebServiceLogout"))
+ } else {
+ menuItems.InsertAt(1, Map("label", "&Login", "name", "WebServiceLogin"))
+ }
+ }
+
+ return menuItems
+ }
+
+ _shouldShowButton(entityObj, buttonName) {
+ shouldShow := super._shouldShowButton(entityObj, buttonName)
+
+ if (shouldShow && buttonName == "DeleteEntity") {
+ shouldShow := entityObj.Id != "launchpad_api"
+ }
+
+ return shouldShow
+ }
+
+ ProcessContextMenuResult(result, key) {
+ if (result == "WebServiceLogout") {
+ this.Logout(key)
+ } else if (result == "WebServiceLogin") {
+ this.Login(key)
+ } else {
+ super.ProcessContextMenuResult(result, key)
+ }
+ }
+
+ Logout(key) {
+ result := this.entityMgr[key].Logout()
+
+ this.UpdateListView()
+
+ return result
+ }
+
+ Login(key) {
+ result := this.entityMgr[key].Login()
+
+ this.UpdateListView()
+
+ return result
+ }
+
+ ViewEntity(key) {
+ entityObj := this.entityMgr[key]
+ }
+
+ AddEntity() {
+ ; @todo open add wizard
+
+ this.UpdateListView()
+ }
+
+ DeleteEntity(key) {
+ entityObj := this.entityMgr[key]
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/LayerSource/WebServiceAdapterLayerSource.ahk b/Lib/Shared/Modules/WebServices/LayerSource/WebServiceAdapterLayerSource.ahk
new file mode 100644
index 00000000..c4dab738
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/LayerSource/WebServiceAdapterLayerSource.ahk
@@ -0,0 +1,51 @@
+class WebServiceAdapterLayerSource extends LayerSourceBase {
+ adapter := ""
+ params := ""
+
+ __New(adapter, params := "") {
+ if (!params) {
+ params := Map()
+ }
+
+ this.adapter := adapter
+ this.params := params
+ }
+
+ SaveData(data := "") {
+ if (this.HasData()) {
+ if (this.adapter.definition["updateAllow"]) {
+ this.adapter.UpdateData(data, this.params)
+ }
+ } else if (this.adapter.definition["createAllow"]) {
+ this.adapter.CreateData(data, this.params)
+ }
+
+ return this
+ }
+
+ LoadData() {
+ data := ""
+
+ if (this.adapter.definition["readAllow"]) {
+ data := this.adapter.ReadData(this.params)
+ }
+
+ if (!data) {
+ data := Map()
+ }
+
+ return data
+ }
+
+ HasData() {
+ return this.adapter.DataExists(this.params)
+ }
+
+ DeleteData() {
+ if (this.adapter.definitions["deleteAllow"]) {
+ this.adapter.DeleteData(this.params)
+ }
+
+ return this
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/WebServiceAdapter/FileWebServiceAdapter.ahk b/Lib/Shared/Modules/WebServices/WebServiceAdapter/FileWebServiceAdapter.ahk
new file mode 100644
index 00000000..e922134a
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/WebServiceAdapter/FileWebServiceAdapter.ahk
@@ -0,0 +1,5 @@
+class FileWebServiceAdapter extends WebServiceAdapterBase {
+ dataClass := ""
+
+ ; @todo Implement file downloading
+}
diff --git a/Lib/Shared/Modules/WebServices/WebServiceAdapter/JsonWebServiceAdapter.ahk b/Lib/Shared/Modules/WebServices/WebServiceAdapter/JsonWebServiceAdapter.ahk
new file mode 100644
index 00000000..5f63b104
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/WebServiceAdapter/JsonWebServiceAdapter.ahk
@@ -0,0 +1,3 @@
+class JsonWebServiceAdapter extends WebServiceAdapterBase {
+ dataClass := "JsonData"
+}
\ No newline at end of file
diff --git a/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk
new file mode 100644
index 00000000..8e30611e
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk
@@ -0,0 +1,352 @@
+class WebServiceAdapterBase {
+ container := ""
+ webService := ""
+ definition := ""
+ dataClass := ""
+ merger := ""
+ operationTypes := ["create", "read", "update", "delete"]
+
+ static ADAPTER_RESULT_DATA := "data"
+ static ADAPTER_RESULT_HTTP_STATUS := "httpStatus"
+ static ADAPTER_RESULT_SUCCESS := "success"
+
+ __New(container, merger, webService, definition) {
+ this.container := container
+ this.merger := merger
+ this.webService := webService
+
+ if (!definition) {
+ definition := Map()
+ }
+
+ if (!HasBase(definition, Map.Prototype)) {
+ throw AppException("Definition must be a Map-like object.")
+ }
+
+ this.definition := this.merger.Merge(this.GetDefaultDefinition(), definition)
+ }
+
+ static Create(container, webService, definition) {
+ className := this.Prototype.__Class
+
+ return %className%(
+ container,
+ container.Get("merger.list"),
+ webService,
+ definition
+ )
+ }
+
+ GetDefaultDefinition() {
+ return Map(
+ "dataType", "",
+ "adapterType", "json",
+ "requestPath", "",
+ "requestData", "",
+ "cacheResponse", true,
+ "cacheMaxAge", 186400,
+ "createAllow", false,
+ "createMethod", "POST",
+ "createAuth", true,
+ "readAllow", true,
+ "readMethod", "GET",
+ "readAuth", false,
+ "updateAllow", false,
+ "updateMethod", "POST",
+ "updateAuth", true,
+ "deleteAllow", false,
+ "deleteMethod", "PUT",
+ "deleteAuth", true,
+ "dataMap", Map(),
+ "dataSelector", "",
+ "weight", 0,
+ "entityType", "",
+ "tags", [],
+ "requiredParams", [],
+ )
+ }
+
+ SupportsOperation(operation) {
+ supported := false
+
+ for index, operationType in this.operationTypes {
+ if (operation == operationType) {
+ supported := true
+
+ break
+ }
+ }
+
+ return supported
+ }
+
+ SendRequest(operation, params := "") {
+ if (!this.SupportsOperation(operation)) {
+ throw AppException("The '" . operation . "' operation is not supported by this data adapter.")
+ }
+
+ result := ""
+ data := params.Has("data") ? params["data"] : ""
+
+ if (operation == "create") {
+ result := this.CreateData(data, params)
+ } else if (operation == "read") {
+ result := this.ReadData(params)
+ } else if (operation == "update") {
+ result := this.UpdateData(data, params)
+ } else if (operation == "delete") {
+ result := this.DeleteData(params)
+ }
+
+ return result
+ }
+
+ CreateData(data, params := "") {
+ if (!this.definition["createAllow"]) {
+ throw AppException("The 'create' operation is not allowed on this data adapter.")
+ }
+
+ response := this._request(
+ params,
+ this.definition["createMethod"],
+ data ? data : this._getData(params),
+ this.definition["createAuth"],
+ false
+ ).Send()
+
+ return this._getResult(
+ params,
+ response,
+ this._getResultType(params, WebServiceAdapterBase.ADAPTER_RESULT_SUCCESS)
+ )
+ }
+
+ _getResultType(params, default) {
+ resultType := default
+
+ if (params.Has("resultType") && params["resultType"]) {
+ resultType := params["resultType"]
+ }
+
+ return resultType
+ }
+
+ _getResult(params, response, resultType) {
+ result := ""
+
+ if (resultType == WebServiceAdapterBase.ADAPTER_RESULT_DATA) {
+ if (response.IsSuccessful()) {
+ data := response.GetResponseBody()
+
+ if (data) {
+ result := this._mapData(this._parseData(data, params), params)
+ }
+
+ }
+ } else if (resultType == WebServiceAdapterBase.ADAPTER_RESULT_HTTP_STATUS) {
+ result := response.GetHttpStatusCode()
+ } else if (resultType == WebServiceAdapterBase.ADAPTER_RESULT_SUCCESS) {
+ result := response.IsSuccessful()
+ }
+
+ return result
+ }
+
+ DataExists(params := "") {
+ if (!this.definition["readAllow"]) {
+ throw AppException("The 'read' operation is not allowed on this data adapter.")
+ }
+
+ return this._request(
+ params,
+ this.definition["readMethod"],
+ this._getData(params),
+ this.definition["readAuth"],
+ false
+ ).Send().IsSuccessful()
+ }
+
+ ReadData(params := "") {
+ if (!this.definition["readAllow"]) {
+ throw AppException("The 'read' operation is not allowed on this data adapter.")
+ }
+
+ response := this._request(
+ params,
+ this.definition["readMethod"],
+ this._getData(params),
+ this.definition["readAuth"],
+ this.definition["cacheResponse"]
+ ).Send()
+
+ return this._getResult(
+ params,
+ response,
+ this._getResultType(params, WebServiceAdapterBase.ADAPTER_RESULT_DATA)
+ )
+ }
+
+ UpdateData(data, params := "") {
+ if (!this.definition["updateAllow"]) {
+ throw AppException("The 'update' operation is not allowed on this data adapter.")
+ }
+
+ response := this._request(
+ params,
+ this.definition["updateMethod"],
+ data ? data : this._getData(params),
+ this.definition["updateAuth"],
+ false
+ ).Send()
+
+ return this._getResult(
+ params,
+ response,
+ this._getResultType(params, WebServiceAdapterBase.ADAPTER_RESULT_SUCCESS)
+ )
+ }
+
+ DeleteData(params := "") {
+ if (!this.definition["deleteAllow"]) {
+ throw AppException("The 'delete' operation is not allowed on this data adapter.")
+ }
+
+ response := this._request(
+ params,
+ this.definition["deleteMethod"],
+ this._getData(params),
+ this.definition["deleteAuth"],
+ false
+ ).Send()
+
+ return this._getResult(
+ params,
+ response,
+ this._getResultType(params, WebServiceAdapterBase.ADAPTER_RESULT_SUCCESS)
+ )
+ }
+
+ _requestPath(params) {
+ requestPath := this.definition["requestPath"]
+ isFound := true
+
+ while isFound {
+ match := ""
+ isFound := RegExMatch(requestPath, "({([^}]+)})", &match)
+
+ if (isFound) {
+ key := match[2]
+
+ replacement := (params && params.Has(key))
+ ? params[key]
+ : ""
+
+ requestPath := StrReplace(requestPath, match[1], replacement)
+ }
+ }
+
+ return requestPath
+ }
+
+ _validateParams(params) {
+ if (!params) {
+ params := Map()
+ }
+
+ valid := true
+ requiredParams := this.definition["requiredParams"]
+
+ if (requiredParams) {
+ if (Type(requiredParams) == "String") {
+ requiredParams := [requiredParams]
+ }
+
+ for , requiredParam in requiredParams {
+ if (!params.Has(requiredParam) || !params[requiredParam]) {
+ valid := false
+
+ break
+ }
+ }
+ }
+
+ return valid
+ }
+
+ _request(params, method, data, useAuthentication, cacheResponse) {
+ if (!this._validateParams(params)) {
+ throw AppException("The data adapter request was called with invalid or missing parameters.")
+ }
+
+ requestPath := this.definition["requestPath"]
+
+ for key, value in params {
+
+ }
+
+ return this.webService.Request(
+ this._requestPath(params),
+ method,
+ data,
+ useAuthentication,
+ cacheResponse
+ )
+ }
+
+ _mapData(data, params, reverse := false) {
+ if (
+ data
+ && HasBase(data, Map.Prototype)
+ && this.definition["dataMap"]
+ && HasBase(this.definition["dataMap"], Map.Prototype)
+ ) {
+ for key1, key2 in this.definition["dataMap"] {
+ oldKey := reverse ? key2 : key1
+ newKey := reverse ? key1 : key2
+
+ if (data.Has(oldKey)) {
+ data[newKey] := data[oldKey]
+ data.Delete(oldKey)
+ }
+ }
+ }
+ }
+
+ _parseData(data, params) {
+ if (data && this.dataClass) {
+ dataClass := this.dataClass
+ data := %dataClass%().FromString(&data)
+
+ if (this.definition["dataSelector"]) {
+ dataSelector := this.definition["dataSelector"]
+
+ if (Type(dataSelector) == "String") {
+ dataSelector := StrSplit(dataSelector, ".")
+ }
+
+ for index, pathPart in dataSelector {
+ if (data.Has(pathPart)) {
+ data := data[pathPart]
+ } else {
+ data := ""
+ break
+ }
+ }
+ }
+ }
+
+ return data
+ }
+
+ _getData(params, data := "") {
+ if (!data) {
+ if (params.Has("data") && params["data"]) {
+ data := params["data"]
+ } else if (this.definition["requestData"]) {
+ data := this.definition["requestData"]
+ }
+ }
+
+ return data
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk
new file mode 100644
index 00000000..ce99bdce
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk
@@ -0,0 +1,178 @@
+class JwtWebServiceAuthenticator extends WebServiceAuthenticatorBase {
+ Login(webServiceEnt, retryCount := 0) {
+ if (retryCount > this.maxRetries) {
+ this._handleLoginFailure("You have used " . retryCount . " of " . this.maxRetries + 1 . " login attempts. Canceling login.")
+ return false
+ }
+
+ authResult := ""
+
+ if (!this._hasRefreshToken(webServiceEnt)) {
+ authResult := this._reauthenticate(webServiceEnt)
+ }
+
+ success := false
+
+ if (authResult != "Cancel") {
+ if (this._hasRefreshToken(webServiceEnt)) {
+ success := this._refreshAuthentication(webServiceEnt)
+ }
+
+ if (!success) {
+ success := this.Login(webServiceEnt, retryCount + 1)
+ }
+ }
+
+ return success
+ }
+
+ Logout(webServiceEnt) {
+ webServiceEnt
+ .ResetAuthData()
+ .DeleteAuthData("auth_token", true)
+ .DeleteAuthData("refresh_token", true)
+ .DeleteAuthData("expires", true)
+ .SetAuthData("authenticated", false, true)
+
+
+ return true
+ }
+
+ RefreshAuthentication(webServiceEnt) {
+ if (this.NeedsRefresh(webServiceEnt)) {
+ this.Login(webServiceEnt)
+ }
+ }
+
+ AlterRequest(webServiceEnt, httpReqObj) {
+ bearerToken := webServiceEnt.AuthData["auth_token"]
+
+ if (bearerToken) {
+ httpReqObj.requestHeaders["Authorization"] := "Bearer " . bearerToken
+ }
+ }
+
+ _hasRefreshToken(webServiceEnt) {
+ return !!(webServiceEnt.AuthData["refresh_token"])
+ }
+
+ _reauthenticate(webServiceEnt) {
+ refreshToken := this._authenticationGui(webServiceEnt)
+
+ if (refreshToken != "Cancel") {
+ this._setRefreshToken(webServiceEnt, refreshToken)
+ }
+
+ return refreshToken
+ }
+
+ _getRefreshToken(webServiceEnt) {
+ return webServiceEnt.AuthData["refresh_token"]
+ }
+
+ _setRefreshToken(webServiceEnt, refreshToken) {
+ webServiceEnt.SetAuthData("refresh_token", refreshToken, true)
+ }
+
+ _extractAuthData(webServiceEnt, response) {
+ loginData := response.GetJsonData()
+
+ if (!loginData.Has("authenticated")) {
+ loginData["authenticated"] := !!(loginData.Has("refresh_token") && loginData["refresh_token"])
+ }
+
+ keyMap := Map(
+ "id_token", "auth_token",
+ "expires_in", "expires"
+ )
+
+ persistentKeys := [
+ "user_id",
+ "refresh_token",
+ "auth_token",
+ "access_token",
+ "authenticated",
+ "expires"
+ ]
+
+ expiresInKeys := [
+ "expires"
+ ]
+
+ skipKeys := []
+
+ for key, val in loginData {
+ if (keyMap.Has(key)) {
+ key := keyMap[key]
+ }
+
+ persist := false
+
+ for , persistKey in persistentKeys {
+ if (key == persistKey) {
+ persist := true
+ break
+ }
+ }
+
+ expires := false
+
+ for , expiresKey in expiresInKeys {
+ if (key == expiresKey) {
+ val := DateAdd(A_Now, val, "S")
+ break
+ }
+ }
+
+ skip := false
+
+ for , skipKey in skipKeys {
+ if (key == skipKey) {
+ skip := true
+ break
+ }
+ }
+
+ if (!skip) {
+ webServiceEnt.SetAuthData(key, val, persist)
+ }
+ }
+ }
+
+ _refreshAuthentication(webServiceEnt) {
+ apiKey := webServiceEnt["Provider"]["AppKey"]
+ refreshToken := webServiceEnt.AuthData["refresh_token"]
+ refreshUrl := webServiceEnt["Provider"].GetAuthenticationRefreshUrl(Map("key", apiKey))
+ response := ""
+
+ if (!apiKey) {
+ throw OperationFailedException("Missing API key for auth refresh.")
+ }
+
+ if (!refreshToken) {
+ throw OperationFailedException("Missing refresh token for auth refresh.")
+ }
+
+ if (refreshUrl) {
+ payload := Map(
+ "grant_type", "refresh_token",
+ "refresh_token", refreshToken
+ )
+
+ response := webServiceEnt.Request(refreshUrl, "POST", payload, false, false).Send()
+ }
+
+ success := response.IsSuccessful()
+
+ if (response && success) {
+ this._extractAuthData(webServiceEnt, response)
+ } else {
+ url := response.httpReqObj.url.ToString(true)
+ webServiceEnt.SetAuthData("refresh_token", "", true)
+
+ ; @todo handle common http error codes
+ }
+
+ return success
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk
new file mode 100644
index 00000000..91664f03
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk
@@ -0,0 +1,83 @@
+class WebServiceAuthenticatorBase {
+ refreshThresholdSeconds := 600
+ authenticatedStateKey := "authenticated"
+ expiresStateKey := "expires"
+ maxRetries := 3
+ guiMgr := ""
+
+ __New(guiMgr) {
+ this.guiMgr := guiMgr
+ }
+
+ Login(webServiceEnt, retryCount := 0) {
+
+ }
+
+ Logout(webServiceEnt) {
+
+ }
+
+ RefreshAuthentication(webServiceEnt) {
+
+ }
+
+ AlterRequest(webServiceEnt, httpReqObj) {
+
+ }
+
+ IsAuthenticated(webServiceEnt) {
+ auth := webServiceEnt.AuthData[this.authenticatedStateKey]
+ expired := this.AuthenticationIsExpired(webServiceEnt)
+
+ return auth && !expired
+
+ }
+
+ NeedsRefresh(webServiceEnt) {
+ needsRefresh := false
+
+ if (this.IsAuthenticated(webServiceEnt)) {
+ expires := webServiceEnt.AuthData[this.expiresStateKey]
+
+ if (expires) {
+ diff := DateDiff(A_Now, expires, "S")
+ needsRefresh := (diff >= (0 - this.refreshThresholdSeconds))
+ } else {
+ needsRefresh := true
+ }
+ } else {
+ needsRefresh := true
+ }
+
+ return needsRefresh
+ }
+
+ AuthenticationIsExpired(webServiceEnt) {
+ expired := true
+
+ if (webServiceEnt.AuthData[this.authenticatedStateKey] && webServiceEnt.AuthData[this.expiresStateKey]) {
+ expired := (DateDiff(A_Now, webServiceEnt.AuthData["expires"], "S") >= 0)
+ }
+
+ return expired
+ }
+
+ _authenticationGui(webServiceEnt) {
+ loginWindowGui := webServiceEnt["Provider"]["LoginWindow"]
+ result := ""
+
+ if (loginWindowGui) {
+ result := this.guiMgr.Dialog(Map("type", loginWindowGui))
+ }
+
+ return result
+ }
+
+ _handleLoginFailure(message) {
+ this.guiMgr.Dialog(Map(
+ "title", "Login Failure",
+ "text", message,
+ "buttons", "*&OK"
+ ))
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/WebServiceRequest/BasicWebServiceRequest.ahk b/Lib/Shared/Modules/WebServices/WebServiceRequest/BasicWebServiceRequest.ahk
new file mode 100644
index 00000000..a723ff4c
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/WebServiceRequest/BasicWebServiceRequest.ahk
@@ -0,0 +1,3 @@
+class BasicWebServiceRequest extends WebServiceRequestBase {
+
+}
\ No newline at end of file
diff --git a/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk
new file mode 100644
index 00000000..e89cfce5
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk
@@ -0,0 +1,201 @@
+class WebServiceRequestBase {
+ eventMgr := ""
+ webServiceEnt := ""
+ cacheObj := ""
+ path := ""
+ method := ""
+ data := ""
+ useAuthentication := false
+ httpReqObj := ""
+ responseObj := ""
+ cacheResponse := true
+ _url := ""
+
+ Url {
+ get => this._getUrl()
+ }
+
+ Response {
+ get => this.responseObj
+ }
+
+ __New(eventMgr, webServiceEnt, cacheObj, method := "", path := "", data := "", useAuthentication := false, cacheResponse := true) {
+ this.eventMgr := eventMgr
+ this.webServiceEnt := webServiceEnt
+ this.cacheObj := cacheObj
+
+ if (HasBase(path, UrlObj.Prototype)) {
+ this._url := path
+ path := this._url.Path
+ }
+
+ if (!method) {
+ method := "GET"
+ }
+
+ this.method := method
+
+ if (path) {
+ this.path := path
+ }
+
+ if (data) {
+ this.data := data
+ }
+
+ this.useAuthentication := useAuthentication
+ this.cacheResponse := cacheResponse
+ }
+
+ GetPath() {
+ return this.path
+ }
+
+ SetPath(path := "") {
+ this.path := path
+
+ if (this._url) {
+ this._url.Path := this.webServiceEnt["Provider"].Path(path)
+ }
+
+ return this
+ }
+
+ GetMethod() {
+ return this.method
+ }
+
+ SetMethod(method := "GET") {
+ this.method := method
+
+ return this
+ }
+
+ GetData() {
+ return this.data
+ }
+
+ SetData(data := "", clearCache := false) {
+ this.data := data
+
+ if (clearCache) {
+ this.cacheObj.RemoveItem(this.GetPath())
+ }
+
+ return this
+ }
+
+ GetUseAuthentication() {
+ return this.useAuthentication
+ }
+
+ SetUseAuthentication(useAuthentication := false) {
+ this.useAuthentication := useAuthentication
+
+ return this
+ }
+
+ GetHttpReq() {
+ if (!this.httpReqObj) {
+ this.httpReqObj := WinHttpReq(this.Url)
+ }
+
+ return this.httpReqObj
+ }
+
+ SetHttpReq(httpReqObj := "") {
+ this.httpReqObj := httpReqObj
+ }
+
+ _getUrl() {
+ if (!this._url) {
+ this._url := this.webServiceEnt["Provider"].Url(this.path)
+ }
+
+ return this._url
+ }
+
+ Send(resend := false) {
+ if (resend || !this.responseObj) {
+ if (this.RequestIsCached()) {
+ this.responseObj := this._createCachedResponse()
+ } else {
+ httpReqObj := this.GetHttpReq()
+
+ if (this.GetUseAuthentication() && this.webServiceEnt["Provider"].Has("Authenticator", false)) {
+ authenticator := this.webServiceEnt["Provider"]["Authenticator"]
+
+ if (authenticator.NeedsRefresh(this.webServiceEnt)) {
+ authenticator.RefreshAuthentication(this.webServiceEnt)
+ }
+
+ authenticator.AlterRequest(this.webServiceEnt, httpReqObj)
+ }
+
+ if (!this.cacheResponse) {
+ httpReqObj.requestHeaders["Cache-Control"] := "no-cache"
+ }
+
+ event := WebServicesRequestEvent(WebServicesEvents.HTTP_REQ_ALTER, this)
+ this.eventMgr.DispatchEvent(event)
+
+ event := WebServicesRequestEvent(WebServicesEvents.REQUEST_PRESEND, this)
+ this.eventMgr.DispatchEvent(event)
+
+ httpReqObj.Send(this.GetMethod(), this.GetData())
+ this.responseObj := this._createHttpReqResponse()
+ this._cacheResponse()
+ }
+
+ event := WebServicesResponseEvent(WebServicesEvents.RESPONSE_ALTER, this, this.responseObj)
+ this.eventMgr.DispatchEvent(event)
+
+ this.responseObj := event.Response
+ }
+
+ return this.responseObj
+ }
+
+ RequestIsCached() {
+ path := this.GetPath()
+
+ return (this.cacheObj.ItemExists(path) && !this.cacheObj.ItemNeedsUpdate(path))
+ }
+
+ _createCachedResponse() {
+ response := CachedWebServiceResponse(this.webServiceEnt, this)
+
+ event := WebServicesResponseEvent(WebServicesEvents.CACHED_RESPONSE_CREATED, this, response)
+ this.eventMgr.DispatchEvent(event)
+
+ return event.Response
+ }
+
+ _createHttpReqResponse() {
+ response := HttpReqWebServiceResponse(this.webServiceEnt, this)
+
+ event := WebServicesResponseEvent(WebServicesEvents.HTTP_RESPONSE_CREATED, this, response)
+ this.eventMgr.DispatchEvent(event)
+
+ return event.Response
+ }
+
+ _cacheResponse() {
+ if (this.responseObj && this.cacheResponse) {
+ path := this.GetPath()
+
+ if (this.responseObj.IsSuccessful()) {
+ body := this.responseObj.GetResponseBody()
+
+ if (body) {
+ this.cacheObj.WriteItem(path, body, this.responseObj.GetHttpStatusCode())
+ } else {
+ ; Response is empty, delete any existing cache for this item
+ this.cacheObj.RemoveItem(path)
+ }
+ } else if (this.responseObj.IsNotFound()) {
+ this.cacheObj.SetNotFound(path)
+ }
+ }
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/WebServiceResponse/CachedWebServiceResponse.ahk b/Lib/Shared/Modules/WebServices/WebServiceResponse/CachedWebServiceResponse.ahk
new file mode 100644
index 00000000..551b7efa
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/WebServiceResponse/CachedWebServiceResponse.ahk
@@ -0,0 +1,30 @@
+class CachedWebServiceResponse extends WebServiceResponseBase {
+ cacheObj := ""
+
+ __New(webServiceEnt, webServiceReq) {
+ this.cacheObj := webServiceEnt.cacheObj
+
+ super.__New(webServiceEnt, webServiceReq)
+ }
+
+ GetHttpStatusCode() {
+ responseCode := 0
+
+ if (this.cacheObj) {
+ responseCode := this.cacheObj.GetResponseCode(this.webServiceReq.GetPath())
+ }
+
+ return responseCode
+ }
+
+ GetResponseBody() {
+ body := ""
+ path := this.webServiceReq.GetPath()
+
+ if (this.cacheObj.ItemExists(path)) {
+ body := this.cacheObj.ReadItem(path)
+ }
+
+ return body
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/WebServiceResponse/HttpReqWebServiceResponse.ahk b/Lib/Shared/Modules/WebServices/WebServiceResponse/HttpReqWebServiceResponse.ahk
new file mode 100644
index 00000000..d7857400
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/WebServiceResponse/HttpReqWebServiceResponse.ahk
@@ -0,0 +1,17 @@
+class HttpReqWebServiceResponse extends WebServiceResponseBase {
+ httpReqObj := ""
+
+ __New(webServiceEnt, webServiceReq) {
+ this.httpReqObj := webServiceReq.GetHttpReq()
+
+ super.__New(webServiceEnt, webServiceReq)
+ }
+
+ GetHttpStatusCode() {
+ return this.httpReqObj.GetStatusCode()
+ }
+
+ GetResponseBody() {
+ return Trim(this.httpReqObj.GetResponseData())
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/WebServiceResponse/WebServiceResponseBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceResponse/WebServiceResponseBase.ahk
new file mode 100644
index 00000000..e7413bfa
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/WebServiceResponse/WebServiceResponseBase.ahk
@@ -0,0 +1,57 @@
+class WebServiceResponseBase {
+ webServiceEnt := ""
+ webServiceReq := ""
+ successCodes := [200]
+ notFoundCodes := [404]
+
+ __New(webServiceEnt, webServiceReq) {
+ this.webServiceEnt := webServiceEnt
+ this.webServiceReq := webServiceReq
+ }
+
+ GetHttpStatusCode() {
+ return ""
+ }
+
+ GetResponseBody() {
+ return ""
+ }
+
+ GetJsonData() {
+ body := this.GetResponseBody()
+
+ if (!body) {
+ body := "{}"
+ }
+
+ return JsonData().FromString(&body)
+ }
+
+ IsSuccessful() {
+ httpCode := this.GetHttpStatusCode()
+ success := false
+
+ for , successCode in this.successCodes {
+ if (httpCode == successCode) {
+ success := true
+ break
+ }
+ }
+
+ return success
+ }
+
+ IsNotFound() {
+ httpCode := this.GetHttpStatusCode()
+ notFound := false
+
+ for , notFoundCode in this.notFoundCodes {
+ if (httpCode == notFoundCode) {
+ notFound := true
+ break
+ }
+ }
+
+ return notFound
+ }
+}
diff --git a/Lib/Shared/Modules/WebServices/WebServices.module.json b/Lib/Shared/Modules/WebServices/WebServices.module.json
new file mode 100644
index 00000000..1f19e86b
--- /dev/null
+++ b/Lib/Shared/Modules/WebServices/WebServices.module.json
@@ -0,0 +1,123 @@
+{
+ "module": {
+ "name": "Web Services",
+ "type": "AppModule",
+ "icon": "",
+ "category": "Web Services",
+ "tags": ["Launchpad", "LaunchpadBuilder"],
+ "description": "Enables Launchpad to connect to and authenticate with remove services on the Internet for additional functionality.",
+ "author": {
+ "name": "Ben McClure, Volantis Dev",
+ "url": "https://volantisdev.com"
+ },
+ "website": "https://launchpad.games",
+ "version": "{{VERSION}}",
+ "appVersion": "",
+ "dependencies": []
+ },
+ "parameters": {
+ "config.web_services_file": "@@{data_dir}\\WebServices.json",
+ "config.web_services_state_path": "@@{data_dir}\\WebServicesState.json",
+ "config.web_services_view_mode": "Report",
+ "entity_type.web_service": {
+ "name_singular": "Web Service",
+ "name_plural": "Web Services",
+ "entity_class": "WebServiceEntity",
+ "storage_config_storage_parent_key": "WebServices",
+ "storage_config_path_parameter": "config.web_services_file",
+ "manager_view_mode_parameter": "config.web_services_view_mode",
+ "definition_loader_parameter_key": "web_services.services",
+ "default_icon": "webhook",
+ "allow_add": true,
+ "allow_edit": true,
+ "allow_delete": true,
+ "manager_gui": "ManageWebServicesWindow",
+ "manager_link_in_tools_menu": true
+ },
+ "entity_type.web_service_provider": {
+ "name_singular": "Service Provider",
+ "name_plural": "Service Providers",
+ "entity_class": "WebServiceProviderEntity",
+ "definition_loader_class": "ParameterEntityDefinitionLoader",
+ "definition_loader_parameter_key": "web_services.providers",
+ "storage_type": "runtime"
+ },
+ "web_services.data_types.account_info": {
+ "name": "Account Info",
+ "description": "Account information related to an authentication session."
+ },
+ "web_services.data_types.error_submission": {
+ "name": "Error Submission",
+ "description": "An endpoint to submit application errors to."
+ },
+ "web_services.data_types.feedback_submission": {
+ "name": "Feedback Submission",
+ "description": "An endpoint to submit feedback to."
+ },
+ "web_services.data_types.game_submission": {
+ "name": "Game Submission",
+ "description": "An endpoint to game launcher data to for sharing with the community."
+ },
+ "web_services.data_types.release_info": {
+ "name": "Release Info",
+ "description": "Information about the latest (or any) version of Launchpad"
+ },
+ "web_services.data_types.entity_list": {
+ "name": "Entity List",
+ "description": "A listing of entities from a web service."
+ },
+ "web_services.data_types.entity_lookup": {
+ "name": "Entity Lookup",
+ "description": "Searches for the ID of a remote entity from the provided data."
+ },
+ "web_services.data_types.entity_data": {
+ "name": "Entity Data",
+ "description": "Data to be imported into an entity within the application."
+ },
+ "web_services.data_types.availability_check": {
+ "name": "Availability check",
+ "description": "Simply checks for any response from the web service."
+ },
+ "web_services.adapter_types.json": {
+ "class": "JsonWebServiceAdapter"
+ },
+ "web_services.adapter_types.file": {
+ "class": "FileWebServiceAdapter"
+ }
+ },
+ "services": {
+ "cache.web_services": {
+ "class": "FileCache",
+ "arguments": ["@{App}", "@cache_state.web_services", "@@config.cache_dir", "WebServices"]
+ },
+ "cache_state.web_services": {
+ "class": "CacheState",
+ "arguments": ["@{App}", "@@config.cache_dir", "WebServices.json"]
+ },
+ "event_subscriber.web_services": {
+ "class": "WebServicesEventSubscriber",
+ "arguments": ["@{}", "@web_services.adapter_manager"],
+ "tags": ["event_subscriber"]
+ },
+ "state.web_services": {
+ "class": "JsonState",
+ "arguments": ["@{App}", "@@config.web_services_state_path"]
+ },
+ "state.web_services_tmp": {
+ "class": "ParameterState",
+ "arguments": ["@{App}", "web_services.state.tmp"]
+ },
+ "web_services_authenticator.jwt": {
+ "class": "JwtWebServiceAuthenticator",
+ "arguments": ["@manager.gui"]
+ },
+ "web_services.adapter_factory": {
+ "class": "WebServiceAdapterFactory",
+ "arguments": ["@{}"]
+ },
+ "web_services.adapter_manager": {
+ "class": "WebServiceAdapterManager",
+ "arguments": ["@{}", "web_services.adapters.", "@web_services.adapter_factory", "@manager.entity_type", "@manager.event"]
+ }
+ }
+}
diff --git a/Lib/Shared/Volantis.App/App/AppBase.ahk b/Lib/Shared/Volantis.App/App/AppBase.ahk
index 14051eb9..307ceb47 100644
--- a/Lib/Shared/Volantis.App/App/AppBase.ahk
+++ b/Lib/Shared/Volantis.App/App/AppBase.ahk
@@ -1,5 +1,4 @@
class AppBase {
- developer := ""
versionStr := ""
appName := ""
appDir := ""
@@ -8,9 +7,9 @@ class AppBase {
configObj := ""
stateObj := ""
serviceContainerObj := ""
- customTrayMenu := false
themeReady := false
startConfig := ""
+ isSetup := false
static Instance := ""
@@ -25,11 +24,20 @@ class AppBase {
}
Config {
- get => this.Service("config.app")
+ get => this["config.app"]
}
State {
- get => this.Service("state.app")
+ get => this["state.app"]
+ }
+
+ Parameter[key] {
+ get => this.Services.GetParameter(key)
+ set => this.Services.SetParameter(key, value)
+ }
+
+ __Item[serviceId] {
+ get => this.Service(serviceId)
}
__New(config := "", autoStart := true) {
@@ -57,6 +65,16 @@ class AppBase {
"app_dir", this.appDir,
"data_dir", this.dataDir,
"tmp_dir", this.tmpDir,
+ "app.website_url", "",
+ "app.custom_tray_menu", false,
+ "app.developer", "",
+ "app.has_settings", false,
+ "app.settings_window", "",
+ "app.show_restart_menu_item", true,
+ "app.supports_update_check", false,
+ "app.show_about_menu_item", false,
+ "app.about_window", "",
+ "app.show_website_menu_item", false,
"resources_dir", "@@{app_dir}\Resources",
"config_path", "@@{app_dir}\" . this.appName . ".json",
"config_key", "config",
@@ -374,10 +392,6 @@ class AppBase {
config["appName"] := appBaseName
}
- if (!config.Has("developer")) {
- config["developer"] := ""
- }
-
if (!config.Has("appDir") || !config["appDir"]) {
config["appDir"] := A_ScriptDir
}
@@ -396,7 +410,6 @@ class AppBase {
this.appName := config["appName"]
this.versionStr := config["version"]
- this.developer := config["developer"]
this.appDir := config["appDir"]
this.tmpDir := config["tmpDir"]
this.dataDir := config["dataDir"]
@@ -412,26 +425,26 @@ class AppBase {
this.LoadServices(config)
if (!config.Has("useShell") || config("useShell")) {
- this.Service("shell")
+ this["shell"]
}
OnError(ObjBindMethod(this, "OnException"))
event := AppRunEvent(Events.APP_PRE_INITIALIZE, this, config)
- this.Service("manager.event").DispatchEvent(event)
+ this["manager.event"].DispatchEvent(event)
this.InitializeApp(config)
event := AppRunEvent(Events.APP_POST_INITIALIZE, this, config)
- this.Service("manager.event").DispatchEvent(event)
-
- event := AppRunEvent(Events.APP_POST_STARTUP, this, config)
- this.Service("manager.event").DispatchEvent(event)
+ this["manager.event"].DispatchEvent(event)
event := AppRunEvent(Events.APP_PRE_RUN, this, config)
- this.Service("manager.event").DispatchEvent(event)
+ this["manager.event"].DispatchEvent(event)
this.RunApp(config)
+
+ event := AppRunEvent(Events.APP_POST_STARTUP, this, config)
+ this["manager.event"].DispatchEvent(event)
}
LoadServices(config) {
@@ -441,18 +454,18 @@ class AppBase {
))
this.Services.LoadDefinitions(MapDefinitionLoader(config))
- sdFactory := this.Service("factory.structured_data")
- serviceFile := this.Services.GetParameter("service_files.app")
+ sdFactory := this["factory.structured_data"]
+ serviceFile := this.Parameter["service_files.app"]
if (FileExist(serviceFile)) {
this.Services.LoadDefinitions(FileDefinitionLoader(sdFactory, serviceFile))
}
- this.Service("config.app")
+ this["config.app"]
this.InitializeTheme()
this.InitializeModules(config)
- for index, moduleServiceFile in this.Service("manager.module").GetModuleServiceFiles() {
+ for index, moduleServiceFile in this["manager.module"].GetModuleServiceFiles() {
if (FileExist(moduleServiceFile)) {
this.Services.LoadDefinitions(FileDefinitionLoader(sdFactory, moduleServiceFile))
} else {
@@ -461,49 +474,49 @@ class AppBase {
}
; Reload user config files to ensure they are the active values
- this.Service("config.app").LoadConfig(true)
+ this["config.app"].LoadConfig(true)
; Register early event subscribers (e.g. modules)
- this.Service("manager.event").RegisterServiceSubscribers(this.Services)
+ this["manager.event"].RegisterServiceSubscribers(this.Services)
- this.Service("manager.event").Register(Events.APP_SERVICES_LOADED, "AppServices", ObjBindMethod(this, "OnServicesLoaded"))
+ this["manager.event"].Register(Events.APP_SERVICES_LOADED, "AppServices", ObjBindMethod(this, "OnServicesLoaded"))
event := ServiceDefinitionsEvent(Events.APP_SERVICE_DEFINITIONS, "", "", config)
- this.Service("manager.event").DispatchEvent(event)
+ this["manager.event"].DispatchEvent(event)
if (event.Services.Count || event.Parameters.Count) {
this.Services.LoadDefinitions(SimpleDefinitionLoader(event.Services, event.Parameters))
}
- serviceFile := this.Services.GetParameter("service_files.user")
+ serviceFile := this.Parameter["service_files.user"]
if (FileExist(serviceFile)) {
this.Services.LoadDefinitions(FileDefinitionLoader(sdFactory, serviceFile))
}
; Register any missing late-loading event subscribers
- this.Service("manager.event").RegisterServiceSubscribers(this.Services)
+ this["manager.event"].RegisterServiceSubscribers(this.Services)
event := AppRunEvent(Events.APP_SERVICES_LOADED, this, config)
- this.Service("manager.event").DispatchEvent(event)
+ this["manager.event"].DispatchEvent(event)
}
OnServicesLoaded(event, extra, eventName, hwnd) {
- this.Service("manager.cache")
- this.Service("manager.entity_type").All()
- this.Service("manager.installer").RunInstallers(InstallerBase.INSTALLER_TYPE_REQUIREMENT)
+ this["manager.cache"]
+ this["manager.entity_type"].All()
+ this["manager.installer"].RunInstallers(InstallerBase.INSTALLER_TYPE_REQUIREMENT)
}
InitializeModules(config) {
- includeFiles := this.Services.GetParameter("include_files")
- updated := this.Service("manager.module").UpdateModuleIncludes(includeFiles["modules"], includeFiles["module_tests"])
+ includeFiles := this.Parameter["include_files"]
+ updated := this["manager.module"].UpdateModuleIncludes(includeFiles["modules"], includeFiles["module_tests"])
if (updated) {
message := A_IsCompiled ?
"Your modules have been updated. Currently, you must recompile " this.appName . " yourself for the changes to take effect. Would you like to exit now (highly recommended)?" :
"Your modules have been updated, and " this.appName . " must be restarted for the changes to take effect. Would you like to restart now?"
- response := this.app.Service("manager.gui").Dialog(Map(
+ response := this.app["manager.gui"].Dialog(Map(
"title", "Module Includes Updated",
"text", message
))
@@ -519,16 +532,16 @@ class AppBase {
}
InitializeTheme() {
- this.Service("gdip", "manager.gui", "manager.theme")
+ this[["gdip", "manager.gui", "manager.theme"]]
this.themeReady := true
}
InitializeApp(config) {
A_AllowMainWindow := false
- if (this.customTrayMenu) {
+ if (this.Parameter["app.custom_tray_menu"]) {
A_TrayMenu.Delete()
- this.Service("manager.event").Register(Events.AHK_NOTIFYICON, "TrayClick", ObjBindMethod(this, "OnTrayIconRightClick"), 1)
+ this["manager.event"].Register(Events.AHK_NOTIFYICON, "TrayClick", ObjBindMethod(this, "OnTrayIconRightClick"), 1)
}
}
@@ -537,19 +550,19 @@ class AppBase {
this.CheckForUpdates(false)
}
- if (this.Services.HasParameter("config_path") && !FileExist(this.Parameter("config_path"))) {
+ if (this.Services.HasParameter("config_path") && !FileExist(this.Parameter["config_path"])) {
this.InitialSetup(config)
}
}
OpenApp() {
- mainWin := this.Parameter("config.main_window")
+ mainWin := this.Parameter["config.main_window"]
if (mainWin) {
- if (this.Service("manager.gui").Has(mainWin)) {
- WinActivate("ahk_id " . this.Service("manager.gui")[mainWin].GetHwnd())
+ if (this["manager.gui"].Has(mainWin)) {
+ WinActivate("ahk_id " . this["manager.gui"][mainWin].GetHwnd())
} else {
- this.Service("manager.gui").OpenWindow(Map(
+ this["manager.gui"].OpenWindow(Map(
"type", mainWin,
"title", this.appName
))
@@ -559,7 +572,7 @@ class AppBase {
ExitApp() {
event := AppRunEvent(Events.APP_SHUTDOWN, this)
- this.Service("manager.event").DispatchEvent(event)
+ this["manager.event"].DispatchEvent(event)
if (this.Services.Has("gdip")) {
Gdip_Shutdown(this.Services["gdip"].GetHandle())
@@ -570,7 +583,7 @@ class AppBase {
RestartApp() {
event := AppRunEvent(Events.APP_RESTART, this)
- this.Service("manager.event").DispatchEvent(event)
+ this["manager.event"].DispatchEvent(event)
if (this.Services.Has("gdip")) {
Gdip_Shutdown(this.Services["gdip"].GetHandle())
@@ -586,7 +599,7 @@ class AppBase {
throw AppException("The shell is disabled, so shell commands cannot currently be run.")
}
- result := this.Service("shell").Exec(A_ComSpec . " /C " . command).StdOut.ReadAll()
+ result := this["shell"].Exec(A_ComSpec . " /C " . command).StdOut.ReadAll()
if (trimOutput) {
result := Trim(result, " `r`n`t")
@@ -606,12 +619,12 @@ class AppBase {
}
for index, arrName in name {
- results[arrName] := this.Service(arrName)
+ results[arrName] := this[arrName]
}
if (params && params.Length) {
for index, arrName in params {
- results[arrName] := this.Service(arrName)
+ results[arrName] := this[arrName]
}
}
@@ -621,14 +634,15 @@ class AppBase {
return this.Services.Get(name)
}
- Parameter(name) {
- return this.Services.GetParameter(name)
- }
-
OnException(e, mode) {
extra := (e.HasProp("Extra") && e.Extra != "") ? "`n`nExtra information:`n" . e.Extra : ""
occurredIn := e.What ? " in " . e.What : ""
- developer := this.developer ? this.developer : "the developer(s)"
+
+ developer := this.Parameter["app.developer"]
+
+ if (!developer) {
+ developer := "the developer(s)"
+ }
errorText := this.appName . " has experienced an unhandled exception. You can find the details below."
errorText .= "`n`n" . e.Message . extra
@@ -646,7 +660,7 @@ class AppBase {
}
if (this.Services.Has("logger")) {
- this.Service("logger").Error(errorText)
+ this["logger"].Error(errorText)
}
errorText .= "`n"
@@ -659,7 +673,7 @@ class AppBase {
if (this.themeReady) {
btns := allowContinue ? "*&Continue|&Reload|&Exit" : "*&Reload|&Exit"
- this.Service("manager.gui").Dialog(Map(
+ this["manager.gui"].Dialog(Map(
"type", "ErrorDialog",
"title", "Unhandled Exception",
"text", errorText,
@@ -683,7 +697,7 @@ class AppBase {
OnTrayIconRightClick(wParam, lParam, msg, hwnd) {
if (lParam == Events.MOUSE_RIGHT_UP) {
- if (this.customTrayMenu) {
+ if (this.Parameter["app.custom_tray_menu"]) {
this.ShowTrayMenu()
return 0
}
@@ -691,11 +705,7 @@ class AppBase {
}
InitialSetup(config) {
- ; Override this to set config values as needed
- }
-
- CheckForUpdates(notify := true) {
- ; Optional method to override
+ this.isSetup := true
}
ShowTrayMenu() {
@@ -706,7 +716,7 @@ class AppBase {
menuItems.Push(Map("label", "Restart", "name", "RestartApp"))
menuItems.Push(Map("label", "Exit", "name", "ExitApp"))
- result := this.Service("manager.gui").Menu(menuItems, this)
+ result := this["manager.gui"].Menu(menuItems, this)
this.HandleTrayMenuClick(result)
}
@@ -730,4 +740,238 @@ class AppBase {
this.ExitApp()
super.__Delete()
}
+
+ MainMenu(parentGui, parentCtl, showOpenAppItem := false) {
+ menuItems := this.GetMainMenuItems(showOpenAppItem)
+
+ if (menuItems.Length) {
+ this.HandleMainMenuClick(this["manager.gui"].Menu(
+ menuItems,
+ parentGui,
+ parentCtl
+ ))
+ }
+ }
+
+ GetMainMenuItems(showOpenAppItem := false) {
+ menuItems := []
+ menuItems := this.AddMainMenuEarlyItems(menuItems, showOpenAppItem)
+
+ if (menuItems.Length) {
+ menuItems.Push("")
+ }
+
+ length := menuItems.Length
+
+ toolsItems := this.GetToolsMenuItems()
+
+ if (toolsItems.Length) {
+ menuItems.Push(Map("label", "&Tools", "name", "ToolsMenu", "childItems", toolsItems))
+ }
+
+ aboutItems := this.GetAboutMenuItems()
+
+ if (aboutItems.Length) {
+ menuItems.Push(Map("label", "&About", "name", "About", "childItems", aboutItems))
+ }
+
+ menuItems := this.AddMainMenuMiddleItems(menuItems)
+
+ if (menuItems.Length > length) {
+ menuItems.Push("")
+ }
+
+ length := menuItems.Length
+ menuItems := this.AddMainMenuLateItems(menuItems)
+
+ if (menuItems.Length > length) {
+ menuItems.Push("")
+ }
+
+ if (this.Parameter["app.show_restart_menu_item"]) {
+ menuItems.Push(Map("label", "&Restart", "name", "Reload"))
+ }
+
+ menuItems.Push(Map("label", "E&xit", "name", "Exit"))
+
+ event := MenuItemsEvent(Events.APP_MENU_ITEMS_ALTER, menuItems)
+ this.Dispatch(event)
+ menuItems := event.MenuItems
+
+ return menuItems
+ }
+
+ GetAboutMenuItems() {
+ aboutItems := []
+
+ if (this.Parameter["app.show_about_menu_item"]) {
+ aboutItems.Push(Map("label", "&About " . this.appName, "name", "About"))
+ }
+
+ if (this.Parameter["app.show_website_menu_item"]) {
+ aboutItems.Push(Map("label", "Open &Website", "name", "OpenWebsite"))
+ }
+
+ event := MenuItemsEvent(Events.APP_MENU_ABOUT_ITEMS_ALTER, aboutItems)
+ this.Dispatch(event)
+ aboutItems := event.MenuItems
+
+ return aboutItems
+ }
+
+ GetToolsMenuItems() {
+ toolsItems := this.AddEntityManagerMenuLinks([])
+ event := MenuItemsEvent(Events.APP_MENU_TOOLS_ITEMS_ALTER, toolsItems)
+ this.Dispatch(event)
+ toolsItems := event.MenuItems
+
+ return toolsItems
+ }
+
+ AddMainMenuEarlyItems(menuItems, showOpenAppItem := false) {
+ if (showOpenAppItem) {
+ menuItems.Push(Map("label", "Open " . this.appName, "name", "OpenApp"))
+ menuItems.Push("")
+ }
+
+ event := MenuItemsEvent(Events.APP_MENU_ITEMS_EARLY, menuItems)
+ this.Dispatch(event)
+ menuItems := event.MenuItems
+
+ return menuItems
+ }
+
+ AddMainMenuMiddleItems(menuItems) {
+ event := MenuItemsEvent(Events.APP_MENU_ITEMS_MIDDLE, menuItems)
+ this.Dispatch(event)
+ menuItems := event.MenuItems
+ return menuItems
+ }
+
+ AddMainMenuLateItems(menuItems) {
+ if (this.Parameter["app.has_settings"]) {
+ menuItems.Push(Map("label", "&Settings", "name", "Settings"))
+ }
+
+ if (this.Parameter["app.supports_update_check"]) {
+ menuItems.Push(Map("label", "Check for &Updates", "name", "CheckForUpdates"))
+ }
+
+ event := MenuItemsEvent(Events.APP_MENU_ITEMS_LATE, menuItems)
+ this.Dispatch(event)
+ menuItems := event.MenuItems
+
+ return menuItems
+ }
+
+ AddEntityManagerMenuLinks(menuItems) {
+ menuEntityTypes := this._getToolsMenuEntityTypes()
+
+ for key, entityType in menuEntityTypes {
+ menuLinkText := entityType.definition["manager_menu_link_text"]
+
+ if (!menuLinkText) {
+ menuLinkText := "&" . entityType.definition["name_plural"]
+ }
+
+ menuItems.Push(Map("label", menuLinkText, "name", "manage_" . key))
+ }
+
+ return menuItems
+ }
+
+ Dispatch(event) {
+ this["manager.event"].DispatchEvent(event)
+ }
+
+ _getToolsMenuEntityTypes() {
+ entityTypes := Map()
+
+ for key, entityType in this["manager.entity_type"] {
+ if (entityType.definition["manager_link_in_tools_menu"]) {
+ entityTypes[key] := entityType
+ }
+ }
+
+ return entityTypes
+ }
+
+ HandleMainMenuClick(result) {
+ event := MenuResultEvent(Events.APP_MENU_PROCESS_RESULT, result)
+ this.Dispatch(event)
+ result := event.Result
+
+ if (!event.IsFinished) {
+ if (result == "About") {
+ this.ShowAbout()
+ } else if (result == "OpenWebsite") {
+ this.OpenWebsite()
+ } else if (result == "Settings") {
+ this.ShowSettings()
+ } else if (result == "CheckForUpdates") {
+ this.CheckForUpdates()
+ } else if (result == "Reload") {
+ this.restartApp()
+ } else if (result == "Exit") {
+ this.ExitApp()
+ } else {
+ for key, entityType in this._getToolsMenuEntityTypes() {
+ if (result == "manage_" . key) {
+ this["entity_type." . key].OpenManageWindow()
+ break
+ }
+ }
+ }
+ }
+
+ return result
+ }
+
+ ShowSettings() {
+ windowName := this.Parameter["app.settings_window"]
+
+ if (windowName) {
+ this["manager.gui"].Dialog(Map("type", windowName, "unique", false))
+ }
+ }
+
+ ShowAbout() {
+ windowName := this.Parameter["app.about_window"]
+
+ if (windowName) {
+ this["manager.gui"].Dialog(Map("type", windowName))
+ }
+ }
+
+ OpenWebsite() {
+ websiteUrl := this.Parameter["app.website_url"]
+
+ if (websiteUrl) {
+ Run(websiteUrl)
+ }
+ }
+
+ CheckForUpdates(notify := true) {
+ if (this.Parameter["app.supports_update_check"]) {
+ updateAvailable := false
+
+ event := ReleaseInfoEvent(Events.APP_GET_RELEASE_INFO, this)
+ this.Dispatch(event)
+ releaseInfo := event.ReleaseInfo
+
+ if (
+ releaseInfo
+ && releaseInfo.Has("version")
+ && releaseInfo["version"]
+ && this["version_checker"].VersionIsOutdated(releaseInfo["version"], this.Version)
+ ) {
+ updateAvailable := true
+ this["manager.gui"].Dialog(Map("type", "UpdateAvailableWindow"), releaseInfo)
+ }
+
+ if (!updateAvailable && notify) {
+ this["notifier"].Info("You're running the latest version of " . this.appName . ". Shiny!")
+ }
+ }
+ }
}
diff --git a/Lib/Shared/Volantis.App/App/TestAppBase.ahk b/Lib/Shared/Volantis.App/App/TestAppBase.ahk
index a5263d94..25120777 100644
--- a/Lib/Shared/Volantis.App/App/TestAppBase.ahk
+++ b/Lib/Shared/Volantis.App/App/TestAppBase.ahk
@@ -1,13 +1,13 @@
class TestAppBase extends AppBase {
ExitApp() {
event := AppRunEvent(Events.APP_SHUTDOWN, this)
- this.Service("manager.event").DispatchEvent(event)
+ this["manager.event"].DispatchEvent(event)
; Don't actually exit
}
RestartApp() {
event := AppRunEvent(Events.APP_SHUTDOWN, this)
- this.Service("manager.event").DispatchEvent(event)
+ this["manager.event"].DispatchEvent(event)
; Don't actually restart
}
}
diff --git a/Lib/Shared/Volantis.App/BulkOperation/BulkOperationBase.ahk b/Lib/Shared/Volantis.App/BulkOperation/BulkOperationBase.ahk
index a9406e35..6f85723d 100644
--- a/Lib/Shared/Volantis.App/BulkOperation/BulkOperationBase.ahk
+++ b/Lib/Shared/Volantis.App/BulkOperation/BulkOperationBase.ahk
@@ -40,7 +40,7 @@ class BulkOperationBase {
}
if (this.app.Services.Has("logger")) {
- this.app.Service("logger").Debug(Type(this) . ": Starting bulk operation...")
+ this.app["logger"].Debug(Type(this) . ": Starting bulk operation...")
}
this.running := true
@@ -56,7 +56,7 @@ class BulkOperationBase {
LogResults() {
if (this.app.Services.Has("logger")) {
- this.app.Service("logger").Info(Type(this) . " Results: " . this.GetResultMessage())
+ this.app["logger"].Info(Type(this) . " Results: " . this.GetResultMessage())
}
}
@@ -89,7 +89,7 @@ class BulkOperationBase {
ownerOrParent := this.parent
}
- this.progress := this.app.Service("manager.gui").OpenWindow(Map(
+ this.progress := this.app["manager.gui"].OpenWindow(Map(
"type", "ProgressIndicator",
"title", this.progressTitle,
"text", this.progressText,
@@ -115,7 +115,7 @@ class BulkOperationBase {
Notify() {
if (this.shouldNotify && this.app.Services.Has("notifier")) {
- this.app.Service("notifier").Info(this.GetResultMessage())
+ this.app["notifier"].Info(this.GetResultMessage())
}
}
diff --git a/Lib/Shared/Volantis.App/BulkOperation/InstallOp/InstallOp.ahk b/Lib/Shared/Volantis.App/BulkOperation/InstallOp/InstallOp.ahk
index 6c4d0aad..b093857a 100644
--- a/Lib/Shared/Volantis.App/BulkOperation/InstallOp/InstallOp.ahk
+++ b/Lib/Shared/Volantis.App/BulkOperation/InstallOp/InstallOp.ahk
@@ -17,7 +17,7 @@ class InstallOp extends BulkOperationBase {
for index, name in this.installers {
name := "installer." . name
- installer := this.app.Service(name)
+ installer := this.app[name]
if (!HasBase(installer, InstallerBase.Prototype)) {
throw AppException("Provided installer is not valid: " . name)
diff --git a/Lib/Shared/Volantis.App/DataSource/DataSourceBase.ahk b/Lib/Shared/Volantis.App/DataSource/DataSourceBase.ahk
deleted file mode 100644
index 91c1fdce..00000000
--- a/Lib/Shared/Volantis.App/DataSource/DataSourceBase.ahk
+++ /dev/null
@@ -1,70 +0,0 @@
-class DataSourceBase {
- cache := ""
- useCache := false
- maxCacheAge := 86400
-
- __New(cacheManager := "", cacheName := "") {
- if (cacheManager != "" && cacheName != "") {
- InvalidParameterException.CheckTypes("DataSourceBase", "cacheManager", cacheManager, "CacheManager")
- this.useCache := true
- this.cache := cacheManager[cacheName]
- }
- }
-
- ItemExists(path) {
- return this.useCache ? this.cache.ItemExists(path) : false
- }
-
- ReadItem(path, private := false, maxCacheAge := "") {
- if (maxCacheAge == "") {
- maxCacheAge := this.maxCacheAge
- }
-
- item := ""
-
- if (this.ItemNeedsRetrieval(path)) {
- item := this.RetrieveItem(path, private, maxCacheAge)
- } else if (this.useCache) {
- item := this.cache.ReadItem(path)
- }
-
- return item
- }
-
- ItemNeedsRetrieval(path) {
- return (!this.useCache || this.cache.ItemNeedsUpdate(path))
- }
-
- RetrieveItem(path, private := false, maxCacheAge := "") {
- return ""
- }
-
- CopyItem(path, destination) {
- if (this.ItemNeedsRetrieval(path)) {
- this.RetrieveItem(path)
- }
-
- return this.useCache ? this.cache.CopyItem(path, destination) : destination
- }
-
- GetRemoteLocation(path) {
- return path
- }
-
- ReadListing(path) {
- listingInstance := DSListing(path, this)
-
- listing := []
-
- if (listingInstance.Exists()) {
- listing := listingInstance.Read()
- }
-
- return listing
- }
-
- ReadJson(key, path := "") {
- dsItem := DSJson(key, path, this)
- return dsItem.Read()
- }
-}
diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DSAssetFile.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DSAssetFile.ahk
deleted file mode 100644
index a5165b1f..00000000
--- a/Lib/Shared/Volantis.App/DataSourceItem/DSAssetFile.ahk
+++ /dev/null
@@ -1,3 +0,0 @@
-class DSAssetFile extends DSFile {
- allowRead := false
-}
diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DSFile.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DSFile.ahk
deleted file mode 100644
index 187d931b..00000000
--- a/Lib/Shared/Volantis.App/DataSourceItem/DSFile.ahk
+++ /dev/null
@@ -1,7 +0,0 @@
-class DSFile extends DataSourceItemBase {
- allowRead := true ; Some files are only meant to be copied
-
- Read() {
- return this.allowRead ? super.Read() : ""
- }
-}
diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DSJson.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DSJson.ahk
deleted file mode 100644
index 991c41dc..00000000
--- a/Lib/Shared/Volantis.App/DataSourceItem/DSJson.ahk
+++ /dev/null
@@ -1,17 +0,0 @@
-class DSJson extends DSFile {
- itemSuffix := ""
- dataType := "Map"
-
- Read() {
- content := super.Read()
- dataType := this.dataType
- obj := %dataType%()
-
- if (content) {
- data := JsonData()
- obj := data.FromString(&content)
- }
-
- return obj
- }
-}
diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DSListing.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DSListing.ahk
deleted file mode 100644
index 1d48a12c..00000000
--- a/Lib/Shared/Volantis.App/DataSourceItem/DSListing.ahk
+++ /dev/null
@@ -1,7 +0,0 @@
-class DSListing extends DSJson {
- dataType := "Array"
-
- __New(path, dataSourceKey := "") {
- super.__New(path, "", dataSourceKey)
- }
-}
diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DataSourceItemBase.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DataSourceItemBase.ahk
deleted file mode 100644
index ef0ff3ea..00000000
--- a/Lib/Shared/Volantis.App/DataSourceItem/DataSourceItemBase.ahk
+++ /dev/null
@@ -1,53 +0,0 @@
-class DataSourceItemBase {
- endpoint := ""
- basePath := ""
- itemSuffix := ""
- path := ""
- key := ""
-
- __New(key, path := "", dataSource := "") {
- InvalidParameterException.CheckTypes("DataSourceItemBase", "key", key, "", "path", path, "")
- InvalidParameterException.CheckEmpty("DataSourceItemBase", "key", key)
- InvalidParameterException.CheckTypes("DataSourceItemBase", "dataSource", dataSource, "DataSourceBase")
-
- this.endpoint := dataSource
- this.key := key
- this.path := path
- }
-
- GetPath(includeFilename := true) {
- path := this.basePath
-
- if (path != "" && this.path != "") {
- path .= "/"
- }
-
- path .= this.path
-
- if (includeFilename) {
- if (path) {
- path .= "/"
- }
-
- path .= this.key . this.itemSuffix
- }
-
- return path
- }
-
- GetRemoteLocation() {
- return this.endpoint.GetRemoteLocation(this.GetPath())
- }
-
- Exists() {
- return this.endpoint.ItemExists(this.GetPath())
- }
-
- Read() {
- return this.endpoint.ReadItem(this.GetPath())
- }
-
- Copy(destination) {
- return this.endpoint.CopyItem(this.GetPath(), destination)
- }
-}
diff --git a/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk b/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk
deleted file mode 100644
index 5b0e84ce..00000000
--- a/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk
+++ /dev/null
@@ -1,195 +0,0 @@
-class AppEntityBase extends FieldableEntity {
- app := ""
- dataSourcePath := ""
- existsInDataSource := false
-
- __New(app, id, entityTypeId, container, eventMgr, storageObj, idSanitizer, parentEntity := "") {
- this.app := app
-
- super.__New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer, parentEntity)
- }
-
- static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, parentEntity := "") {
- className := this.Prototype.__Class
-
- return %className%(
- container.GetApp(),
- id,
- entityTypeId,
- container,
- eventMgr,
- storageObj,
- idSanitizer,
- parentEntity
- )
- }
-
- GetDefaultFieldGroups() {
- groups := super.GetDefaultFieldGroups()
-
- groups["advanced"] := Map(
- "name", "Advanced",
- "weight", 100
- )
-
- groups["api"] := Map(
- "name", "API",
- "weight", 150
- )
-
- return groups
- }
-
- BaseFieldDefinitions() {
- definitions := super.BaseFieldDefinitions()
-
- definitions["DataSourceKeys"] := Map(
- "description", "The data source keys to load defaults from, in order.",
- "help", "The default data source is 'api' which connects to the default api endpoint (Which can be any HTTP location compatible with Launchpad's API format)",
- "default", [this.app.Config["data_source_key"]],
- "multiple", true,
- "group", "api",
- "processValue", false,
- "modes", Map("simple", Map("formField", false))
- )
-
- definitions["DataSourceItemKey"] := Map(
- "description", "The key that is used to look up the entity's data from configured external data sources.",
- "help", "It defaults to the key which is usually sufficient, but it can be overridden by setting this value.`n`nAddtionally, multiple copies of the same data source entity can exist by giving them different keys but using the same DataSourceKey",
- "group", "api",
- "processValue", false,
- "modes", Map("simple", Map("formField", false))
- )
-
- definitions["AssetsDir"] := Map(
- "type", "directory",
- "description", "The directory where any required assets for this entity will be saved.",
- "default", this.app.Config["assets_dir"] . "\" . this.Id,
- "group", "advanced",
- "formField", false,
- "modes", Map("simple", Map("formField", false))
- )
-
- definitions["DependenciesDir"] := Map(
- "type", "directory",
- "description", "The directory where dependencies which have been installed for this entity can be accessed.",
- "default", this.app.appDir . "\Vendor",
- "group", "advanced",
- "required", true,
- "formField", false,
- "modes", Map("simple", Map("formField", false))
- )
-
- return definitions
- }
-
- _getLayerNames() {
- layerNames := super._getLayerNames()
- layerNames.Push("ds")
-
- return layerNames
- }
-
- _getLayerSources() {
- layerSources := super._getLayerSources()
- layerSources["ds"] := ObjBindMethod(this, "AggregateDataSourceDefaults")
-
- return layerSources
- }
-
- UpdateDataSourceDefaults(recurse := true) {
- ; @todo Move this to a module
- this.GetData().UnloadLayer("ds")
-
- if (recurse) {
- for key, child in this.GetReferencedEntities(true) {
- child.UpdateDataSourceDefaults(recurse)
- }
- }
- }
-
- AggregateDataSourceDefaults(includeParentData := true, includeChildData := true) {
- defaults := (this.parentEntity != "" && includeParentData)
- ? this.parentEntity.AggregateDataSourceDefaults(includeParentData, false)
- : Map()
-
- ; @todo Uncomment if needed, remove if not
- ;this.GetData().SetLayer("ds", defaults)
-
- for index, dataSource in this.GetAllDataSources() {
- defaults := this.merger.Merge(this.GetDataSourceDefaults(dataSource), defaults)
- }
-
- if (includeChildData) {
- for key, child in this.GetReferencedEntities(true) {
- defaults := this.merger.Merge(child.AggregateDataSourceDefaults(false, includeChildData), defaults)
- }
- }
-
- return defaults
- }
-
- GetAllDataSources() {
- dataSources := Map()
-
- if (this.Has("DataSourceKeys", false)) {
- dataSourceKeys := this["DataSourceKeys"]
-
- if (!HasBase(dataSourceKeys, Array.Prototype)) {
- dataSourceKeys := [dataSourceKeys]
- }
-
- for index, dataSourceKey in dataSourceKeys {
- if (this.app.Service("manager.data_source").Has(dataSourceKey)) {
- dataSource := this.app.Service("manager.data_source")[dataSourceKey]
-
- if (dataSource) {
- dataSources[dataSourceKey] := dataSource
- }
- }
- }
- }
-
- return dataSources
- }
-
- GetDataSourceDefaults(dataSource) {
- defaults := Map()
- itemKey := this.DiscoverDataSourceItemKey()
-
- if (itemKey) {
- dsData := dataSource.ReadJson(itemKey, this.GetDataSourceItemPath())
-
- if (dsData) {
- this.existsInDataSource := true
-
- if (dsData.Has("data")) {
- dsData := dsData["data"]
- }
-
- if (dsData.Has("defaults")) {
- defaults := this.merger.Merge(dsData["defaults"], defaults)
- defaults := this.MergeAdditionalDataSourceDefaults(defaults, dsData)
- }
- }
- }
-
- return defaults
- }
-
- DiscoverDataSourceItemKey() {
- return this.Id
- }
-
- GetDataSourceItemPath() {
- return this.dataSourcePath
- }
-
- MergeAdditionalDataSourceDefaults(defaults, dataSourceData) {
- return defaults
- }
-
- GetAssetPath(filePath) {
- return this["AssetsDir"] . "\" . filePath
- }
-}
diff --git a/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk b/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk
index 9ff0d0b0..9dfc0cf0 100644
--- a/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk
+++ b/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk
@@ -1,11 +1,12 @@
-class BackupEntity extends AppEntityBase {
+class BackupEntity extends FieldableEntity {
backup := ""
- __New(app, key, config, parentEntity := "", requiredConfigKeys := "") {
- super.__New(app, key, config, parentEntity, requiredConfigKeys)
- backupClass := config.Has("BackupClass") ? config["BackupClass"] : "FileBackup"
+ SetupEntity() {
+ super.SetupEntity()
if (!this.backup) {
+ backupClass := this.config.Has("BackupClass") ? this.config["BackupClass"] : "FileBackup"
+
this.CreateBackupObject(backupClass)
}
}
@@ -13,10 +14,6 @@ class BackupEntity extends AppEntityBase {
BaseFieldDefinitions() {
definitions := super.BaseFieldDefinitions()
- if (definitions.Has("DataSourceKeys")) {
- definitions["DataSourceKeys"]["default"] := []
- }
-
definitions["IsEditable"] := Map(
"type", "boolean",
"default", true
@@ -25,7 +22,7 @@ class BackupEntity extends AppEntityBase {
definitions["IconSrc"] := Map(
"type", "icon_file",
"description", "The path to this an icon (.ico or .exe).",
- "default", this.app.Service("manager.theme")[].GetIconPath("Backup")
+ "default", this.app["manager.theme"][].GetIconPath("backup")
)
definitions["Source"] := Map(
@@ -96,12 +93,12 @@ class BackupEntity extends AppEntityBase {
this.CreateBackupObject()
}
- AutoDetectValues(recurse := true) {
+ AutoDetectValues() {
if (!this.backup) {
this.CreateBackupObject()
}
- detectedValues := super.AutoDetectValues(recurse)
+ detectedValues := super.AutoDetectValues()
return detectedValues
}
diff --git a/Lib/Shared/Volantis.App/Entity/TaskEntity.ahk b/Lib/Shared/Volantis.App/Entity/TaskEntity.ahk
index c8a5b9b7..3f294960 100644
--- a/Lib/Shared/Volantis.App/Entity/TaskEntity.ahk
+++ b/Lib/Shared/Volantis.App/Entity/TaskEntity.ahk
@@ -1,3 +1,3 @@
-class TaskEntity extends AppEntityBase {
+class TaskEntity extends FieldableEntity {
}
diff --git a/Lib/Shared/Volantis.App/Event/MenuItemsEvent.ahk b/Lib/Shared/Volantis.App/Event/MenuItemsEvent.ahk
new file mode 100644
index 00000000..23c1f89f
--- /dev/null
+++ b/Lib/Shared/Volantis.App/Event/MenuItemsEvent.ahk
@@ -0,0 +1,16 @@
+class MenuItemsEvent extends EventBase {
+ menuItemsObj := ""
+
+ MenuItems {
+ get => this.menuItemsObj
+ }
+
+ __New(eventName, menuItems := "") {
+ if (!menuItems) {
+ menuItems := []
+ }
+
+ this.menuItemsObj := menuItems
+ super.__New(eventName)
+ }
+}
diff --git a/Lib/Shared/Volantis.App/Event/MenuResultEvent.ahk b/Lib/Shared/Volantis.App/Event/MenuResultEvent.ahk
new file mode 100644
index 00000000..8d9011d6
--- /dev/null
+++ b/Lib/Shared/Volantis.App/Event/MenuResultEvent.ahk
@@ -0,0 +1,20 @@
+class MenuResultEvent extends EventBase {
+ resultItem := ""
+ finished := false
+
+ Result {
+ get => this.resultItem
+ set => this.resultItem := value
+ }
+
+ IsFinished {
+ get => this.finished
+ set => !!(value)
+ }
+
+ __New(eventName, result) {
+ this.resultItem := result
+
+ super.__New(eventName)
+ }
+}
diff --git a/Lib/Shared/Volantis.App/Event/ReleaseInfoEvent.ahk b/Lib/Shared/Volantis.App/Event/ReleaseInfoEvent.ahk
new file mode 100644
index 00000000..84af7d5c
--- /dev/null
+++ b/Lib/Shared/Volantis.App/Event/ReleaseInfoEvent.ahk
@@ -0,0 +1,23 @@
+class ReleaseInfoEvent extends EventBase {
+ appObj := ""
+ releaseInfoObj := Map()
+
+ App {
+ get => this.appObj
+ }
+
+ ReleaseInfo {
+ get => this.releaseInfoObj
+ set => this.releaseInfoObj := value
+ }
+
+ __New(eventName, app, releaseInfo := "") {
+ this.appObj := app
+
+ if (releaseInfo) {
+ this.releaseInfoObj := releaseInfo
+ }
+
+ super.__New(eventName)
+ }
+}
diff --git a/Lib/Shared/Volantis.App/Events/Events.ahk b/Lib/Shared/Volantis.App/Events/Events.ahk
index 35992f39..74ee9b65 100644
--- a/Lib/Shared/Volantis.App/Events/Events.ahk
+++ b/Lib/Shared/Volantis.App/Events/Events.ahk
@@ -19,5 +19,15 @@ class Events {
static APP_SHUTDOWN := 0x1020
static APP_RESTART := 0x1025
+ static APP_MENU_ITEMS_EARLY := 0x1030
+ static APP_MENU_ITEMS_MIDDLE := 0x1032
+ static APP_MENU_ITEMS_LATE := 0x1034
+ static APP_MENU_ITEMS_ALTER := 0x1036
+ static APP_MENU_TOOLS_ITEMS_ALTER := 0x1038
+ static APP_MENU_ABOUT_ITEMS_ALTER := 0x1040
+ static APP_MENU_PROCESS_RESULT := 0x1042
+
+ static APP_GET_RELEASE_INFO := 0x1050
+
static AHK_NOTIFYICON := 0x404
}
diff --git a/Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk b/Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk
index 73d274dc..f5a6a6cd 100644
--- a/Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk
+++ b/Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk
@@ -2,7 +2,6 @@
entityObj := ""
entityManager := ""
missingFields := Map()
- dataSource := ""
__New(container, themeObj, config, entityObj, entityManager) {
this.entityObj := entityObj
diff --git a/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk b/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk
index 566e0964..a97a6e87 100644
--- a/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk
+++ b/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk
@@ -1,7 +1,6 @@
class ErrorDialog extends DialogBox {
errorObj := ""
notifierObj := ""
- apiEndpoint := ""
formShown := false
formH := 0
guiH := 0
@@ -9,15 +8,6 @@ class ErrorDialog extends DialogBox {
__New(container, themeObj, config, errorObj) {
this.errorObj := errorObj
this.notifierObj := container.Get("notifier").notifierObj
-
- if (container.Has("manager.data_source")) {
- dsManager := container.Get("manager.data_source")
-
- if (dsManager.GetDefaultDataSource()) {
- this.apiEndpoint := container.Get("manager.data_source").GetDefaultDataSource()
- }
- }
-
this.formShown := config.Has("submitError") ? config["submitError"] : false
super.__New(container, themeObj, config)
@@ -118,9 +108,14 @@ class ErrorDialog extends DialogBox {
SendError() {
global appVersion
- if (this.apiEndpoint) {
- endpoint := this.apiEndpoint.endpointUrl . "/submit-error"
+ ; @todo Move the API connection stuff into the LaunchpadApi module
+ filters := "error_submission"
+ operation := "create"
+ if (
+ this.container.Has("web_services.adapter_manager")
+ && this.container["web_services.adapter_manager"].HasAdapters(filters, operation)
+ ) {
body := Map()
body["message"] := this.errorObj.Message
body["what"] := this.errorObj.What
@@ -132,12 +127,25 @@ class ErrorDialog extends DialogBox {
body["version"] := appVersion ? appVersion : ""
body["details"] := this.guiObj["ErrorDetails"].Text
- request := WinHttpReq(endpoint)
- response := request.Send("POST", body)
- success := !!(request.GetStatusCode() == 200)
+ results := this.container["web_services.adapter_manager"].AdapterRequest(
+ Map("data", body),
+ filters,
+ operation,
+ true
+ )
+
+ success := false
+
+ for adapterId, adapterResult in results {
+ if (adapterResult) {
+ success := true
+
+ break
+ }
+ }
- notification := success ? "Successfully sent error to Volantis Development" : "Failed to send error to Volantis Development"
- this.notifierObj.Notify(notification, "Error Sent", success ? "info" : "error")
+ notification := success ? "Successfully sent error details for further investigation" : "Failed to send error details"
+ this.notifierObj.Notify(notification, "Error Submission", success ? "info" : "error")
}
}
}
diff --git a/Lib/Shared/Volantis.App/Gui/Dialog/UpdateAvailableWindow.ahk b/Lib/Shared/Volantis.App/Gui/Dialog/UpdateAvailableWindow.ahk
index 8e0a2a83..d0d8c4a1 100644
--- a/Lib/Shared/Volantis.App/Gui/Dialog/UpdateAvailableWindow.ahk
+++ b/Lib/Shared/Volantis.App/Gui/Dialog/UpdateAvailableWindow.ahk
@@ -21,9 +21,9 @@ class UpdateAvailableWindow extends FormGuiBase {
super.Controls()
this.guiObj.AddText("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin*2), "Current version: " . appVersion)
this.SetFont("normal", "Bold")
- this.guiObj.AddText("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin), "Latest version: " . this.releaseInfo["data"]["version"])
+ this.guiObj.AddText("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin), "Latest version: " . this.releaseInfo["version"])
this.SetFont()
- this.guiObj.AddLink("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin), 'View release notes')
+ this.guiObj.AddLink("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin), 'View release notes')
this.guiObj.AddText("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin*2), "Would you like to update " . this.app.appName . " now?")
}
@@ -36,14 +36,14 @@ class UpdateAvailableWindow extends FormGuiBase {
}
ApplyUpdate() {
- downloadUrl := this.releaseInfo["data"].Has("installer") ? this.releaseInfo["data"]["installer"] : ""
+ downloadUrl := this.releaseInfo.Has("installer") ? this.releaseInfo["installer"] : ""
if (!DirExist(this.app.tmpDir . "\Installers")) {
DirCreate(this.app.tmpDir . "\Installers")
}
if (downloadUrl) {
- localFile := this.app.tmpDir . "\Installers\" . this.app.appName . "-" . this.releaseInfo["data"]["version"] . ".exe"
+ localFile := this.app.tmpDir . "\Installers\" . this.app.appName . "-" . this.releaseInfo["version"] . ".exe"
FileDelete(this.app.tmpDir . "\Installers\" . this.app.appName . "-*")
Download(downloadUrl, localFile)
Run(localFile)
diff --git a/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk b/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk
index 7ad3903b..fd681036 100644
--- a/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk
+++ b/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk
@@ -9,7 +9,6 @@
class EntityEditorBase extends FormGuiBase {
entityObj := ""
missingFields := Map()
- dataSource := ""
entityFormFactory := ""
entityForm := ""
@@ -75,9 +74,4 @@ class EntityEditorBase extends FormGuiBase {
AddEntityCtl(heading, fieldName, showDefaultCheckbox, params*) {
return this.Add("EntityControl", "", heading, this.entityObj, fieldName, showDefaultCheckbox, params*)
}
-
- Create() {
- super.Create()
- this.dataSource := this.app.Service("manager.data_source").GetDefaultDataSource()
- }
}
diff --git a/Lib/Shared/Volantis.App/Gui/GuiBase.ahk b/Lib/Shared/Volantis.App/Gui/GuiBase.ahk
index 04c1ab3b..412fb7eb 100644
--- a/Lib/Shared/Volantis.App/Gui/GuiBase.ahk
+++ b/Lib/Shared/Volantis.App/Gui/GuiBase.ahk
@@ -30,10 +30,12 @@ class GuiBase {
isShown := false
config := ""
merger := ""
+ addedControls := []
GetDefaultConfig(container, config) {
return Map(
"id", Type(this),
+ "resizable", false,
"titlebar", true,
"waitForResult", false,
"titleIsMenu", false,
@@ -81,6 +83,10 @@ class GuiBase {
extraOptions["Border"] := true
}
+ if (this.config["resizable"]) {
+ extraOptions["Resize"] := true
+ }
+
if (this.owner != "") {
extraOptions["Owner" . this.owner.Hwnd] := true
}
@@ -106,7 +112,6 @@ class GuiBase {
this.margin := this.windowSettings["spacing"]["margin"]
this.guiId := this.config["id"]
-
this.RegisterCallbacks()
this.Create()
}
@@ -124,7 +129,7 @@ class GuiBase {
RegisterCallbacks() {
guiId := "Gui" . this.guiId
- this.app.Service("manager.event")
+ this.app["manager.event"]
.Register(Events.MOUSE_MOVE, guiId, ObjBindMethod(this, "OnMouseMove"))
.Register(Events.WM_NCCALCSIZE, guiId, ObjBindMethod(this, "OnCalcSize"))
.Register(Events.WM_NCACTIVATE, guiId, ObjBindMethod(this, "OnActivate"))
@@ -137,10 +142,10 @@ class GuiBase {
__Delete() {
if (this.app) {
- this.app.Service("manager.event").Unregister(Events.MOUSE_MOVE, "Gui" . this.guiId)
- this.app.Service("manager.event").Unregister(Events.WM_NCCALCSIZE, "Gui" . this.guiId)
- this.app.Service("manager.event").Unregister(Events.WM_NCACTIVATE, "Gui" . this.guiId)
- this.app.Service("manager.event").Unregister(Events.WM_NCHITTEST, "Gui" . this.guiId)
+ this.app["manager.event"].Unregister(Events.MOUSE_MOVE, "Gui" . this.guiId)
+ this.app["manager.event"].Unregister(Events.WM_NCCALCSIZE, "Gui" . this.guiId)
+ this.app["manager.event"].Unregister(Events.WM_NCACTIVATE, "Gui" . this.guiId)
+ this.app["manager.event"].Unregister(Events.WM_NCHITTEST, "Gui" . this.guiId)
}
if (this.activeTooltip) {
@@ -169,7 +174,9 @@ class GuiBase {
}
Add(ctlClass, options := "", params*) {
- return %ctlClass%(this, options, params*)
+ ctlObj := %ctlClass%(this, options, params*)
+ this.addedControls.Push(ctlObj)
+ return ctlObj
}
OnCalcSize(wParam, lParam, msg, hwnd) {
@@ -342,20 +349,12 @@ class GuiBase {
return this.guiObj.AddEdit(opts, defaultValue)
}
- UpdateStatusIndicator() {
+ UpdateStatusIndicator(webService) {
if (this.config["showStatusIndicator"]) {
- this.titlebar.statusIndicator.UpdateStatusIndicator(this.GetStatusInfo(), this.StatusWindowIsOnline() ? "status" : "statusOffline")
+ webService.UpdateStatusIndicators()
}
}
- StatusWindowIsOnline() {
- return false
- }
-
- GetStatusInfo() {
- return Map("name", "", "photo", "")
- }
-
SetFont(fontPreset := "normal", extraStyles := "", colorName := "text") {
this.guiObj.SetFont()
this.guiObj.SetFont("c" . this.themeObj.GetColor(colorName) . " " . this.themeObj.GetFont(fontPreset) . " " . extraStyles)
@@ -568,22 +567,22 @@ class GuiBase {
}
if (!this.isClosed && WinExist("ahk_id " . this.guiObj.Hwnd)) {
- this.app.Service("manager.gui").StoreWindowState(this)
+ this.app["manager.gui"].StoreWindowState(this)
WinClose("ahk_id " . this.guiObj.Hwnd)
} else {
this.Destroy()
}
- this.app.Service("manager.gui").CleanupWindow(this.guiId)
+ this.app["manager.gui"].CleanupWindow(this.guiId)
}
Destroy() {
if (!this.isClosed && this.config["saveWindowState"]) {
- this.app.Service("manager.gui").StoreWindowState(this)
+ this.app["manager.gui"].StoreWindowState(this)
}
if (this.owner) {
- this.app.Service("manager.gui").ReleaseFromParent(this.guiId)
+ this.app["manager.gui"].ReleaseFromParent(this.guiId)
}
this.Cleanup()
@@ -595,7 +594,7 @@ class GuiBase {
}
Cleanup() {
- this.app.Service("manager.gui").UnloadComponent(this.guiId)
+ this.app["manager.gui"].UnloadComponent(this.guiId)
; Extend to clear any global variables used
}
@@ -762,8 +761,12 @@ class GuiBase {
}
OnSize(guiObj, minMax, width, height) {
- if (this.config["titlebar"]) {
- this.titlebar.OnSize(minMax, width, height)
+ for index, ctlObj in this.addedControls {
+ ctlObj.OnSize(guiObj, minMax, width, height)
}
+
+ ; if (this.config["titlebar"]) {
+ ; this.titlebar.OnSize(minMax, width, height)
+ ; }
}
}
diff --git a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk
index 4d18b352..1a4b3c09 100644
--- a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk
+++ b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk
@@ -24,6 +24,7 @@ class ManageEntitiesWindow extends ManageWindowBase {
defaults := super.GetDefaultConfig(container, config)
defaults["entity_type"] := this.entityTypeId
defaults["title"] := this.entityType.definition["name_plural"]
+
return defaults
}
@@ -74,10 +75,9 @@ class ManageEntitiesWindow extends ManageWindowBase {
defaultIcon := this.themeObj.GetIconPath(defaultIconName)
iconNum := 1
- iconField := this.entityType.definition["icon_field"]
for key, entityObj in this.entityMgr {
- iconSrc := entityObj[iconField]
+ iconSrc := this.GetEntityIconSrc(entityObj)
if (!InStr(iconSrc, ":\")) {
iconSrc := this.themeObj.GetIconPath(iconSrc)
@@ -94,6 +94,17 @@ class ManageEntitiesWindow extends ManageWindowBase {
return IL
}
+ GetEntityIconSrc(entityObj) {
+ iconSrc := ""
+ iconField := this.entityType.definition["icon_field"]
+
+ if (iconField && entityObj.Has(iconField)) {
+ iconSrc := entityObj[iconField]
+ }
+
+ return iconSrc
+ }
+
OnDoubleClick(LV, rowNum) {
key := this.listView.GetRowKey(rowNum)
@@ -149,31 +160,35 @@ class ManageEntitiesWindow extends ManageWindowBase {
this.AutoXYWH("y", ["AddButton"])
}
- GetContextMenuItems() {
+ GetContextMenuItems(entityObj) {
definition := this.entityType.definition
menuItems := []
- if (definition["allow_view"]) {
+ if (definition["allow_view"] && this._shouldShowButton(entityObj, "ViewEntity")) {
menuItems.Push(Map("label", "&View", "name", "ViewEntity"))
}
- if (definition["allow_edit"]) {
+ if (definition["allow_edit"] && this._shouldShowButton(entityObj, "EditEntity")) {
menuItems.Push(Map("label", "Edit", "name", "EditEntity"))
}
- if (definition["allow_delete"]) {
+ if (definition["allow_delete"] && this._shouldShowButton(entityObj, "DeleteEntity")) {
menuItems.Push(Map("label", "Delete", "name", "DeleteEntity"))
}
return menuItems
}
+ _shouldShowButton(entityObj, buttonName) {
+ return true
+ }
+
ShowListViewContextMenu(lv, item, isRightClick, X, Y) {
key := this.listView.GetRowKey(item)
entityObj := this.entityMgr[key]
- menuItems := this.GetContextMenuItems()
- result := this.app.Service("manager.gui").Menu(menuItems, this)
+ menuItems := this.GetContextMenuItems(entityObj)
+ result := this.app["manager.gui"].Menu(menuItems, this)
this.ProcessContextMenuResult(result, key)
}
diff --git a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageWindowBase.ahk b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageWindowBase.ahk
index 56c85348..72cb7f01 100644
--- a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageWindowBase.ahk
+++ b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageWindowBase.ahk
@@ -6,11 +6,13 @@
lvWidth := 0
showDetailsPane := false
detailsFields := []
+ lvResizeOpts := "wh"
GetDefaultConfig(container, config) {
defaults := super.GetDefaultConfig(container, config)
defaults["frameShadow"] := false
defaults["saveWindowState"] := true
+ defaults["resizable"] := true
return defaults
}
@@ -74,8 +76,7 @@
opts.Push("w" . this.lvWidth)
}
- this.listView := this.Add("ListViewControl", opts, "", this.listViewColumns, "GetListViewData", "GetListViewImgList", "InitListView", "ShouldHighlightRow")
- this.listView.resizeOpts := "h"
+ this.listView := this.Add("ListViewControl", opts, "", this.listViewColumns, "GetListViewData", "GetListViewImgList", "InitListView", "ShouldHighlightRow", this.lvResizeOpts)
return this.listView
}
diff --git a/Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk b/Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk
index 13f2c878..a7b5d689 100644
--- a/Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk
+++ b/Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk
@@ -133,7 +133,7 @@
if (btn.ChildItems) {
this.childOpen := true
- this.result := this.app.Service("manager.gui").Menu(Map(
+ this.result := this.app["manager.gui"].Menu(Map(
"parent", this,
"child", true,
"openAtCtlSide", "right"
diff --git a/Lib/Shared/Volantis.App/GuiControl/EntityControl.ahk b/Lib/Shared/Volantis.App/GuiControl/EntityControl.ahk
index fb3688c8..7c159b01 100644
--- a/Lib/Shared/Volantis.App/GuiControl/EntityControl.ahk
+++ b/Lib/Shared/Volantis.App/GuiControl/EntityControl.ahk
@@ -66,7 +66,7 @@ class EntityControl extends GuiControlBase {
this.widget.WriteValueToEntity()
if (this.refreshDataOnChange && (!this.dependentFields || this.dependentFields.Length == 0)) {
- this.entityObj.UpdateDataSourceDefaults()
+ this.entityObj.UpdateDefaults()
}
this.SetDependentFieldValues()
@@ -117,7 +117,7 @@ class EntityControl extends GuiControlBase {
SetDependentFieldValues() {
if (this.dependentFields && this.dependentFields.Length > 0) {
- this.entityObj.UpdateDataSourceDefaults()
+ this.entityObj.UpdateDefaults()
for index, field in this.dependentFields {
this.guiObj.guiObj[field].Value := this.entityObj.GetField(field).GetRawValue()
diff --git a/Lib/Shared/Volantis.App/GuiControl/GuiControlBase.ahk b/Lib/Shared/Volantis.App/GuiControl/GuiControlBase.ahk
index 399d18cc..9d58675e 100644
--- a/Lib/Shared/Volantis.App/GuiControl/GuiControlBase.ahk
+++ b/Lib/Shared/Volantis.App/GuiControl/GuiControlBase.ahk
@@ -1,5 +1,6 @@
class GuiControlBase {
app := ""
+ container := ""
guiObj := ""
ctl := ""
defaultH := 20
@@ -12,6 +13,7 @@ class GuiControlBase {
__New(guiObj, options := "", heading := "", params*) {
InvalidParameterException.CheckTypes("GuiControlBase", "guiObj", guiObj, "GuiBase")
this.app := guiObj.app
+ this.container := this.app.Services
this.guiObj := guiObj
if (HasBase(options, GuiControlParameters.Prototype)) {
diff --git a/Lib/Shared/Volantis.App/GuiControl/ListViewControl.ahk b/Lib/Shared/Volantis.App/GuiControl/ListViewControl.ahk
index 2d5c06f8..650cad6e 100644
--- a/Lib/Shared/Volantis.App/GuiControl/ListViewControl.ahk
+++ b/Lib/Shared/Volantis.App/GuiControl/ListViewControl.ahk
@@ -11,7 +11,7 @@ class ListViewControl extends GuiControlBase {
imgListL := ""
resizeOpts := "wh"
- CreateControl(columns, dataCallback, imgListCallback := "", initCallback := "", highlightRowCallback := "", resizeOpts := "") {
+ CreateControl(columns, dataCallback, imgListCallback := "", initCallback := "", highlightRowCallback := "", resizeOpts := "wh") {
global LVM_GETHEADER
super.CreateControl(false)
columns.InsertAt(this.keyCol, "")
diff --git a/Lib/Shared/Volantis.App/GuiControl/LocationBlock.ahk b/Lib/Shared/Volantis.App/GuiControl/LocationBlock.ahk
index 40f371d8..3f535567 100644
--- a/Lib/Shared/Volantis.App/GuiControl/LocationBlock.ahk
+++ b/Lib/Shared/Volantis.App/GuiControl/LocationBlock.ahk
@@ -83,7 +83,7 @@ class LocationBlock extends GuiControlBase {
}
OnLocationOptions(btn, info) {
- result := this.app.Service("manager.gui").Menu(Map(
+ result := this.app["manager.gui"].Menu(Map(
"parent", this.guiObj,
"child", true
), btn.MenuItems, btn)
diff --git a/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk b/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk
index bec24d81..12bceb51 100644
--- a/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk
+++ b/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk
@@ -1,32 +1,83 @@
class StatusIndicatorControl extends GuiControlBase {
- statusIndicatorW := 120
- statusIndicatorMinW := 120
+ statusIndicatorW := 40
+ statusIndicatorMinW := 40
+ statusIndicatorExpandedMinW := 120
innerControl := ""
+ webService := ""
- CreateControl(statusInfo, handler := "", statusStyle := "status") {
+ CreateControl(webService, handler := "") {
super.CreateControl(false)
+ this.webService := webService
+
+ if (!handler) {
+ handler := ObjBindMethod(this, "OnStatusIndicatorClick")
+ }
if (handler == "" && HasMethod(this.guiObj, "OnStatusIndicatorClick")) {
handler := "OnStatusIndicatorClick"
}
+ this.statusIndicatorW := webService["StatusIndicatorExpanded"] ? this.statusIndicatorExpandedMinW : this.statusIndicatorMinW
+
options := this.parameters.SetDefaultOptions(this.parameters["options"].Clone(), [
"x+" . this.guiObj.margin,
"yp",
"w" . this.statusIndicatorW,
"h26",
- "vStatusIndicator"
+ "vStatusIndicator" . webService.Id
])
-
- name := statusInfo && statusInfo.Has("name") ? statusInfo["name"] : ""
- photo := statusInfo && statusInfo.Has("photo") ? statusInfo["photo"] : ""
+
+ statusInfo := webService.GetStatusInfo()
+ statusStyle := webService.Authenticated ? "status" : "statusOffline"
+ name := (statusInfo && statusInfo.Has("name") && webService["StatusIndicatorExpanded"]) ? statusInfo["name"] : ""
+ photo := (statusInfo && statusInfo.Has("photo")) ? statusInfo["photo"] : ""
this.innerControl := this.guiObj.Add("ButtonControl", options, name, handler, statusStyle, Map("photo", photo))
this.ctl := this.innerControl.ctl
return this.ctl
}
- UpdateStatusIndicator(statusInfo, statusStyle := "status") {
+ OnStatusIndicatorClick(btn, info) {
+ webService := this.webService
+ menuItems := []
+
+ if (webService) {
+ if (webService.Authenticated) {
+ menuItems.Push(Map("label", "Account Details", "name", "AccountDetails"))
+ menuItems.Push(Map("label", "Logout", "name", "Logout"))
+ } else {
+ menuItems.Push(Map("label", "Login", "name", "Login"))
+ }
+ }
+
+ result := this.container["manager.gui"].Menu(menuItems, this, btn)
+
+ if (result == "AccountDetails") {
+ accountResult := this.container["manager.gui"].Dialog(Map(
+ "type", "AccountInfoWindow",
+ "webService", this.webService,
+ "ownerOrParent", this.guiObj.guiId,
+ "child", true
+ ))
+
+ if (accountResult == "OK") {
+ this.UpdateStatusIndicator(webService)
+ }
+ } else if (result == "Logout") {
+ if (webService) {
+ webService.Logout()
+ }
+ } else if (result == "Login") {
+ if (webService) {
+ webService.Login()
+ }
+ }
+ }
+
+ UpdateStatusIndicator() {
+ statusInfo := this.webService.GetStatusInfo()
+ statusStyle := this.webService.Authenticated ? "status" : "statusOffline"
+
oldW := this.statusIndicatorW
newW := this.CalculateWidth(statusInfo)
this.statusIndicatorW := newW
@@ -38,23 +89,24 @@ class StatusIndicatorControl extends GuiControlBase {
this.ctl.Move(statusX - difference,, statusW + difference)
}
- name := statusInfo && statusInfo.Has("name") ? statusInfo["name"] : ""
- photo := statusInfo && statusInfo.Has("photo") ? statusInfo["photo"] : ""
+ name := (statusInfo && statusInfo.Has("name") && this.webService["StatusIndicatorExpanded"]) ? statusInfo["name"] : ""
+ photo := (statusInfo && statusInfo.Has("photo")) ? statusInfo["photo"] : ""
this.guiObj.themeObj.DrawButton(this.ctl, name, statusStyle, Map("photo", photo))
return difference
}
CalculateWidth(statusInfo) {
- width := this.statusIndicatorMinW
+ expanded := this.webService["StatusIndicatorExpanded"]
+ width := expanded ? this.statusIndicatorExpandedMinW : this.statusIndicatorMinW
requiredW := 10
if (statusInfo) {
- if (statusInfo.Has("name")) {
+ if (statusInfo.Has("name") && expanded) {
requiredW += this.guiObj.themeObj.CalculateTextWidth(statusInfo["name"])
}
- if (StatusInfo.Has("photo")) {
+ if (StatusInfo.Has("photo") || !expanded) {
requiredW += 26
}
}
diff --git a/Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk b/Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk
index 3b6a1e62..591a2a05 100644
--- a/Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk
+++ b/Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk
@@ -122,6 +122,5 @@ class TabsControl extends GuiControlBase {
}
this.guiObj.AutoXYWH("wh", [this.ctl.Name])
- this.ResizeColumns()
}
}
diff --git a/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk b/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk
index 3e4ed8cb..872dccae 100644
--- a/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk
+++ b/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk
@@ -5,10 +5,12 @@ class TitlebarControl extends GuiControlBase {
iconW := 16
titlebarH := 31
titlebarButtonW := 16
- initialStatusIndicatorW := 120
+ initialStatusIndicatorW := 40
+ initialStatusIndicatorExpandedW := 120
titleButton := ""
titleText := ""
statusIndicator := ""
+ statusIndicators := []
minBtn := ""
maxBtn := ""
unmaxBtn := ""
@@ -33,9 +35,21 @@ class TitlebarControl extends GuiControlBase {
buttonsW := 0
statusIndicatorW := this.guiObj.config["showStatusIndicator"] ? this.initialStatusIndicatorW : 0
+ statusIndicatorExpandedW := this.guiObj.config["showStatusIndicator"] ? this.initialStatusIndicatorExpandedW : 0
+
+ serviceMgr := this.container["entity_manager.web_service"]
+ webServices := Map()
if (this.guiObj.config["showStatusIndicator"]) {
- buttonsW += statusIndicatorW + (this.guiObj.margin * 2)
+ webServices := serviceMgr.EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES)
+ .Condition(IsTrueCondition(), "Enabled")
+ .Condition(IsTrueCondition(), "StatusIndicator")
+ .Execute()
+
+ for serviceId, webService in webServices {
+ serviceStatusW := (webService["StatusIndicatorExpanded"]) ? statusIndicatorExpandedW : statusIndicatorW
+ buttonsW += serviceStatusW + (this.guiObj.margin * 2)
+ }
}
if (this.guiObj.config["showMinimize"]) {
@@ -84,20 +98,19 @@ class TitlebarControl extends GuiControlBase {
this.titleText := this.guiObj.guiObj.AddText(opts, titleText)
}
- if (this.guiObj.config["showStatusIndicator"]) {
- opts := "x" . buttonsX . " y" . (this.topMargin - 5) . " w" . statusIndicatorW
- statusStyle := this.guiObj.StatusWindowIsOnline() ? "status" : "statusOffline"
- initialInfo := Map()
- statusInfo := this.guiObj.GetStatusInfo()
-
- if (statusInfo) {
- initialInfo := statusInfo.Clone()
- initialInfo["name"] := ""
+ if (this.guiObj.config["showStatusIndicator"] && webServices.Count) {
+ for serviceId, service in webServices {
+ expanded := service["StatusIndicatorExpanded"]
+ serviceStatusW := expanded ? statusIndicatorExpandedW : statusIndicatorW
+ opts := "x" . buttonsX . " y" . (this.topMargin - 5) . " w" . serviceStatusW
+
+ statusIndicator := this.guiObj.Add("StatusIndicatorControl", opts, "", service, "")
+ this.statusIndicators.Push(statusIndicator)
+ service.AddStatusIndicator(statusIndicator)
+
+ difference := statusIndicator.UpdateStatusIndicator()
+ buttonsX += serviceStatusW + (this.guiObj.margin * 2)
}
-
- this.statusIndicator := this.guiObj.Add("StatusIndicatorControl", opts, "", initialInfo, "", statusStyle)
- difference := this.statusIndicator.UpdateStatusIndicator(statusInfo, statusStyle)
- buttonsX += this.initialStatusIndicatorW + (this.guiObj.margin * 2)
}
handler := this.RegisterCallback("OnTitlebarButtonClick")
@@ -182,7 +195,7 @@ class TitlebarControl extends GuiControlBase {
}
}
- OnSize(minMax, width, height) {
+ OnSize(guiObj, minMax, width, height) {
if (minMax == 1 and this.guiObj.config["showMaximize"]) {
this.guiObj.guiObj["WindowUnmaxButton"].Visible := true
this.guiObj.guiObj["WindowMaxButton"].Visible := false
@@ -198,7 +211,15 @@ class TitlebarControl extends GuiControlBase {
this.guiObj.AutoXYWH("w", ["WindowTitlebar"])
if (this.guiObj.config["showStatusIndicator"]) {
- this.guiObj.AutoXYWH("x*", ["StatusIndicator"])
+ indicatorCtlNames := []
+
+ for index, statusIndicator in this.statusIndicators {
+ indicatorCtlNames.Push(statusIndicator.ctl.Name)
+ }
+
+ if (indicatorCtlNames.Length) {
+ this.guiObj.AutoXYWH("x*", indicatorCtlNames)
+ }
}
if (this.guiObj.config["showClose"]) {
@@ -209,7 +230,7 @@ class TitlebarControl extends GuiControlBase {
this.guiObj.AutoXYWH("x*", ["WindowMaxButton", "WindowUnmaxButton"])
}
- if (this.guiObj.config["showMaximize"]) {
+ if (this.guiObj.config["showMinimize"]) {
this.guiObj.AutoXYWH("x*", ["WindowMinButton"])
}
}
diff --git a/Lib/Shared/Volantis.App/Service/AuthService.ahk b/Lib/Shared/Volantis.App/Service/AuthService.ahk
deleted file mode 100644
index 9d9b4cd2..00000000
--- a/Lib/Shared/Volantis.App/Service/AuthService.ahk
+++ /dev/null
@@ -1,154 +0,0 @@
-class AuthService extends AppServiceBase {
- authProviderObj := ""
- stateObj := ""
- authenticationEnabled := false
- authInfoObj := ""
-
- __New(app, authProviderObj, stateObj) {
- InvalidParameterException.CheckTypes("AuthenticationService", "stateObj", stateObj, "StateBase")
-
- if (authProviderObj && Type(authProviderObj) == "String") {
- authProviderObj := app.Services.Get(authProviderObj)
- }
-
- this.authProviderObj := authProviderObj
- this.stateObj := stateObj
-
- authState := this.stateObj.Authentication
-
- if (authState && authState.Count > 0) {
- authInfoObj := AuthInfo()
- authInfoObj.Authenticated := true
-
- for key, value in authState {
- authInfoObj.Set(key, value, true)
- }
-
- this.authInfoObj := authInfoObj
- }
-
- super.__New(app)
- }
-
- SetAuthProvider(authProviderObj) {
- this.authProviderObj := authProviderObj
- }
-
- SetState(stateObj) {
- this.stateObj := stateObj
- }
-
- Login() {
- if (this.app.Config["api_authentication"] && this.authProviderObj) {
- authInfoObj := ""
-
- if (!this.IsAuthenticated()) {
- authInfoObj := this.authProviderObj.Login()
- } else if (this.AuthenticationNeedsRefresh()) {
- authInfoObj := this.RefreshAuthentication()
- }
-
- if (authInfoObj) {
- this.UpdateAuthState(authInfoObj)
- this.app.UpdateStatusIndicators()
- }
- }
- }
-
- GetStatusInfo() {
- statusText := "Not logged in"
- imgPath := ""
- email := ""
-
- if (this.IsAuthenticated()) {
- playerName := this.app.Config["player_name"]
- email := this.authInfoObj.Get("email")
-
- if (playerName) {
- statusText := playerName
- } else if (email) {
- statusText := email
- } else {
- statusText := "Logged in"
- }
-
- imgPath := this.authInfoObj.Get("photo")
-
- if (SubStr(imgPath, 1, 4) == "http") {
- cachePath := "account--profile.jpg"
- imgPath := this.app.Service("manager.cache")["file"].GetCachedDownload(cachePath, imgPath)
- }
- }
-
- return Map("name", statusText, "email", email, "photo", imgPath)
- }
-
- Logout() {
- if (this.app.Config["api_authentication"] && this.authProviderObj && this.authInfoObj) {
- this.authProviderObj.Logout(this.authInfoObj)
- this.authInfoObj := ""
- this.stateObj.Authentication := Map()
- this.app.UpdateStatusIndicators()
- }
- }
-
- IsAuthenticated() {
- return this.app.Config["api_authentication"] && this.authProviderObj && this.authInfoObj && this.authInfoObj.Authenticated
- }
-
- AuthenticationNeedsRefresh() {
- needsRefresh := false
-
- if (this.app.Config["api_authentication"] && this.authProviderObj && this.IsAuthenticated()) {
- needsRefresh := this.authProviderObj.NeedsRefresh(this.authInfoObj)
- }
-
- return needsRefresh
- }
-
- RefreshAuthentication() {
- if (this.app.Config["api_authentication"] && this.authProviderObj && this.IsAuthenticated()) {
- authInfoObj := this.authProviderObj.RefreshAuthentication(this.authInfoObj)
-
- if (authInfoObj) {
- this.UpdateAuthState(authInfoObj)
- }
- }
- }
-
- UpdateAuthState(authInfoObj) {
- if (this.app.Config["api_authentication"] && this.authProviderObj && authInfoObj) {
- this.authInfoObj := authInfoObj
- this.AddUserInfoFromApi(authInfoObj)
- this.stateObj.SetAuthentication(authInfoObj.GetPersistentData())
- }
- }
-
- AddUserInfoFromApi(authInfoObj) {
- dataSource := this.app.Service("manager.data_source").GetDefaultDataSource()
-
- if (dataSource) {
- apiStatus := dataSource.GetStatus()
-
- if (apiStatus) {
- if (apiStatus.Has("email")) {
- authInfoObj.Set("email", apiStatus["email"], true)
- }
-
- if (apiStatus.Has("photo")) {
- authInfoObj.Set("photo", apiStatus["photo"], true)
- }
- }
- }
- }
-
- AlterApiRequest(request) {
- if (this.IsAuthenticated()) {
- if (this.AuthenticationNeedsRefresh()) {
- this.RefreshAuthentication()
- }
-
- this.authProviderObj.AddAuthInfoToRequest(this.authInfoObj, request)
- }
- }
-}
diff --git a/Lib/Shared/Volantis.App/Service/ComponentManager/DataSourceManager.ahk b/Lib/Shared/Volantis.App/Service/ComponentManager/DataSourceManager.ahk
deleted file mode 100644
index 2b688991..00000000
--- a/Lib/Shared/Volantis.App/Service/ComponentManager/DataSourceManager.ahk
+++ /dev/null
@@ -1,61 +0,0 @@
-class DataSourceManager extends ComponentManagerBase {
- primaryKey := ""
-
- __New(container, eventMgr, notifierObj, primaryKey := "") {
- if (primaryKey) {
- this.primaryKey := primaryKey
- }
-
- super.__New(container, "data_source.", eventMgr, notifierObj, DataSourceBase)
- }
-
- GetDefaultDataSource() {
- if (!this.primaryKey) {
- throw ComponentException("There is no default data source set")
- }
-
- if (!this.Has(this.primaryKey)) {
- throw ComponentException("Primary data source key " . this.primaryKey . " does not exist")
- }
-
- return this[this.primaryKey]
- }
-
- GetDefaultComponentId() {
- return this.primaryKey
- }
-
- GetItem(key := "") {
- if (key == "") {
- key := this.primaryKey
- }
-
- return super.GetItem(key)
- }
-
- ReadListing(path, dataSourceKey := "") {
- if (dataSourceKey == "") {
- dataSourceKey := this.primaryKey
- }
-
- if (!this.Has(dataSourceKey)) {
- throw ComponentException("Component " . dataSourceKey . " does not exist in the data source manager")
- }
-
- dataSource := this[dataSourceKey]
- return dataSource.ReadListing(path)
- }
-
- ReadJson(key, path := "", dataSourceKey := "") {
- if (dataSourceKey == "") {
- dataSourceKey := this.primaryKey
- }
-
- if (!this.Has(dataSourceKey)) {
- throw ComponentException("Component " . dataSourceKey . " does not exist in the data source manager")
- }
-
- dataSource := this[dataSourceKey]
- return dataSource.ReadJson(key, path)
- }
-}
diff --git a/Lib/Shared/Volantis.App/Service/ComponentManager/GuiManager.ahk b/Lib/Shared/Volantis.App/Service/ComponentManager/GuiManager.ahk
index cfbcfd9e..022e8688 100644
--- a/Lib/Shared/Volantis.App/Service/ComponentManager/GuiManager.ahk
+++ b/Lib/Shared/Volantis.App/Service/ComponentManager/GuiManager.ahk
@@ -278,7 +278,7 @@ class GuiManager extends ComponentManagerBase {
guiObj := obj
} else if (HasBase(obj, GuiBase.Prototype)) {
guiObj := obj.guiObj
- } else if (Type(guiObj) == "String" && this.Has(obj)) {
+ } else if (Type(obj) == "String" && this.Has(obj)) {
guiObj := this[obj].guiObj
}
diff --git a/Lib/Shared/Volantis.App/State/ParameterState.ahk b/Lib/Shared/Volantis.App/State/ParameterState.ahk
new file mode 100644
index 00000000..7e573004
--- /dev/null
+++ b/Lib/Shared/Volantis.App/State/ParameterState.ahk
@@ -0,0 +1,39 @@
+class ParameterState extends StateBase {
+ parameterKey := ""
+
+ __New(app, parameterKey, autoLoad := false) {
+ this.parameterKey := parameterKey
+ super.__New(app, "", autoLoad)
+ }
+
+ SaveState(newState := "") {
+ if (newState != "") {
+ this.stateMap := newState
+ }
+
+ if (this.parameterKey) {
+ this.container.Parameters[this.parameterKey] := this.stateMap
+ }
+
+ return this.stateMap
+ }
+
+ LoadState() {
+ if (this.parameterKey && !this.stateLoaded) {
+ newState := super.LoadState()
+
+ if (this.container.HasParameter(this.parameterKey)) {
+ paramValue := this.container.Parameters[this.parameterKey]
+
+ if (HasBase(paramValue, Map.Prototype)) {
+ newState := paramValue
+ }
+ }
+
+ this.stateMap := newState
+ this.stateLoaded := true
+ }
+
+ return this.stateMap
+ }
+}
diff --git a/Lib/Shared/Volantis.App/State/StateBase.ahk b/Lib/Shared/Volantis.App/State/StateBase.ahk
index 4aaf8b0a..c19f07e9 100644
--- a/Lib/Shared/Volantis.App/State/StateBase.ahk
+++ b/Lib/Shared/Volantis.App/State/StateBase.ahk
@@ -1,5 +1,6 @@
class StateBase {
app := ""
+ container := ""
stateMap := Map()
stateLoaded := false
@@ -14,13 +15,14 @@ class StateBase {
}
IsStateOutdated() {
- return this.app.Service("version_checker").VersionIsOutdated(this.app.Version, this.Version)
+ return this.app["version_checker"].VersionIsOutdated(this.app.Version, this.Version)
}
__New(app, state := "", autoLoad := false) {
InvalidParameterException.CheckTypes("StateBase", "app", app, "AppBase")
this.app := app
+ this.container := app.Services
if (state != "") {
InvalidParameterException.CheckTypes("StateBase", "state", state, "Map")
diff --git a/Lib/Shared/Volantis.Base/EventSubscriber/EventSubscriberBase.ahk b/Lib/Shared/Volantis.Base/EventSubscriber/EventSubscriberBase.ahk
index 871cef25..3043d56d 100644
--- a/Lib/Shared/Volantis.Base/EventSubscriber/EventSubscriberBase.ahk
+++ b/Lib/Shared/Volantis.Base/EventSubscriber/EventSubscriberBase.ahk
@@ -2,6 +2,12 @@
Extending this class is optional as its main purpose is to document the API
*/
class EventSubscriberBase {
+ container := ""
+
+ __New(container) {
+ this.container := container
+ }
+
/*
Format:
Map(
@@ -12,6 +18,6 @@ class EventSubscriberBase {
)
*/
GetEventSubscribers() {
- return []
+ return Map()
}
}
diff --git a/Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk b/Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk
index 18a0e7e4..1b011249 100644
--- a/Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk
+++ b/Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk
@@ -51,19 +51,21 @@ class UrlObj {
urlMap := Map()
urlParts := ""
- regexStr := "^((P[^:/?#]+):)?(//(P[^/?#]*))?(P[^?#]*)(\?(P[^#]*))?(#(P.*))?"
- isUrl := RegExMatch(urlStr, regexStr, urlParts)
-
- loop urlParts.Count {
- matchName := urlParts.Name[A_Index]
- matchVal := urlParts[A_Index]
-
- if (matchName) {
- if (matchName == "query") {
- matchVal := this._splitQueryStr(matchVal)
+ regexStr := "^((?P[^:/?#]+):)?(//(?P[^/?#]*))?(?P[^?#]*)(\?(?P[^#]*))?(#(?P.*))?"
+ isUrl := RegExMatch(urlStr, regexStr, &urlParts)
+
+ if (urlParts) {
+ loop urlParts.Count {
+ matchName := urlParts.Name[A_Index]
+ matchVal := urlParts[A_Index]
+
+ if (matchName) {
+ if (matchName == "query") {
+ matchVal := this._splitQueryStr(matchVal)
+ }
+
+ urlMap[matchName] := matchVal
}
-
- urlMap[matchName] := matchVal
}
}
diff --git a/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk b/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk
index 56d06d51..60a4095d 100644
--- a/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk
+++ b/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk
@@ -8,7 +8,7 @@
*
* Example:
* - Layer 1: Initial defaults
- * - Layer 2: Defaults from datasource
+ * - Layer 2: Defaults from external sources
* - Layer 3: Auto-detected defaults
* - Layer 4: User configuration values
* - Processor 1: Token expander
@@ -25,10 +25,12 @@ class LayeredDataBase {
cloner := ""
userLayers := ["data"]
loadingLayers := Map()
+ extraDataLayer := "data"
+ extraDataKey := "_extra"
static NO_VALUE := ":NO_VAL:"
- __New(cloner, processors, layerNames, layerSources) {
+ __New(cloner, processors, layerNames := "", layerSources := "") {
this.cloner := cloner
if (processors) {
@@ -255,6 +257,54 @@ class LayeredDataBase {
}
}
+ GetExtraData(key := "") {
+ extraData := this.GetValue(this.extraDataKey, false, this.extraDataLayer, Map())
+
+ if (key) {
+ extraData := extraData.Has(key) ? extraData[key] : Map()
+ }
+
+ return extraData
+ }
+
+ SetExtraData(value, key := "") {
+ if (key) {
+ extraData := this.GetExtraData()
+ extraData[key] := value
+ value := extraData
+ }
+
+ this.SetValue(this.extraDataKey, value, this.extraDataLayer)
+
+ return this
+ }
+
+ HasExtraData(key := "") {
+ hasData := this.HasValue(this.extraDataKey, this.extraDataLayer, false)
+
+ if (hasData && key) {
+ extraData := this.GetExtraData()
+ hasData := extraData.Has(key)
+ }
+
+ return hasData
+ }
+
+ DeleteExtraData(key := "") {
+ if (key) {
+ extraData := this.GetExtraData()
+
+ if (extraData.Has(key)) {
+ extraData.Delete(key)
+ this.SetExtraData(extraData)
+ }
+ } else {
+ this.DeleteValue(this.extraDataKey, this.extraDataLayer)
+ }
+
+ return this
+ }
+
/**
key: The key to retrieve
@@ -486,8 +536,22 @@ class LayeredDataBase {
return data
}
+ GetUserLayers() {
+ layerNames := this.userLayers
+
+ layers := Map()
+
+ for index, layerName in layerNames {
+ layers[layerName] := this.GetLayer(layerName)
+ }
+
+ return layers
+ }
+
CloneLayers(layers := "") {
if (layers == "") {
+ layers := this.GetUserLayers()
+ } else if (layers == "*") {
this.LoadAllLayers()
layers := this.layers
} else if (Type(layers) == "String") {
@@ -500,8 +564,10 @@ class LayeredDataBase {
cloned := Map()
- for key, layer in layers {
- cloned[key] := this.CloneData(layer)
+ if (layers) {
+ for key, layer in layers {
+ cloned[key] := this.CloneData(layer)
+ }
}
return cloned
diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk
index a92092c7..079af972 100644
--- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk
+++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk
@@ -2,15 +2,21 @@ class EntityBase {
idVal := ""
entityTypeIdVal := ""
parentEntityObj := ""
+ parentEntityTypeId := ""
+ parentEntityId := ""
+ parentEntityStorage := false
container := ""
+ app := ""
eventMgr := ""
dataObj := ""
storageObj := ""
idSanitizer := ""
sanitizeId := true
+ loading := false
loaded := false
- merger := ""
dataLayer := "data"
+ dataLoaded := false
+ merger := ""
cloner := ""
Id {
@@ -20,14 +26,16 @@ class EntityBase {
EntityTypeId {
get => this.entityTypeIdVal
+ set => this.entityTypeIdVal := value
}
EntityType {
get => this.GetEntityType()
+ set => this.EntityTypeId := value
}
FieldData {
- Get => this.GetData().GetMergedData()
+ get => this.GetData().GetMergedData()
}
Name {
@@ -35,14 +43,25 @@ class EntityBase {
set => this.SetValue("name", value)
}
- UnmergedFieldData {
+ RawData {
get => this.GetData().GetLayer(this.dataLayer)
set => this.GetData().SetLayer(this.dataLayer, value)
}
ParentEntity {
get => this.GetParentEntity()
- set => this.SetParentEntity(value)
+ }
+
+ ReferencedEntities {
+ get => this.GetReferencedEntities(false)
+ }
+
+ ChildEntities {
+ get => this.GetReferencedEntities(true)
+ }
+
+ ChildEntityData {
+ get => this.GetAllChildEntityData()
}
__Item[key := ""] {
@@ -54,7 +73,8 @@ class EntityBase {
return this.GetAllValues().__Enum(numberOfVars)
}
- __New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer := "", autoLoad := true, parentEntity := "") {
+ __New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer := "", autoLoad := true, parentEntity := "", parentEntityStorage := false) {
+ this.app := container.GetApp()
this.idSanitizer := idSanitizer
if (this.sanitizeId && this.idSanitizer) {
@@ -68,25 +88,44 @@ class EntityBase {
this.storageObj := storageObj
this.merger := container.Get("merger.list")
this.cloner := container.Get("cloner.list")
+ this.parentEntityStorage := parentEntityStorage
- if (!parentEntity) {
- parentEntity := this.DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer)
+ if (!parentEntity && this.parentEntityObj) {
+ parentEntity := this.parentEntityObj
}
- if (parentEntity) {
- this.SetParentEntity(parentEntity)
- }
+ this.DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer, parentEntity)
this._createEntityData()
this.SetupEntity()
if (autoLoad) {
- this.LoadEntity(false, true)
+ this.LoadEntity()
}
}
+ static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, autoLoad := true, parentEntity := "", parentEntityStorage := false) {
+ className := this.Prototype.__Class
+
+ return %className%(
+ id,
+ entityTypeId,
+ container,
+ eventMgr,
+ storageObj,
+ idSanitizer,
+ autoLoad,
+ parentEntity,
+ parentEntityStorage
+ )
+ }
+
_createEntityData() {
- this.dataObj := EntityData(this, this._getLayerNames(), this._getLayerSources())
+ if (!this.dataLoaded) {
+ this.dataObj := EntityData(this, this._getLayerNames(), this._getLayerSources())
+ }
+
+ this.dataLoaded := true
}
_getLayerNames() {
@@ -95,39 +134,51 @@ class EntityBase {
}
_getLayerSources() {
+ layerSource := this.parentEntityStorage
+ ? ParentEntityLayerSource(this)
+ : EntityStorageLayerSource(this.storageObj, this.GetStorageId())
+
return Map(
"defaults", ObjBindMethod(this, "InitializeDefaults"),
"auto", ObjBindMethod(this, "AutoDetectValues"),
- "data", EntityStorageLayerSource(this.storageObj, this.GetStorageId())
+ "data", layerSource
)
}
- static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, parentEntity := "") {
- className := this.Prototype.__Class
-
- return %className%(
- id,
- entityTypeId,
- container,
- eventMgr,
- storageObj,
- idSanitizer,
- parentEntity
- )
+ /**
+ * Get an array of all IDs
+ *
+ * List managed IDs and give modules a chance to add others.
+ */
+ ListEntities(includeManaged := true, includeExtended := true) {
+ return this.container["entity_manager." . this.EntityTypeId]
+ .ListEntities(includeManaged, includeExtended)
}
- DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer) {
- return ""
+ DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer, parentEntity := "") {
+ event := EntityParentEvent(EntityEvents.ENTITY_DISCOVER_PARENT, this.entityTypeId, this, parentEntity)
+ this.eventMgr.DispatchEvent(event)
+
+ if (event.ParentEntity) {
+ this.parentEntityObj := event.ParentEntity
+ } else if (event.ParentEntityId) {
+ this.parentEntityTypeId := event.ParentEntityTypeId
+ this.parentEntityId := event.ParentEntityId
+ this.parentEntityMgr := event.ParentEntityManager
+ ? event.ParentEntityManager
+ : container.Get("entity_manager." . event.ParentEntityTypeId)
+
+ }
+
+ this.parentEntityObj := event.ParentEntity
+
+ return event.ParentEntity
}
GetParentEntity() {
return this.parentEntityObj
}
- SetParentEntity(parentEntity) {
- this.parentEntityObj := parentEntity
- }
-
SetupEntity() {
event := EntityEvent(EntityEvents.ENTITY_PREPARE, this.entityTypeId, this)
this.eventMgr.DispatchEvent(event)
@@ -137,26 +188,16 @@ class EntityBase {
return this.GetData().GetMergedData(!raw)
}
- GetEntityTypeId() {
- return this.entityTypeId
- }
-
GetEntityType() {
; @todo Inject entity type manager service
return this.container.Get("manager.entity_type")[this.EntityTypeId]
}
- InitializeDefaults(recurse := true) {
+ InitializeDefaults() {
defaults := Map(
"name", this.Id
)
- if (recurse) {
- for key, referencedEntity in this.GetReferencedEntities(true) {
- this.merger.Merge(defaults, referencedEntity.InitializeDefaults())
- }
- }
-
return defaults
}
@@ -202,26 +243,27 @@ class EntityBase {
return this.GetData().DeleteValue(key, this.dataLayer)
}
- CreateSnapshot(name, recurse := true) {
- this.GetData().CreateSnapshot(name)
-
+ CreateSnapshot(name, recurse := false) {
if (recurse) {
- for index, entityObj in this.GetReferencedEntities(true) {
- if (entityObj.HasOwnDataStorage()) {
- entityObj.GetData().CreateSnapshot(name, recurse)
- }
+ for index, entityObj in this.ChildEntities {
+ entityObj.GetData().CreateSnapshot(name, recurse)
}
}
- return this
- }
+ this.GetData().CreateSnapshot(name)
- HasOwnDataStorage() {
- return this.dataObj
+ return this
}
- RestoreSnapshot(name, recurse := true) {
+ RestoreSnapshot(name, recurse := false) {
this.GetData().RestoreSnapshot(name)
+
+ if (recurse) {
+ for index, entityObj in this.ChildEntities {
+ entityObj.GetData().RestoreSnapshot(name, recurse)
+ }
+ }
+
return this
}
@@ -230,24 +272,28 @@ class EntityBase {
}
LoadEntity(reload := false, recurse := false) {
- loaded := false
+ if (this.loading) {
+ throw AppException("Attempting to load entity with a circular reference.")
+ }
- if (!this.loaded || reload) {
- this.RefreshEntityData(true)
+ if (!this.loading && this.dataLoaded && (!this.loaded || reload)) {
+ this.loading := true
+ this.RefreshEntityData(recurse)
this.CreateSnapshot("original")
this.loaded := true
loaded := true
- }
+ this.loading := false
- if (recurse) {
- for index, entityObj in this.GetReferencedEntities(true) {
- entityObj.LoadEntity(reload, recurse)
+ if (recurse) {
+ for index, entityObj in this.ChildEntities {
+ entityObj.LoadEntity(reload, recurse)
+ }
}
- }
- if (loaded) {
- event := EntityEvent(EntityEvents.ENTITY_LOADED, this.entityTypeId, this)
- this.eventMgr.DispatchEvent(event)
+ if (loaded) {
+ event := EntityEvent(EntityEvents.ENTITY_LOADED, this.entityTypeId, this)
+ this.eventMgr.DispatchEvent(event)
+ }
}
}
@@ -255,7 +301,7 @@ class EntityBase {
this.GetData().UnloadAllLayers(reloadUserData)
if (recurse) {
- for index, entityObj in this.GetReferencedEntities(true) {
+ for index, entityObj in this.ChildEntities {
entityObj.RefreshEntityData(recurse, reloadUserData)
}
}
@@ -264,16 +310,16 @@ class EntityBase {
this.eventMgr.DispatchEvent(event)
}
- AutoDetectValues(recurse := true) {
+ AutoDetectValues() {
values := Map()
- if (recurse) {
- for key, referencedEntity in this.GetReferencedEntities(true) {
- this.merger.Merge(values, referencedEntity.AutoDetectValues(recurse))
- }
- }
+ event := EntityDetectValuesEvent(EntityEvents.ENTITY_DETECT_VALUES, this.EntityTypeId, this, values)
+ this.eventMgr.DispatchEvent(event)
+
+ event := EntityDetectValuesEvent(EntityEvents.ENTITY_DETECT_VALUES_ALTER, this.EntityTypeId, this, event.Values)
+ this.eventMgr.DispatchEvent(event)
- return values
+ return event.Values
}
SaveEntity(recurse := true) {
@@ -285,15 +331,15 @@ class EntityBase {
event := EntityEvent(EntityEvents.ENTITY_PRESAVE, this.entityTypeId, this)
this.eventMgr.DispatchEvent(event)
-
- this.GetData().SaveData()
- this.CreateSnapshot("original")
if (recurse) {
- for index, entityObj in this.GetReferencedEntities(true) {
+ for index, entityObj in this.ChildEntities {
entityObj.SaveEntity(recurse)
}
}
+
+ this.GetData().SaveData()
+ this.CreateSnapshot("original")
if (alreadyExists) {
event := EntityEvent(EntityEvents.ENTITY_UPDATED, this.entityTypeId, this)
@@ -317,11 +363,17 @@ class EntityBase {
}
}
- DeleteEntity() {
+ DeleteEntity(recurse := false) {
if (this.storageObj.HasData(this.GetStorageId())) {
event := EntityEvent(EntityEvents.ENTITY_PREDELETE, this.entityTypeId, this)
this.eventMgr.DispatchEvent(event)
+ if (recurse) {
+ for index, entityObj in this.ChildEntities {
+ entityObj.DeleteEntity(recurse)
+ }
+ }
+
this.storageObj.DeleteData(this.GetStorageId())
event := EntityEvent(EntityEvents.ENTITY_DELETED, this.entityTypeId, this)
@@ -344,14 +396,14 @@ class EntityBase {
return !!(changes.GetAdded().Count || changes.GetModified().Count || changes.GetDeleted().Count)
}
- DiffChanges(recursive := true) {
+ DiffChanges(recurse := true) {
diff := this.GetData().DiffChanges("original", this.dataLayer)
- if (recursive) {
+ if (recurse) {
diffs := [diff]
- for index, referencedEntity in this.GetReferencedEntities(true) {
- diffs.Push(referencedEntity.DiffChanges(recursive))
+ for index, referencedEntity in this.ChildEntities {
+ diffs.Push(referencedEntity.DiffChanges(recurse))
}
diff := DiffResult.Combine(diffs)
@@ -391,7 +443,7 @@ class EntityBase {
result := "Cancel"
while (mode) {
- result := this.app.Service("manager.gui").Dialog(Map(
+ result := this.app["manager.gui"].Dialog(Map(
"type", "SimpleEntityEditor",
"mode", mode,
"child", !!(ownerOrParent),
@@ -435,4 +487,52 @@ class EntityBase {
return text
}
+
+ UpdateDefaults(recurse := false) {
+ if (recurse) {
+ for key, child in this.ChildEntities {
+ child.UpdateDefaults(recurse)
+ }
+ }
+
+ this.GetData().UnloadAllLayers(false)
+ }
+
+ GetAllChildEntityData() {
+ return this.GetData().GetExtraData()
+ }
+
+ GetChildEntityData(entityTypeId, entityId) {
+ dataKey := entityTypeId . "." . entityId
+
+ childData := this.GetData().GetExtraData(dataKey)
+
+ return childData ? childData : Map()
+ }
+
+ SetChildEntityData(entityTypeId, entityId, data) {
+ dataKey := entityTypeId . "." . entityId
+
+ if (!data) {
+ data := Map()
+ }
+
+ this.GetData().SetExtraData(data, dataKey)
+
+ return this
+ }
+
+ HasChildEntityData(entityTypeId, entityId) {
+ dataKey := entityTypeId . "." . entityId
+
+ return this.GetData().HasExtraData(dataKey)
+ }
+
+ DeleteChildEntityData(entityTypeId, entityId) {
+ dataKey := entityTypeId . "." . entityId
+
+ this.GetData().DeleteExtraData(dataKey)
+
+ return this
+ }
}
diff --git a/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk b/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk
index 2776d249..38709289 100644
--- a/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk
+++ b/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk
@@ -11,10 +11,29 @@ class FieldableEntity extends EntityBase {
get => this.GetFieldDefinitions()
}
- __New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer := "", autoLoad := true) {
- this.entityFieldFactory := container.Get("entity_field_factory." . entityTypeId)
- this.entityWidgetFactory := container.Get("entity_widget_factory." . entityTypeId)
- super.__New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer, autoLoad)
+ __New(id, entityTypeId, container, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer := "", autoLoad := true, parentEntity := "", parentEntityStorage := false) {
+ this.entityFieldFactory := fieldFactory
+ this.entityWidgetFactory := widgetFactory
+
+ super.__New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer, autoLoad, parentEntity, parentEntityStorage)
+ }
+
+ static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, autoLoad := true, parentEntity := "", parentEntityStorage := false) {
+ className := this.Prototype.__Class
+
+ return %className%(
+ id,
+ entityTypeId,
+ container,
+ container.Get("entity_field_factory." . entityTypeId),
+ container.Get("entity_widget_factory." . entityTypeId),
+ eventMgr,
+ storageObj,
+ idSanitizer,
+ autoLoad,
+ parentEntity,
+ parentEntityStorage
+ )
}
GetDefaultFieldGroups() {
@@ -235,8 +254,8 @@ class FieldableEntity extends EntityBase {
}
}
- InitializeDefaults(recurse := true) {
- defaults := super.InitializeDefaults(recurse)
+ InitializeDefaults() {
+ defaults := super.InitializeDefaults()
for key, fieldObj in this.GetFields() {
defaults[fieldObj.Definition["storageKey"]] := fieldObj.Definition["default"]
diff --git a/Lib/Shared/Volantis.Entity/EntityField/BooleanEntityField.ahk b/Lib/Shared/Volantis.Entity/EntityField/BooleanEntityField.ahk
index b3dad413..91d37e01 100644
--- a/Lib/Shared/Volantis.Entity/EntityField/BooleanEntityField.ahk
+++ b/Lib/Shared/Volantis.Entity/EntityField/BooleanEntityField.ahk
@@ -2,20 +2,61 @@ class BooleanEntityField extends EntityFieldBase {
DefinitionDefaults(fieldDefinition) {
defaults := super.DefinitionDefaults(fieldDefinition)
defaults["widget"] := "checkbox"
+ defaults["default"] := false
return defaults
}
- GetValue() {
- isTrue := StrLower(super.GetValue())
+ GetValue(index := "") {
+ value := super.GetValue(index)
- if (isTrue == "true" || isTrue == "false") {
- isTrue := (isTrue == "true")
+ if (!HasBase(value, Array.Prototype)) {
+ value := [value]
}
- return !!(isTrue)
+ newValues := []
+
+ for singleIndex, singleValue in value {
+ isTrue := StrLower(singleValue)
+
+ if (isTrue == "true" || isTrue == "false") {
+ isTrue := (isTrue == "true")
+ }
+
+ newValues.Push(!!isTrue)
+ }
+
+ value := newValues
+
+ if (!value.Length) {
+ value.Push("")
+ }
+
+ if (index && !value.Has(index)) {
+ throw AppException("Index out of range")
+ }
+
+ if (index) {
+ return value[index]
+ } else if (this.multiple) {
+ return value
+ } else {
+ return value[1]
+ }
}
- SetValue(value) {
- super.SetValue(!!(value))
+ SetValue(value, index := "") {
+ if (index || !this.multiple || !HasBase(value, Array.Prototype)) {
+ value := !!value
+ } else {
+ newValues := []
+
+ for singleIndex, singleValue in value {
+ newValues.Push(!!singleValue)
+ }
+
+ value := newValues
+ }
+
+ super.SetValue(value, index)
}
}
diff --git a/Lib/Shared/Volantis.Entity/EntityField/EntityFieldBase.ahk b/Lib/Shared/Volantis.Entity/EntityField/EntityFieldBase.ahk
index a13646a4..c4856c10 100644
--- a/Lib/Shared/Volantis.Entity/EntityField/EntityFieldBase.ahk
+++ b/Lib/Shared/Volantis.Entity/EntityField/EntityFieldBase.ahk
@@ -10,6 +10,7 @@ class EntityFieldBase {
userLayer := "data"
cloner := ""
merger := ""
+ multiple := false
needsEntityRefresh := false
static VALUE_TYPE_DATA := "data"
@@ -19,6 +20,10 @@ class EntityFieldBase {
get => this.fieldDefinition
set => this.fieldDefinition := value
}
+
+ IsMultiple {
+ get => this.multiple
+ }
__New(fieldTypeId, entityObj, container, eventMgr, dataObj, fieldKey, fieldDefinition) {
this.fieldTypeId := fieldTypeId
@@ -31,6 +36,11 @@ class EntityFieldBase {
this.merger := container.Get("merger.list")
this.Definition := ParameterBag(this.DefinitionDefaults(fieldDefinition))
this.Definition.Add(fieldDefinition)
+ this.multiple := (this.Definition["cardinality"] == 0 || this.Definition["cardinality"] > 1)
+
+ if (this.multiple && this.Definition["default"] && !HasBase(this.Definition["default"], Array.Prototype)) {
+ this.Definition["default"] := [this.Definition["default"]]
+ }
}
static Create(container, entityTypeId, entityObj, dataObj, fieldId, definition) {
@@ -98,9 +108,8 @@ class EntityFieldBase {
"formField", true,
"group", "general",
"help", "",
- "limit", false,
+ "cardinality", 1,
"modes", Map(),
- "multiple", false,
"processValue", false,
"refreshEntityOnChange", false,
"required", false,
@@ -139,15 +148,49 @@ class EntityFieldBase {
return result
}
- GetValue() {
- return this.GetRawValue()
+ GetValue(index := "") {
+ return this.GetRawValue(index)
}
- GetRawValue() {
- return this._callback("GetValue")
+ GetRawValue(index := "") {
+ value := this._callback("GetValue")
+
+ if (this.multiple && !HasBase(value, Array.Prototype)) {
+ value := [value]
+ }
+
+ if (this.multiple && index && !value.Has(index)) {
+ throw AppException("Index " . index . " does not exist in field " . this.fieldKey . ".")
+ }
+
+ if (this.multiple && index) {
+ value := value[index]
+ }
+
+ return value
}
- SetValue(value) {
+ SetValue(value, index := "") {
+ if (index && this.multiple) {
+ existingValues := this.GetRawValue()
+
+ if (existingValues.Length < (index + 1)) {
+ throw AppException("Index to set is too high, there are only " . existingValues.Length . " values in field " . this.fieldKey . ".")
+ }
+
+ if (existingValues.Length < index) {
+ existingValues.Push(value)
+ } else {
+ existingValues[index] := value
+ }
+
+ value := existingValues
+ }
+
+ if (this.multiple && !HasBase(value, Array.Prototype)) {
+ value := [value]
+ }
+
this._callback("SetValue", value)
this.RefreshEntity()
return this
@@ -175,9 +218,19 @@ class EntityFieldBase {
}
Validate(value) {
- return this
- .CreateValidator(this.GetValidators(value))
- .Validate(value)
+ if (!HasBase(value, Array.Prototype)) {
+ value := [value]
+ }
+
+ results := []
+
+ validator := this.CreateValidator(this.GetValidators(value))
+
+ for index, singleValue in value {
+ results.Push(validator.Validate(singleValue))
+ }
+
+ return this.multiple ? results : results[1]
}
/**
@@ -187,11 +240,9 @@ class EntityFieldBase {
_parseLayer(layer := "", allowAll := true) {
if (!layer) {
layer := this.Definition["dataLayer"]
+ } else if (layer == "*" && !allowAll) {
+ throw EntityException("Cannot pass wildcard for this layer value.")
} else if (layer == "*") {
- if (!allowAll) {
- throw EntityException("Cannot pass wildcard for this layer value.")
- }
-
layer := ""
}
@@ -234,6 +285,14 @@ class EntityFieldBase {
allowEmpty
)
+ if (this.multiple && HasBase(val, Array.Prototype)) {
+ if (val.Length) {
+ val := (val[1] != "")
+ } else {
+ val := false
+ }
+ }
+
if (negate) {
val := !val
}
@@ -244,6 +303,10 @@ class EntityFieldBase {
_hasDefaultValue(allowEmpty := true, negate := false) {
hasValue := allowEmpty ? true : !!(this.Definition["default"])
+ if (hasValue && !allowEmpty && this.multiple && HasBase(this.Definition["default"], Array.Prototype)) {
+ hasValue := !!(this.Definition["default"][1])
+ }
+
if (negate) {
hasValue := !hasValue
}
diff --git a/Lib/Shared/Volantis.Entity/EntityField/EntityReferenceField.ahk b/Lib/Shared/Volantis.Entity/EntityField/EntityReferenceField.ahk
index 6591eedc..af424717 100644
--- a/Lib/Shared/Volantis.Entity/EntityField/EntityReferenceField.ahk
+++ b/Lib/Shared/Volantis.Entity/EntityField/EntityReferenceField.ahk
@@ -1,4 +1,4 @@
-class EntityReferenceField extends ServiceReferenceField {
+class EntityReferenceField extends EntityFieldBase {
managerObj := ""
DefinitionDefaults(fieldDefinition) {
@@ -8,12 +8,14 @@ class EntityReferenceField extends ServiceReferenceField {
throw EntityException("Entity reference fields require an entityType mapping.")
}
- managerObj := this._entityManager(entityTypeId)
defaults := super.DefinitionDefaults(fieldDefinition)
- defaults["servicePrefix"] := managerObj.GetServicePrefix()
- defaults["entityType"] := managerObj.entityTypeId
+ defaults["entityType"] := entityTypeId
+ defaults["widget"] := "select"
defaults["child"] := false
+ defaults["storeEntityData"] := false
+ defaults["selectOptionsCallback"] := ObjBindMethod(this, "GetEntitySelectOptions")
+ defaults["selectConditions"] := []
return defaults
}
@@ -32,34 +34,98 @@ class EntityReferenceField extends ServiceReferenceField {
return validators
}
+ GetValue(index := "") {
+ value := super.GetValue(index)
+
+ if (!HasBase(value, Array.Prototype)) {
+ value := [value]
+ }
+
+ entities := []
+ entityManager := this._entityManager()
+
+ for entityIndex, entityId in value {
+ if (!entityId) {
+ entities.Push("")
+ } else if (entityManager.Has(entityId)) {
+ entities.Push(entityManager[entityId])
+ } else {
+ throw AppException("Entity with ID '" . entityId . "' does not exist.")
+ }
+ }
+
+ if (!this.multiple || index) {
+ value := entities.Length ? entities[1] : ""
+ } else {
+ value := entities
+ }
+
+ return value
+ }
+
+ SetValue(value, index := "") {
+ if (!HasBase(value, Array.Prototype)) {
+ value := [value]
+ }
+
+ newValues := []
+
+ for singleIndex, singleValue in value {
+ if (HasBase(singleValue, EntityBase.Prototype)) {
+ newValues.Push(singleValue.Id)
+ } else if (Type(singleValue) == "String") {
+ newValues.Push(singleValue)
+ } else {
+ throw AppException("Invalid entity reference data.")
+ }
+ }
+
+ value := newValues
+
+ if (!this.multiple || index) {
+ value := value.Length ? value[1] : ""
+ }
+
+ super.SetValue(value, index)
+ return this
+ }
+
+ GetEntitySelectOptions() {
+ options := this._getSelectQuery().Execute()
+
+ if (!this.Definition["required"]) {
+ options.InsertAt(1, "")
+ }
+
+ return options
+ }
+
_entityManager(entityTypeId := "") {
if (!this.managerObj) {
if (!entityTypeId) {
entityTypeId := this.Definition["entityType"]
}
- this.managerObj := this.container.Get("entity_manager." . entityTypeId)
+ this.managerObj := this.container["entity_manager." . entityTypeId]
}
return this.managerObj
}
- _getService(entityId) {
- if (!this.Definition["entityType"]) {
- throw AppException("Entity type of reference field is not specified")
- }
+ _getSelectQuery() {
+ query := this._entityManager().EntityQuery(EntityQuery.RESULT_TYPE_IDS)
+ conditions := this.Definition["selectConditions"]
- entityObj := ""
+ if (conditions) {
+ if (Type(conditions) != "Array") {
+ conditions := [conditions]
+ }
- if (entityId) {
- entityObj := this._entityManager()[entityId]
- entityObj.LoadEntity()
+ for index, condition in conditions {
+ query.Condition(condition)
+ }
}
- return entityObj
- }
-
- _getSelectQuery() {
- return this._entityManager().EntityQuery(EntityQuery.RESULT_TYPE_IDS)
+ return query
}
}
diff --git a/Lib/Shared/Volantis.Entity/EntityField/ServiceReferenceField.ahk b/Lib/Shared/Volantis.Entity/EntityField/ServiceReferenceField.ahk
index 10ed2747..b0c21423 100644
--- a/Lib/Shared/Volantis.Entity/EntityField/ServiceReferenceField.ahk
+++ b/Lib/Shared/Volantis.Entity/EntityField/ServiceReferenceField.ahk
@@ -1,6 +1,13 @@
class ServiceReferenceField extends EntityFieldBase {
- ReferencedObject {
- get => this.GetValue()
+ DefinitionDefaults(fieldDefinition) {
+ defaults := super.DefinitionDefaults(fieldDefinition)
+
+ defaults["servicePrefix"] := ""
+ defaults["widget"] := "select"
+ defaults["selectOptionsCallback"] := ObjBindMethod(this, "GetServiceSelectOptions")
+ defaults["selectConditions"] := []
+
+ return defaults
}
GetValidators(value) {
@@ -13,30 +20,34 @@ class ServiceReferenceField extends EntityFieldBase {
return validators
}
- DefinitionDefaults(fieldDefinition) {
- defaults := super.DefinitionDefaults(fieldDefinition)
+ GetValue(index := "") {
+ value := super.GetValue(index)
- defaults["servicePrefix"] := ""
- defaults["widget"] := "select"
- defaults["selectOptionsCallback"] := ObjBindMethod(this, "GetEntitySelectOptions")
- defaults["selectConditions"] := []
-
- return defaults
- }
+ if (!HasBase(value, Array.Prototype)) {
+ value := [value]
+ }
- GetValue() {
- serviceObj := ""
- serviceId := super.GetValue()
+ newValues := []
- if (serviceId ) {
- if (Type(serviceId) != "String") {
- serviceObj := serviceId
+ for singleIndex, singleValue in value {
+ if (Type(singleValue) != "String") {
+ serviceObj := singleValue
} else {
- serviceObj := this._getService(serviceId)
+ serviceObj := this._getService(singleValue)
+ }
+
+ if (serviceObj) {
+ newValues.Push(serviceObj)
}
}
- return serviceObj
+ value := newValues
+
+ if (!this.multiple || index) {
+ value := value.Length ? value[1] : ""
+ }
+
+ return value
}
_getService(serviceId) {
@@ -53,8 +64,20 @@ class ServiceReferenceField extends EntityFieldBase {
return serviceObj
}
- SetValue(value) {
- super.SetValue(this._getServiceId(value))
+ SetValue(value, index := "") {
+ if (index || !this.multiple || !HasBase(value, Array.Prototype)) {
+ value := this._getServiceId(singleValue)
+ } else {
+ newValues := []
+
+ for singleIndex, singleValue in value {
+ newValues[singleIndex] = this._getServiceId(singleValue)
+ }
+
+ value := newValues
+ }
+
+ super.SetValue(value, index)
}
_getServiceId(value) {
@@ -69,7 +92,7 @@ class ServiceReferenceField extends EntityFieldBase {
return this.container.Query(this.Definition["servicePrefix"], ContainerQuery.RESULT_TYPE_NAMES, false, true)
}
- GetEntitySelectOptions() {
+ GetServiceSelectOptions() {
query := this._getSelectQuery()
conditions := this.Definition["selectConditions"]
diff --git a/Lib/Shared/Volantis.Entity/EntityManager/EntityManagerBase.ahk b/Lib/Shared/Volantis.Entity/EntityManager/EntityManagerBase.ahk
index c84eacc2..00399489 100644
--- a/Lib/Shared/Volantis.Entity/EntityManager/EntityManagerBase.ahk
+++ b/Lib/Shared/Volantis.Entity/EntityManager/EntityManagerBase.ahk
@@ -94,4 +94,30 @@ class EntityManagerBase extends ComponentManagerBase {
childManager.LoadComponents(reloadComponents)
}
}
+
+ /**
+ * Get an array of all IDs
+ *
+ * List managed IDs and give modules a chance to add others.
+ */
+ ListEntities(includeManaged := true, includeExtended := true) {
+ entities := includeManaged
+ ? this.EntityQuery(EntityQuery.RESULT_TYPE_IDS).Execute()
+ : []
+
+ if (includeExtended) {
+ event := EntityListEvent(
+ EntityEvents.ENTITY_LIST_ENTITIES,
+ this.entityTypeId,
+ entities,
+ includeManaged,
+ includeExtended
+ )
+ this.eventMgr.DispatchEvent(event)
+
+ entities := event.EntityList
+ }
+
+ return entities
+ }
}
diff --git a/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk b/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk
index b471ec6f..ebf04287 100644
--- a/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk
+++ b/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk
@@ -24,6 +24,7 @@ class EntityStorageBase {
id := this._dereferenceId(idOrObj)
data := this._dereferenceData(idOrObj, data)
this._saveEntityData(id, data)
+ return this
}
_saveEntityData(id, data) {
@@ -48,6 +49,7 @@ class EntityStorageBase {
DeleteData(idOrObj) {
this._deleteEntityData(this._dereferenceId(idOrObj))
+ return this
}
_dereferenceId(idOrObj) {
@@ -64,7 +66,7 @@ class EntityStorageBase {
_dereferenceData(idOrObj, data := "") {
if (HasBase(idOrObj, EntityBase.Prototype) && !data) {
- data := idOrObj.UnmergedFieldData
+ data := idOrObj.RawData
}
return data
diff --git a/Lib/Shared/Volantis.Entity/EntityStorage/NullEntityStorage.ahk b/Lib/Shared/Volantis.Entity/EntityStorage/NullEntityStorage.ahk
new file mode 100644
index 00000000..38cb4c2b
--- /dev/null
+++ b/Lib/Shared/Volantis.Entity/EntityStorage/NullEntityStorage.ahk
@@ -0,0 +1,3 @@
+class NullEntityStorage extends EntityStorageBase {
+
+}
diff --git a/Lib/Shared/Volantis.Entity/Event/EntityDetectValuesEvent.ahk b/Lib/Shared/Volantis.Entity/Event/EntityDetectValuesEvent.ahk
new file mode 100644
index 00000000..2a5bf92c
--- /dev/null
+++ b/Lib/Shared/Volantis.Entity/Event/EntityDetectValuesEvent.ahk
@@ -0,0 +1,17 @@
+class EntityDetectValuesEvent extends EntityEvent {
+ _valuesMap := ""
+
+ Values {
+ get => this._valuesMap
+ }
+
+ __New(eventName, entityTypeId, entityObj, values := "") {
+ if (!values) {
+ values := Map()
+ }
+
+ this._valuesMap := values
+
+ super.__New(eventName, entityTypeId, entityObj)
+ }
+}
diff --git a/Lib/Shared/Volantis.Entity/Event/EntityLayerSourcesEvent.ahk b/Lib/Shared/Volantis.Entity/Event/EntityLayerSourcesEvent.ahk
new file mode 100644
index 00000000..f8431925
--- /dev/null
+++ b/Lib/Shared/Volantis.Entity/Event/EntityLayerSourcesEvent.ahk
@@ -0,0 +1,13 @@
+class EntityLayerSourcesEvent extends EntityEvent {
+ _layerSourcesObj := ""
+
+ LayerSources {
+ get => this._layerSourcesObj
+ }
+
+ __New(eventName, entityTypeId, entityObj, layerSourcesObj) {
+ this._layerSourcesObj := layerSourcesObj
+
+ super.__New(eventName, entityTypeId, entityObj)
+ }
+}
diff --git a/Lib/Shared/Volantis.Entity/Event/EntityListEvent.ahk b/Lib/Shared/Volantis.Entity/Event/EntityListEvent.ahk
new file mode 100644
index 00000000..9782b0be
--- /dev/null
+++ b/Lib/Shared/Volantis.Entity/Event/EntityListEvent.ahk
@@ -0,0 +1,32 @@
+class EntityListEvent extends EventBase {
+ _entityTypeId := ""
+ _entityList := []
+ _includeManaged := false
+ _includeExtended := false
+
+ __New(eventName, entityTypeId, entityList, includeManaged, includeExtended) {
+ this._entityTypeId := entityTypeId
+ this._entityList := entityList
+ this._includeManaged := includeManaged
+ this._includeExtended := includeExtended
+
+ super.__New(eventName)
+ }
+
+ EntityTypeId {
+ get => this._entityTypeId
+ }
+
+ EntityList {
+ get => this._entityList
+ set => this._entityList := value
+ }
+
+ IncludeManaged {
+ get => this._includeManaged
+ }
+
+ IncludeExtended {
+ get => this._includeExtended
+ }
+}
diff --git a/Lib/Shared/Volantis.Entity/Event/EntityParentEvent.ahk b/Lib/Shared/Volantis.Entity/Event/EntityParentEvent.ahk
new file mode 100644
index 00000000..c95ed335
--- /dev/null
+++ b/Lib/Shared/Volantis.Entity/Event/EntityParentEvent.ahk
@@ -0,0 +1,28 @@
+class EntityParentEvent extends EntityEvent {
+ _parentEntity := ""
+ _parentEntityTypeId := ""
+ _parentEntityId := ""
+
+ __New(eventName, entityTypeId, entityObj, parentEntity := "", parentEntityTypeId := "", parentEntityId := "") {
+ this._parentEntity := parentEntity
+ this._parentEntityTypeId := parentEntityTypeId
+ this._parentEntityId := parentEntityId
+
+ super.__New(eventName, entityTypeId, entityObj)
+ }
+
+ ParentEntity {
+ get => this._parentEntity
+ set => this._parentEntity := value
+ }
+
+ ParentEntityTypeId {
+ get => this._parentEntityTypeId
+ set => this._parentEntityTypeId := value
+ }
+
+ ParentEntityId {
+ get => this._parentEntityId
+ set => this._parentEntityId := value
+ }
+}
diff --git a/Lib/Shared/Volantis.Entity/Event/EntityStorageEvent.ahk b/Lib/Shared/Volantis.Entity/Event/EntityStorageEvent.ahk
deleted file mode 100644
index 643d20fe..00000000
--- a/Lib/Shared/Volantis.Entity/Event/EntityStorageEvent.ahk
+++ /dev/null
@@ -1,13 +0,0 @@
-class EntityStorageEvent extends EntityEvent {
- _storageObj := ""
-
- Storage {
- get => this._storageObj
- }
-
- __New(eventName, entityTypeId, entityObj, storageObj) {
- this._storageObj := storageObj
-
- super.__New(eventName, entityTypeId, entityObj)
- }
-}
diff --git a/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk b/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk
index 1076b822..2d2ed47a 100644
--- a/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk
+++ b/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk
@@ -1,6 +1,6 @@
class EntityEvents {
- static ENTITY_STORAGE_OBJECTS := 0x4020
- static ENTITY_STORAGE_OBJECTS_ALTER := 0x4022
+ static ENTITY_LAYER_SOURCES := 0x4020
+ static ENTITY_LAYER_SOURCES_ALTER := 0x4022
static ENTITY_DATA_PROCESSORS := 0x4030
static ENTITY_DATA_PROCESSORS_ALTER := 0x4032
static ENTITY_PREPARE := 0x4035
@@ -15,10 +15,14 @@ class EntityEvents {
static ENTITY_REFRESH := 0x4065
static ENTITY_DATA_LAYERS := 0x4070
static ENTITY_DATA_LAYERS_ALTER := 0x4071
+ static ENTITY_DETECT_VALUES := 0x4075
+ static ENTITY_DETECT_VALUES_ALTER := 0x4076
static ENTITY_VALIDATE := 0x4080
static ENTITY_FIELD_DEFINITIONS := 0x4085
static ENTITY_FIELD_DEFINITIONS_ALTER := 0x4087
static ENTITY_FIELD_GROUPS := 0x4090
static ENTITY_FIELD_GROUPS_ALTER := 0x4092
static ENTITY_REFERENCE_ENTITY_SAVED := 0x4095
+ static ENTITY_LIST_ENTITIES := 0x4098
+ static ENTITY_DISCOVER_PARENT := 0x4100
}
diff --git a/Lib/Shared/Volantis.Entity/Factory/EntityFactory.ahk b/Lib/Shared/Volantis.Entity/Factory/EntityFactory.ahk
index 19af31bc..7d45933b 100644
--- a/Lib/Shared/Volantis.Entity/Factory/EntityFactory.ahk
+++ b/Lib/Shared/Volantis.Entity/Factory/EntityFactory.ahk
@@ -110,6 +110,6 @@ class EntityFactory {
throw EntityException("Unable to create entity '" . id . "' of type '" . entityTypeObj . "' in EntityFactory")
}
- return %entityTypeObj%.Create(this.container, this.eventMgr, id, this.entityTypeId, this.storageObj, this.idSanitizer, parentEntity)
+ return %entityTypeObj%.Create(this.container, this.eventMgr, id, this.entityTypeId, this.storageObj, this.idSanitizer, true, parentEntity, this.definition["parent_entity_storage"])
}
}
diff --git a/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk b/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk
index 88d866bd..fe637a5b 100644
--- a/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk
+++ b/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk
@@ -30,6 +30,7 @@ class EntityTypeFactory {
"event_manager", "manager.event",
"notifier", "notifier",
"parent_entity_type", "",
+ "parent_entity_storage", false,
"default_icon", "cube-outline",
"icon_field", "IconSrc",
"allow_view", false,
@@ -40,7 +41,8 @@ class EntityTypeFactory {
"manager_view_mode_parameter", "",
"manager_gui", "ManageEntitiesWindow",
"manager_link_in_tools_menu", false,
- "manager_menu_link_text", ""
+ "manager_menu_link_text", "",
+ "storage_type", "persistent"
)
}
@@ -97,7 +99,7 @@ class EntityTypeFactory {
),
)
- if (definition["storage_class"] == "ConfigEntityStorage" && definition["storage_config_path_parameter"]) {
+ if (definition["storage_type"] == "persistent" && definition["storage_class"] == "ConfigEntityStorage" && definition["storage_config_path_parameter"]) {
services["config_storage." . id] := Map(
"class", definition["storage_config_storage_class"],
"arguments", ["@@" . definition["storage_config_path_parameter"], definition["storage_config_storage_parent_key"]]
@@ -107,6 +109,11 @@ class EntityTypeFactory {
"class", "PersistentConfig",
"arguments", ["@config_storage." . id, "@{}", "entity_data." . id]
)
+ } else if (definition["storage_type"] == "runtime") {
+ services["config." . id] := Map(
+ "class", "RuntimeConfig",
+ "arguments", ["@{}", "entity_data." . id]
+ )
}
entityClass := definition["entity_class"]
diff --git a/Lib/Shared/Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk b/Lib/Shared/Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk
index b58e5bb9..ec182c25 100644
--- a/Lib/Shared/Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk
+++ b/Lib/Shared/Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk
@@ -24,5 +24,7 @@ class EntityStorageLayerSource extends LayerSourceBase {
if (this.HasData()) {
this.storageObj.DeleteData(this.storageId)
}
+
+ return this
}
}
diff --git a/Lib/Shared/Volantis.Entity/LayerSource/ParentEntityLayerSource.ahk b/Lib/Shared/Volantis.Entity/LayerSource/ParentEntityLayerSource.ahk
new file mode 100644
index 00000000..dfa685aa
--- /dev/null
+++ b/Lib/Shared/Volantis.Entity/LayerSource/ParentEntityLayerSource.ahk
@@ -0,0 +1,55 @@
+class ParentEntityLayerSource extends LayerSourceBase {
+ entityObj := ""
+
+ __New(entityObj) {
+ this.entityObj := entityObj
+ }
+
+ SaveData(data := "") {
+ this._validateParentEntity()
+
+ this.entityObj.ParentEntity
+ .SetChildEntityData(this.entityObj.EntityTypeId, this.entityObj.Id, data)
+
+ return this
+ }
+
+ LoadData() {
+ this._validateParentEntity()
+
+ return this.entityObj.ParentEntity
+ .GetChildEntityData(this.entityObj.EntityTypeId, this.entityObj.Id)
+ }
+
+ HasData() {
+ this._validateParentEntity()
+
+ return this.entityObj.ParentEntity
+ .HasChildEntityData(this.entityObj.EntityTypeId, this.entityObj.Id)
+ }
+
+ DeleteData() {
+ this._validateParentEntity()
+
+ this.entityObj.ParentEntity
+ .DeleteChildEntityData(this.entityObj.EntityTypeId, this.entityObj.Id)
+
+ return this
+ }
+
+ _validateParentEntity() {
+ if (!this.entityObj.ParentEntity) {
+ throw AppException("Parent entity not set.")
+ }
+
+ if (!HasBase(this.entityObj.ParentEntity, EntityBase.Prototype)) {
+ throw AppException("Parent entity is not an entity.")
+ }
+
+ parentData := this.entityObj.ParentEntity.GetData()
+
+ if (!parentData) {
+ throw AppException("Parent entity data is not set.")
+ }
+ }
+}
diff --git a/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk b/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk
index 2a3c041d..26addd79 100644
--- a/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk
+++ b/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk
@@ -3,31 +3,60 @@ class EntityData extends LayeredDataBase {
entity := ""
eventMgr := ""
- __New(entity, layerNames, layerSources) {
+ __New(entity, layerNames := "", layerSources := "") {
this.entityTypeId := entity.EntityTypeId
this.entity := entity
this.eventMgr := entity.eventMgr
super.__New(
entity.cloner,
- this._createProcessors(),
- this._getLayerNames(layerNames),
- this._collectEntityStorage(layerSources)
+ this._createProcessors(),
+ layerNames,
+ layerSources
)
}
- _collectEntityStorage(layerSources) {
+ InitializeLayers(layerNames) {
+ if (!layerNames) {
+ layerNames := []
+ }
+
+ this._appendLayerNames(["defaults"], layerNames)
+
+ event := EntityLayersEvent(EntityEvents.ENTITY_DATA_LAYERS, this.entityTypeId, this.entity, layerNames)
+ this.eventMgr.DispatchEvent(event)
+
+ layerNames := event.Layers
+ this._appendLayerNames(["auto", "data"], layerNames)
+
+ event := EntityLayersEvent(EntityEvents.ENTITY_DATA_LAYERS_ALTER, this.entityTypeId, this.entity, layerNames)
+ this.eventMgr.DispatchEvent(event)
+
+ layerNames := event.Layers
+ layers := Map()
+
+ for index, layerName in layerNames {
+ this.layerPriority.Push(layerName)
+ layers[layerName] := Map()
+ }
+
+ this.SetLayers(layers)
+ }
+
+ SetLayerSources(layerSources) {
if (!layerSources.Has("defaults")) {
layerSources["defaults"] := ObjBindMethod(this.entity, "InitializeDefaults")
}
- event := EntityStorageEvent(EntityEvents.ENTITY_STORAGE_OBJECTS, this.entityTypeId, this.entity, layerSources)
+ event := EntityLayerSourcesEvent(EntityEvents.ENTITY_LAYER_SOURCES, this.entityTypeId, this.entity, layerSources)
this.eventMgr.DispatchEvent(event)
- event := EntityStorageEvent(EntityEvents.ENTITY_STORAGE_OBJECTS_ALTER, this.entityTypeId, this.entity, event.Storage)
+ event := EntityLayerSourcesEvent(EntityEvents.ENTITY_LAYER_SOURCES_ALTER, this.entityTypeId, this.entity, event.LayerSources)
this.eventMgr.DispatchEvent(event)
- return event.Storage
+ for key, source in event.LayerSources {
+ this.SetLayerSource(key, source)
+ }
}
_createProcessors() {
@@ -45,25 +74,6 @@ class EntityData extends LayeredDataBase {
return event.Processors
}
- _getLayerNames(layerNames) {
- if (!layerNames) {
- layerNames := []
- }
-
- this._appendLayerNames(["defaults"], layerNames)
-
- event := EntityLayersEvent(EntityEvents.ENTITY_DATA_LAYERS, this.entityTypeId, this.entity, layerNames)
- this.eventMgr.DispatchEvent(event)
-
- layerNames := event.Layers
- this._appendLayerNames(["auto", "data"], layerNames)
-
- event := EntityLayersEvent(EntityEvents.ENTITY_DATA_LAYERS_ALTER, this.entityTypeId, this.entity, layerNames)
- this.eventMgr.DispatchEvent(event)
-
- return event.Layers
- }
-
_appendLayerNames(namesToAppend, existingNames) {
for index, name in namesToAppend {
exists := false
diff --git a/Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk b/Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk
index b873f5a9..e5b73f15 100644
--- a/Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk
+++ b/Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk
@@ -22,7 +22,7 @@ class ModuleFactory {
),
"module." . key, Map(
"class", this.classMap.Has(key) ? this.classMap[key] : "SimpleModule",
- "arguments", [key, "@module_info." . key, "@module_config." . key],
+ "arguments", [key, "@module_info." . key, "@module_config." . key, isCore],
"file", file,
"enabled", enabled,
"core", isCore,
diff --git a/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk b/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk
index af874ef4..2712c2b5 100644
--- a/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk
+++ b/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk
@@ -2,11 +2,13 @@ class ModuleBase {
moduleInfo := ""
config := ""
key := ""
+ _core := false
- __New(key, moduleInfo, config) {
+ __New(key, moduleInfo, config, isCore) {
this.key := key
this.moduleInfo := moduleInfo
this.config := config
+ this._core := isCore
}
IsEnabled() {
@@ -14,7 +16,7 @@ class ModuleBase {
}
IsCore() {
- return (this.config.Has("core") && this.config["core"])
+ return this._core
}
GetConfigValue(key, defaultValue := "") {
@@ -28,7 +30,20 @@ class ModuleBase {
}
GetVersion() {
- return this.moduleInfo.Has("version") ? this.moduleInfo["version"] : ""
+ versionStr := this.moduleInfo.Has("version") ? this.moduleInfo["version"] : ""
+
+ if (versionStr == "{{VERSION}}") {
+
+ if (AppBase.Instance) {
+ versionStr := AppBase.Instance.Version
+ }
+
+ if (versionStr == "{{VERSION}}") {
+ versionStr := "Core"
+ }
+ }
+
+ return versionStr
}
GetServiceFiles() {
diff --git a/Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk b/Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk
index 61ead15e..a33e9428 100644
--- a/Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk
+++ b/Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk
@@ -8,7 +8,7 @@ class ThemeBase {
defaultTheme := "Lightpad"
vars := Map()
colors := Map("background", "FFFFFF", "text", "000000", "textInactive", "959595", "accent", "9466FC", "accentBright", "EEE6FF", "accentBg", "8A57F0", "transColor", "")
- themeAssets := Map("logo", "Resources\Graphics\Logo.png", "icon", "Resources\Graphics\Launchpad.ico", "spinner", "Resources\Graphics\Spinner.gif")
+ themeAssets := Map("logo", "Resources\Graphics\logo.png", "icon", "Resources\Graphics\launchpad.ico", "spinner", "Resources\Graphics\spinner.gif")
symbols := Map()
buttons := Map("height", Map("s", 20, "m", 30, "l", 40, "xl", 80), "fixedWidth", Map("s", 80, "m", 100, "l", 120, "xl", 140))
labels := Map("height", "auto", "fixedWidth", 75, "font", "normal")
diff --git a/Lib/Shared/Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk b/Lib/Shared/Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk
index cb9e37da..aab7342d 100644
--- a/Lib/Shared/Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk
+++ b/Lib/Shared/Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk
@@ -25,7 +25,12 @@ class IncludeWriterBase {
updated := this.FilesAreDifferent(this.tmpPath, this.outputPath)
if (updated) {
- FileDelete(this.outputPath)
+ try {
+ FileDelete(this.outputPath)
+ } catch Any {
+ throw AppException("Unable to delete file path " . this.outputPath)
+ }
+
}
}
diff --git a/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk b/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk
index fdf689d8..fe00f734 100644
--- a/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk
+++ b/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk
@@ -77,7 +77,7 @@ class VersionChecker {
}
incrementIndex := versionArr.Length - 1
- } else if (versionArr[0] == "0") {
+ } else if (versionArr[1] == "0") {
incrementIndex := versionArr.Length
}
diff --git a/Lib/TestLib/Test/AppTestBase.ahk b/Lib/TestLib/Test/AppTestBase.ahk
index 028d322c..4414cacf 100644
--- a/Lib/TestLib/Test/AppTestBase.ahk
+++ b/Lib/TestLib/Test/AppTestBase.ahk
@@ -6,12 +6,12 @@ class AppTestBase extends TestBase {
GetTestAppConfig() {
config := Map(
"appName", "Test App",
- "developer", "Test Developer",
"appDir", A_ScriptDir,
"tmpDir", this.testDir . "\Temp",
"dataDir", this.testDir . "\Data",
"version", this.testAppVersion,
"parameters", Map(
+ "app.developer", "Test Developer",
"config.flush_cache_on_exit", false,
"config.logging_level", "none",
"config.module_dirs", [],
diff --git a/Resources/Graphics/Icons/Dark/Backup.ico b/Resources/Graphics/Icons/Dark/backup.ico
similarity index 100%
rename from Resources/Graphics/Icons/Dark/Backup.ico
rename to Resources/Graphics/Icons/Dark/backup.ico
diff --git a/Resources/Graphics/Icons/Dark/Config.ico b/Resources/Graphics/Icons/Dark/config.ico
similarity index 100%
rename from Resources/Graphics/Icons/Dark/Config.ico
rename to Resources/Graphics/Icons/Dark/config.ico
diff --git a/Resources/Graphics/Icons/Dark/Game.ico b/Resources/Graphics/Icons/Dark/game.ico
similarity index 100%
rename from Resources/Graphics/Icons/Dark/Game.ico
rename to Resources/Graphics/Icons/Dark/game.ico
diff --git a/Resources/Graphics/Icons/Dark/Logo.ico b/Resources/Graphics/Icons/Dark/logo.ico
similarity index 100%
rename from Resources/Graphics/Icons/Dark/Logo.ico
rename to Resources/Graphics/Icons/Dark/logo.ico
diff --git a/Resources/Graphics/Icons/Dark/Module.png b/Resources/Graphics/Icons/Dark/module.png
similarity index 100%
rename from Resources/Graphics/Icons/Dark/Module.png
rename to Resources/Graphics/Icons/Dark/module.png
diff --git a/Resources/Graphics/Icons/Dark/Platform.ico b/Resources/Graphics/Icons/Dark/platform.ico
similarity index 100%
rename from Resources/Graphics/Icons/Dark/Platform.ico
rename to Resources/Graphics/Icons/Dark/platform.ico
diff --git a/Resources/Graphics/Icons/Gradient/Backup.ico b/Resources/Graphics/Icons/Gradient/backup.ico
similarity index 100%
rename from Resources/Graphics/Icons/Gradient/Backup.ico
rename to Resources/Graphics/Icons/Gradient/backup.ico
diff --git a/Resources/Graphics/Icons/Gradient/Config.ico b/Resources/Graphics/Icons/Gradient/config.ico
similarity index 100%
rename from Resources/Graphics/Icons/Gradient/Config.ico
rename to Resources/Graphics/Icons/Gradient/config.ico
diff --git a/Resources/Graphics/Icons/Gradient/Game.ico b/Resources/Graphics/Icons/Gradient/game.ico
similarity index 100%
rename from Resources/Graphics/Icons/Gradient/Game.ico
rename to Resources/Graphics/Icons/Gradient/game.ico
diff --git a/Resources/Graphics/Icons/Gradient/Logo.ico b/Resources/Graphics/Icons/Gradient/logo.ico
similarity index 100%
rename from Resources/Graphics/Icons/Gradient/Logo.ico
rename to Resources/Graphics/Icons/Gradient/logo.ico
diff --git a/Resources/Graphics/Icons/Gradient/Platform.ico b/Resources/Graphics/Icons/Gradient/platform.ico
similarity index 100%
rename from Resources/Graphics/Icons/Gradient/Platform.ico
rename to Resources/Graphics/Icons/Gradient/platform.ico
diff --git a/Resources/Graphics/Icons/Light/Backup.ico b/Resources/Graphics/Icons/Light/backup.ico
similarity index 100%
rename from Resources/Graphics/Icons/Light/Backup.ico
rename to Resources/Graphics/Icons/Light/backup.ico
diff --git a/Resources/Graphics/Icons/Light/Config.ico b/Resources/Graphics/Icons/Light/config.ico
similarity index 100%
rename from Resources/Graphics/Icons/Light/Config.ico
rename to Resources/Graphics/Icons/Light/config.ico
diff --git a/Resources/Graphics/Icons/Light/Game.ico b/Resources/Graphics/Icons/Light/game.ico
similarity index 100%
rename from Resources/Graphics/Icons/Light/Game.ico
rename to Resources/Graphics/Icons/Light/game.ico
diff --git a/Resources/Graphics/Icons/Light/Logo.ico b/Resources/Graphics/Icons/Light/logo.ico
similarity index 100%
rename from Resources/Graphics/Icons/Light/Logo.ico
rename to Resources/Graphics/Icons/Light/logo.ico
diff --git a/Resources/Graphics/Icons/Light/Module.png b/Resources/Graphics/Icons/Light/module.png
similarity index 100%
rename from Resources/Graphics/Icons/Light/Module.png
rename to Resources/Graphics/Icons/Light/module.png
diff --git a/Resources/Graphics/Icons/Light/Platform.ico b/Resources/Graphics/Icons/Light/platform.ico
similarity index 100%
rename from Resources/Graphics/Icons/Light/Platform.ico
rename to Resources/Graphics/Icons/Light/platform.ico
diff --git a/Resources/Graphics/Launchpad-256.png b/Resources/Graphics/launchpad-256.png
similarity index 100%
rename from Resources/Graphics/Launchpad-256.png
rename to Resources/Graphics/launchpad-256.png
diff --git a/Resources/Graphics/Launchpad-64.png b/Resources/Graphics/launchpad-64.png
similarity index 100%
rename from Resources/Graphics/Launchpad-64.png
rename to Resources/Graphics/launchpad-64.png
diff --git a/Resources/Graphics/Launchpad.ico b/Resources/Graphics/launchpad.ico
similarity index 100%
rename from Resources/Graphics/Launchpad.ico
rename to Resources/Graphics/launchpad.ico
diff --git a/Resources/Graphics/Logo.png b/Resources/Graphics/logo.png
similarity index 100%
rename from Resources/Graphics/Logo.png
rename to Resources/Graphics/logo.png
diff --git a/Resources/Graphics/Spinner-Steam.gif b/Resources/Graphics/spinner-steam.gif
similarity index 100%
rename from Resources/Graphics/Spinner-Steam.gif
rename to Resources/Graphics/spinner-steam.gif
diff --git a/Resources/Graphics/Spinner.gif b/Resources/Graphics/spinner.gif
similarity index 100%
rename from Resources/Graphics/Spinner.gif
rename to Resources/Graphics/spinner.gif
diff --git a/Resources/Themes/Lightpad.json b/Resources/Themes/Lightpad.json
index 2049c352..bcabf3a0 100644
--- a/Resources/Themes/Lightpad.json
+++ b/Resources/Themes/Lightpad.json
@@ -108,9 +108,9 @@
"transColor": ""
},
"themeAssets": {
- "icon": "Graphics\\Launchpad.ico",
- "logo": "Graphics\\Logo.png",
- "spinner": "Graphics\\Spinner.gif"
+ "icon": "Graphics\\launchpad.ico",
+ "logo": "Graphics\\logo.png",
+ "spinner": "Graphics\\spinner.gif"
},
"symbols": {
"arrowDown": "ArrowDownSymbol",
diff --git a/Resources/Themes/Steampad.json b/Resources/Themes/Steampad.json
index 9b6eae9f..c4e36465 100644
--- a/Resources/Themes/Steampad.json
+++ b/Resources/Themes/Steampad.json
@@ -107,7 +107,7 @@
},
"themeAssets": {
"logo": "",
- "spinner": "Graphics\\Spinner-Steam.gif"
+ "spinner": "Graphics\\spinner-steam.gif"
},
"buttons": {
"styles": {
diff --git a/Scripts/Build.ahk b/Scripts/Build.ahk
index 9d166304..09656b22 100644
--- a/Scripts/Build.ahk
+++ b/Scripts/Build.ahk
@@ -9,8 +9,7 @@ appVersion := "{{VERSION}}"
LaunchpadBuilder(Map(
"appDir", appDir,
"appName", "Launchpad",
- "developer", "Volantis Development",
"version", appVersion,
- "trayIcon", appDir . "\Resources\Graphics\Launchpad.ico",
+ "trayIcon", appDir . "\Resources\Graphics\launchpad.ico",
"console", true,
))