// Copyright (C) 2025 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 using System; using System.Collections.Generic; using System.ComponentModel.Design; using System.IO; using System.Text; using Microsoft.VisualStudio.Shell; namespace QtVsTools { using Core; using Core.MsBuild; using QtVsTools.Core.Common; using VisualStudio; /// /// Command handler /// internal sealed class QtMainMenu { /// /// Gets the instance of the command. /// private static QtMainMenu Instance { get; set; } /// /// Initializes the singleton instance of the command. /// public static void Initialize() { Instance = new QtMainMenu(); } /// /// Command ID. /// TODO: Remove, take form QtMenus.Package /// private enum CommandId { QtVersion = QtMenus.Package.QtVersion, ViewQtHelp = QtMenus.Package.ViewQtHelp, ViewGettingStarted = QtMenus.Package.ViewGettingStarted, LaunchDesigner = QtMenus.Package.LaunchDesigner, LaunchLinguist = QtMenus.Package.LaunchLinguist, OpenProFile = QtMenus.Package.OpenProFile, ImportPriFile = QtMenus.Package.ImportPriFile, ConvertToQtMsBuild = QtMenus.Package.ConvertToQtMsBuild, QtProjectSettings = QtMenus.Package.QtProjectSettings, QtOptions = QtMenus.Package.QtOptions, QtVersions = QtMenus.Package.QtVersions } /// /// Initializes a new instance of the class. /// Adds our command handlers for menu (commands must exist in the command table file) /// /// Owner package, not null. private QtMainMenu() { var commandService = VsServiceProvider .GetService(); if (commandService == null) return; foreach (int id in Enum.GetValues(typeof(CommandId))) { var command = new OleMenuCommand(ExecHandler, new CommandID(QtMenus.Package.Guid, id)); command.BeforeQueryStatus += BeforeQueryStatus; commandService.AddCommand(command); } } private static void ExecHandler(object sender, EventArgs e) { ThreadHelper.ThrowIfNotOnUIThread(); if (sender is not OleMenuCommand command) return; var properties = new Dictionary { {"Command", Enum.GetName(typeof(CommandId), command.CommandID.ID)} }; Telemetry.TrackEvent(typeof(QtMainMenu) + ".ExecHandler", properties); switch (command.CommandID.ID) { case QtMenus.Package.ViewQtHelp: VsShellUtilities.OpenSystemBrowser("https://www.qt.io/developers"); break; case QtMenus.Package.ViewGettingStarted: VsShellUtilities.OpenSystemBrowser("https://doc.qt.io/qtvstools/index.html"); break; case QtMenus.Package.LaunchDesigner: try { const string uiContent = "\n" + " Form\n" + " \n" + " \n" + " Form\n" + " \n" + " \n" + " \n" + " 0\n" + " 0\n" + " 400\n" + " 300\n" + " \n" + " \n" + " \n" + " Form\n" + " \n" + " \n" + "\n"; VsEditor.Open(path: CreateTempFile("form", ".ui", uiContent)); } catch (Exception exception) { exception.Log(); QtVsToolsPackage.Instance.QtDesigner.Start(hideWindow: false); } break; case QtMenus.Package.LaunchLinguist: try { const string tsContent = "\n" + "\n" + "\n" + "\n" + " Empty\n" + " \n" + " Empty\n" + " \n" + " \n" + "\n" + "\n"; VsEditor.Open(path: CreateTempFile("lang", ".ts", tsContent)); } catch (Exception exception) { exception.Log(); QtVsToolsPackage.Instance.QtLinguist.Start(hideWindow: false); } break; case QtMenus.Package.OpenProFile: ProjectImporter.ImportProFile(QtVsToolsPackage.Instance.Dte); break; case QtMenus.Package.ImportPriFile: ProjectImporter.ImportPriFile(QtVsToolsPackage.Instance.Dte, Utils.PackageInstallPath); break; case QtMenus.Package.ConvertToQtMsBuild: MsBuildProjectConverter.SolutionToQtMsBuild(); break; case QtMenus.Package.QtProjectSettings: QtVsToolsPackage.Instance.Dte.ExecuteCommand("Project.Properties"); break; case QtMenus.Package.QtOptions: QtVsToolsPackage.Instance.ShowOptionPage(typeof(Core.Options.QtOptionsPage)); break; case QtMenus.Package.QtVersions: QtVsToolsPackage.Instance.ShowOptionPage(typeof(Core.Options.QtVersionsPage)); break; } } private static void BeforeQueryStatus(object sender, EventArgs e) { ThreadHelper.ThrowIfNotOnUIThread(); if (sender is not OleMenuCommand command) return; var dte = QtVsToolsPackage.Instance.Dte; switch (command.CommandID.ID) { case QtMenus.Package.ViewQtHelp: case QtMenus.Package.ViewGettingStarted: case QtMenus.Package.LaunchDesigner: case QtMenus.Package.LaunchLinguist: case QtMenus.Package.OpenProFile: case QtMenus.Package.QtOptions: case QtMenus.Package.QtVersions: command.Visible = command.Enabled = true; break; case QtMenus.Package.QtVersion: command.Text = "Qt Visual Studio Tools version " + Version.USER_VERSION; command.Visible = true; command.Enabled = false; break; case QtMenus.Package.ImportPriFile: case QtMenus.Package.QtProjectSettings: command.Visible = command.Enabled = HelperFunctions.GetSelectedQtProject(dte) is { }; break; case QtMenus.Package.ConvertToQtMsBuild: command.Visible = command.Enabled = false; foreach (var project in HelperFunctions.ProjectsInSolution(dte)) { switch (MsBuildProjectFormat.GetVersion(project)) { case MsBuildProjectFormat.Version.V1: case MsBuildProjectFormat.Version.V2: command.Visible = command.Enabled = true; return; case >= MsBuildProjectFormat.Version.V3 and < MsBuildProjectFormat.Version.Latest: command.Visible = command.Enabled = true; command.Text = "Upgrade projects to latest Qt project format version"; return; } } break; } } private static string CreateTempFile(string prefix, string extension, string fileContent) { var tempFolderPath = Path.GetTempPath(); for (var i = 1; i <= 9999; i++) { var tmpPath = Path.Combine(tempFolderPath, $"{prefix}-{i:D4}{extension}"); try { // Use FileMode.CreateNew to ensure uniqueness. using var fs = new FileStream(tmpPath, FileMode.CreateNew, FileAccess.Write); using var writer = new StreamWriter(fs, Encoding.UTF8); writer.Write(fileContent); return tmpPath; } catch (IOException) { // The file already exists, so try the next candidate. } } throw new Exception("No available file name could be generated within the range 1-9999."); } } }