/****************************************************************************
|
**
|
** Copyright (C) 2020 The Qt Company Ltd.
|
** Contact: https://www.qt.io/licensing/
|
**
|
** This file is part of the Qt VS Tools.
|
**
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
** Commercial License Usage
|
** Licensees holding valid commercial Qt licenses may use this file in
|
** accordance with the commercial license agreement provided with the
|
** Software or, alternatively, in accordance with the terms contained in
|
** a written agreement between you and The Qt Company. For licensing terms
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
** information use the contact form at https://www.qt.io/contact-us.
|
**
|
** GNU General Public License Usage
|
** Alternatively, this file may be used under the terms of the GNU
|
** General Public License version 3 as published by the Free Software
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
** included in the packaging of this file. Please review the following
|
** information to ensure the GNU General Public License requirements will
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
**
|
** $QT_END_LICENSE$
|
**
|
****************************************************************************/
|
|
using System;
|
using System.Collections.Generic;
|
using System.Drawing;
|
using System.IO;
|
using System.Linq;
|
using System.Windows;
|
using System.Windows.Controls;
|
using System.Windows.Interop;
|
using System.Windows.Media;
|
using System.Windows.Media.Imaging;
|
using Microsoft.Win32;
|
using QtVsTools.Common;
|
|
namespace QtVsTools.Wizards.ProjectWizard
|
{
|
using QtVsTools.Core;
|
|
public partial class ConfigPage : WizardPage
|
{
|
interface ICloneable<T> where T : ICloneable<T>
|
{
|
T Clone();
|
}
|
|
class Module : ICloneable<Module>
|
{
|
public string Name { get; set; }
|
public string Id { get; set; }
|
public bool IsSelected { get; set; }
|
public bool IsReadOnly { get; set; }
|
public bool IsEnabled => !IsReadOnly;
|
|
public Module Clone()
|
{
|
return new Module
|
{
|
Name = Name,
|
Id = Id,
|
IsSelected = IsSelected,
|
IsReadOnly = IsReadOnly
|
};
|
}
|
}
|
|
class Config : ICloneable<Config>, IWizardConfiguration
|
{
|
public string Name { get; set; }
|
public VersionInformation QtVersion { get; set; }
|
public string QtVersionName { get; set; }
|
public string QtVersionPath { get; set; }
|
public string Target { get; set; }
|
public string Platform { get; set; }
|
public bool IsDebug { get; set; }
|
|
public Dictionary<string, Module> Modules { get; set; }
|
|
public IEnumerable<Module> AllModules
|
=> Modules.Values;
|
public IEnumerable<Module> SelectedModules
|
=> Modules.Values.Where((Module m) => m.IsSelected);
|
|
IEnumerable<string> IWizardConfiguration.Modules
|
=> SelectedModules.SelectMany((Module m) => m.Id.Split(' '));
|
|
public Config Clone()
|
{
|
return new Config
|
{
|
Name = Name,
|
QtVersion = QtVersion,
|
QtVersionName = QtVersionName,
|
Target = Target,
|
Platform = Platform,
|
IsDebug = IsDebug,
|
Modules = AllModules
|
.Select((Module m) => m.Clone())
|
.ToDictionary((Module m) => m.Name)
|
};
|
}
|
}
|
|
class CloneableList<T> : List<T> where T : ICloneable<T>
|
{
|
public CloneableList() : base()
|
{ }
|
|
public CloneableList(IEnumerable<T> collection) : base(collection)
|
{ }
|
|
public CloneableList<T> Clone()
|
{
|
return new CloneableList<T>(this.Select(x => x.Clone()));
|
}
|
}
|
|
const string QT_VERSION_DEFAULT = "<Default>";
|
const string QT_VERSION_BROWSE = "<Browse...>";
|
|
IEnumerable<string> qtVersionList = new[] { QT_VERSION_DEFAULT, QT_VERSION_BROWSE }
|
.Union(QtVersionManager.The().GetVersions());
|
|
QtVersionManager qtVersionManager = QtVersionManager.The();
|
VersionInformation defaultQtVersionInfo;
|
|
CloneableList<Config> defaultConfigs;
|
List<Config> currentConfigs;
|
bool initialNextButtonIsEnabled;
|
bool initialFinishButtonIsEnabled;
|
|
public ConfigPage()
|
{
|
InitializeComponent();
|
|
string defaultQtVersionName = qtVersionManager.GetDefaultVersion();
|
defaultQtVersionInfo = qtVersionManager.GetVersionInfo(defaultQtVersionName);
|
|
ErrorIcon.Source = Imaging.CreateBitmapSourceFromHIcon(
|
SystemIcons.Exclamation.Handle, Int32Rect.Empty,
|
BitmapSizeOptions.FromEmptyOptions());
|
|
DataContext = this;
|
Loaded += OnLoaded;
|
}
|
|
void OnLoaded(object sender, RoutedEventArgs e)
|
{
|
Loaded -= OnLoaded;
|
|
var qtModules = QtModules.Instance.GetAvailableModules()
|
.Where((QtModule mi) => mi.Selectable)
|
.Select((QtModule mi) => new Module()
|
{
|
Name = mi.Name,
|
Id = mi.proVarQT,
|
IsSelected = Data.DefaultModules.Contains(mi.LibraryPrefix),
|
IsReadOnly = Data.DefaultModules.Contains(mi.LibraryPrefix),
|
});
|
|
qtVersionList = new[] { QT_VERSION_DEFAULT, QT_VERSION_BROWSE }
|
.Union(QtVersionManager.The().GetVersions());
|
|
if (defaultQtVersionInfo == null) {
|
Validate();
|
return;
|
}
|
|
defaultConfigs = new CloneableList<Config> {
|
new Config {
|
Name = "Debug",
|
IsDebug = true,
|
QtVersion = defaultQtVersionInfo,
|
QtVersionName = defaultQtVersionInfo.name,
|
Target = defaultQtVersionInfo.isWinRT()
|
? ProjectTargets.WindowsStore.Cast<string>()
|
: ProjectTargets.Windows.Cast<string>(),
|
Platform = defaultQtVersionInfo.is64Bit()
|
? ProjectPlatforms.X64.Cast<string>()
|
: ProjectPlatforms.Win32.Cast<string>(),
|
Modules = qtModules.ToDictionary((Module m) => m.Name),
|
},
|
new Config {
|
Name = "Release",
|
IsDebug = false,
|
QtVersion = defaultQtVersionInfo,
|
QtVersionName = defaultQtVersionInfo.name,
|
Target = defaultQtVersionInfo.isWinRT()
|
? ProjectTargets.WindowsStore.Cast<string>()
|
: ProjectTargets.Windows.Cast<string>(),
|
Platform = defaultQtVersionInfo.is64Bit()
|
? ProjectPlatforms.X64.Cast<string>()
|
: ProjectPlatforms.Win32.Cast<string>(),
|
Modules = qtModules.ToDictionary((Module m) => m.Name),
|
}
|
};
|
currentConfigs = defaultConfigs.Clone();
|
ConfigTable.ItemsSource = currentConfigs;
|
|
initialNextButtonIsEnabled = NextButton.IsEnabled;
|
initialFinishButtonIsEnabled = FinishButton.IsEnabled;
|
|
Validate();
|
}
|
|
/// <summary>
|
/// Callback to validate selected configurations.
|
/// Must return an error message in case of failed validation.
|
/// Otherwise, return empty string or null.
|
/// </summary>
|
public Func<IEnumerable<IWizardConfiguration>, string> ValidateConfigs { get; set; }
|
|
void Validate()
|
{
|
if (currentConfigs == null) {
|
ErrorMsg.Content = "Register at least one Qt version using \"Qt VS Tools\"" +
|
" -> \"Qt Options\".";
|
ErrorPanel.Visibility = Visibility.Visible;
|
NextButton.IsEnabled = false;
|
FinishButton.IsEnabled = false;
|
} else if (currentConfigs // "$(Configuration)|$(Platform)" must be unique
|
.GroupBy((Config c) => string.Format("{0}|{1}", c.Name, c.Platform))
|
.Where((IGrouping<string, Config> g) => g.Count() > 1)
|
.Any()) {
|
ErrorMsg.Content = "(Configuration, Platform) must be unique";
|
ErrorPanel.Visibility = Visibility.Visible;
|
NextButton.IsEnabled = false;
|
FinishButton.IsEnabled = false;
|
} else if (ValidateConfigs != null
|
&& ValidateConfigs(currentConfigs) is string errorMsg
|
&& !string.IsNullOrEmpty(errorMsg)) {
|
ErrorMsg.Content = errorMsg;
|
ErrorPanel.Visibility = Visibility.Visible;
|
NextButton.IsEnabled = false;
|
FinishButton.IsEnabled = false;
|
} else {
|
ErrorMsg.Content = string.Empty;
|
ErrorPanel.Visibility = Visibility.Hidden;
|
NextButton.IsEnabled = initialNextButtonIsEnabled;
|
FinishButton.IsEnabled = initialFinishButtonIsEnabled;
|
}
|
}
|
|
void RemoveConfig_Click(object sender, RoutedEventArgs e)
|
{
|
if (sender is Button buttonRemove
|
&& GetBinding(buttonRemove) is Config config) {
|
currentConfigs.Remove(config);
|
if (!currentConfigs.Any()) {
|
currentConfigs = defaultConfigs.Clone();
|
ConfigTable.ItemsSource = currentConfigs;
|
}
|
ConfigTable.Items.Refresh();
|
Validate();
|
}
|
}
|
|
void DuplicateConfig_Click(object sender, RoutedEventArgs e)
|
{
|
if (sender is Button buttonDuplicate
|
&& GetBinding(buttonDuplicate) is Config config) {
|
currentConfigs.Add(config.Clone());
|
ConfigTable.Items.Refresh();
|
Validate();
|
}
|
}
|
|
void Name_TextChanged(object sender, TextChangedEventArgs e)
|
{
|
if (sender is TextBox txt && GetBinding(txt) is Config cfg)
|
cfg.Name = txt.Text;
|
Validate();
|
}
|
|
void QtVersion_ComboBox_Loaded(object sender, RoutedEventArgs e)
|
{
|
if (sender is ComboBox comboBoxQtVersion
|
&& GetBinding(comboBoxQtVersion) is Config config) {
|
comboBoxQtVersion.IsEnabled = false;
|
comboBoxQtVersion.ItemsSource = qtVersionList;
|
comboBoxQtVersion.Text = config.QtVersionName;
|
comboBoxQtVersion.IsEnabled = true;
|
}
|
}
|
|
void QtVersion_TextChanged(object sender, TextChangedEventArgs e)
|
{
|
if (sender is ComboBox comboBoxQtVersion
|
&& comboBoxQtVersion.IsEnabled
|
&& GetBinding(comboBoxQtVersion) is Config config
|
&& config.QtVersionName != comboBoxQtVersion.Text) {
|
var oldQtVersion = config.QtVersion;
|
if (comboBoxQtVersion.Text == QT_VERSION_DEFAULT) {
|
config.QtVersion = defaultQtVersionInfo;
|
config.QtVersionName = defaultQtVersionInfo.name;
|
config.QtVersionPath = defaultQtVersionInfo.qtDir;
|
comboBoxQtVersion.Text = defaultQtVersionInfo.name;
|
} else if (comboBoxQtVersion.Text == QT_VERSION_BROWSE) {
|
var openFileDialog = new OpenFileDialog
|
{
|
Filter = "qmake (qmake.exe)|qmake.exe"
|
};
|
if (openFileDialog.ShowDialog() == true) {
|
IEnumerable<string> binPath = Path.GetDirectoryName(openFileDialog.FileName)
|
.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
string lastDirName = binPath.LastOrDefault();
|
if ("bin".Equals(lastDirName, StringComparison.InvariantCultureIgnoreCase))
|
binPath = binPath.Take(binPath.Count() - 1);
|
|
var qtVersion = string.Join(
|
Path.DirectorySeparatorChar.ToString(), binPath);
|
var versionInfo = VersionInformation.Get(qtVersion);
|
if (versionInfo != null) {
|
versionInfo.name = qtVersion;
|
config.QtVersion = versionInfo;
|
config.QtVersionName = versionInfo.name;
|
config.QtVersionPath = config.QtVersion.qtDir;
|
}
|
}
|
comboBoxQtVersion.Text = config.QtVersionName;
|
} else if (qtVersionManager.GetVersions().Contains(comboBoxQtVersion.Text)) {
|
config.QtVersion = qtVersionManager.GetVersionInfo(comboBoxQtVersion.Text);
|
config.QtVersionName = comboBoxQtVersion.Text;
|
config.QtVersionPath = qtVersionManager.GetInstallPath(comboBoxQtVersion.Text);
|
} else {
|
config.QtVersion = null;
|
config.QtVersionName = config.QtVersionPath = comboBoxQtVersion.Text;
|
}
|
|
if (oldQtVersion != config.QtVersion) {
|
if (config.QtVersion != null) {
|
config.Target = config.QtVersion.isWinRT()
|
? ProjectTargets.WindowsStore.Cast<string>()
|
: ProjectTargets.Windows.Cast<string>();
|
config.Platform = config.QtVersion.is64Bit()
|
? ProjectPlatforms.X64.Cast<string>()
|
: ProjectPlatforms.Win32.Cast<string>();
|
} else if (config.QtVersionPath.StartsWith("SSH:")) {
|
config.Target = ProjectTargets.LinuxSSH.Cast<string>();
|
} else if (config.QtVersionPath.StartsWith("WSL:")) {
|
config.Target = ProjectTargets.LinuxWSL.Cast<string>();
|
}
|
ConfigTable.Items.Refresh();
|
}
|
Validate();
|
}
|
}
|
|
void Target_ComboBox_Loaded(object sender, RoutedEventArgs e)
|
{
|
if (sender is ComboBox comboBoxTarget
|
&& GetBinding(comboBoxTarget) is Config config) {
|
comboBoxTarget.IsEnabled = false;
|
comboBoxTarget.ItemsSource = EnumExt.GetValues<string>(typeof(ProjectTargets));
|
comboBoxTarget.Text = config.Target;
|
comboBoxTarget.IsEnabled = true;
|
}
|
}
|
|
void Target_TextChanged(object sender, TextChangedEventArgs e)
|
{
|
if (sender is ComboBox comboBoxTarget
|
&& comboBoxTarget.IsEnabled
|
&& GetBinding(comboBoxTarget) is Config config
|
&& config.Target != comboBoxTarget.Text) {
|
config.Target = comboBoxTarget.Text;
|
ConfigTable.Items.Refresh();
|
Validate();
|
}
|
}
|
|
void Platform_ComboBox_Loaded(object sender, RoutedEventArgs e)
|
{
|
if (sender is ComboBox comboBoxPlatform
|
&& GetBinding(comboBoxPlatform) is Config config) {
|
comboBoxPlatform.IsEnabled = false;
|
comboBoxPlatform.ItemsSource = EnumExt.GetValues<string>(typeof(ProjectPlatforms));
|
comboBoxPlatform.Text = config.Platform;
|
comboBoxPlatform.IsEnabled = true;
|
}
|
}
|
|
void Platform_TextChanged(object sender, TextChangedEventArgs e)
|
{
|
if (sender is ComboBox comboBoxPlatform
|
&& comboBoxPlatform.IsEnabled
|
&& GetBinding(comboBoxPlatform) is Config config
|
&& config.Platform != comboBoxPlatform.Text) {
|
config.Platform = comboBoxPlatform.Text;
|
ConfigTable.Items.Refresh();
|
Validate();
|
}
|
}
|
|
void Debug_Click(object sender, RoutedEventArgs e)
|
{
|
if (sender is CheckBox checkBox && GetBinding(checkBox) is Config config) {
|
config.IsDebug = checkBox.IsChecked ?? false;
|
if (config.IsDebug && config.Name.EndsWith("Release")) {
|
config.Name = string.Format("{0}Debug",
|
config.Name.Substring(0, config.Name.Length - "Release".Length));
|
ConfigTable.Items.Refresh();
|
} else if (!config.IsDebug && config.Name.EndsWith("Debug")) {
|
config.Name = string.Format("{0}Release",
|
config.Name.Substring(0, config.Name.Length - "Debug".Length));
|
ConfigTable.Items.Refresh();
|
}
|
Validate();
|
}
|
}
|
|
void Module_Click(object sender, RoutedEventArgs e)
|
{
|
if (sender is CheckBox checkBoxModule
|
&& (checkBoxModule.TemplatedParent as ContentPresenter)?.Content is Module module
|
&& GetBinding(checkBoxModule) is Config config
|
&& FindAncestor(checkBoxModule, "Modules") is ComboBox comboBoxModules
|
&& FindDescendant(comboBoxModules, "SelectedModules") is ListView selectedModules) {
|
selectedModules.ItemsSource = config.SelectedModules;
|
Validate();
|
}
|
}
|
|
protected override void OnNextButtonClick(object sender, RoutedEventArgs e)
|
{
|
Data.Configs = currentConfigs.Cast<IWizardConfiguration>();
|
base.OnNextButtonClick(sender, e);
|
}
|
|
protected override void OnFinishButtonClick(object sender, RoutedEventArgs e)
|
{
|
Data.Configs = currentConfigs.Cast<IWizardConfiguration>();
|
base.OnFinishButtonClick(sender, e);
|
}
|
|
static object GetBinding(FrameworkElement control)
|
{
|
if (control.BindingGroup == null
|
|| control.BindingGroup.Items == null
|
|| control.BindingGroup.Items.Count == 0) {
|
return null;
|
}
|
return control.BindingGroup.Items[0];
|
}
|
|
static FrameworkElement FindAncestor(FrameworkElement control, string name)
|
{
|
while (control != null && control.Name != name) {
|
object parent = control.Parent
|
?? control.TemplatedParent
|
?? VisualTreeHelper.GetParent(control);
|
control = parent as FrameworkElement;
|
}
|
return control;
|
}
|
|
static FrameworkElement FindDescendant(FrameworkElement control, string name)
|
{
|
var stack = new Stack<FrameworkElement>(new[] { control });
|
while (stack.Any()) {
|
control = stack.Pop();
|
if (control?.Name == name && control is FrameworkElement result)
|
return result;
|
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(control); ++i) {
|
if (VisualTreeHelper.GetChild(control, i) is FrameworkElement child)
|
stack.Push(child);
|
}
|
}
|
return null;
|
}
|
}
|
}
|