/****************************************************************************
|
**
|
** Copyright (C) 2016 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 EnvDTE;
|
using Microsoft.VisualStudio.Settings;
|
using Microsoft.VisualStudio.Shell;
|
using Microsoft.VisualStudio.Shell.Interop;
|
using Microsoft.VisualStudio.Shell.Settings;
|
using QtVsTools.Core;
|
using QtVsTools.VisualStudio;
|
using System;
|
using System.Collections.Generic;
|
using System.ComponentModel.Design;
|
using System.Data.Common;
|
using System.Data.SQLite;
|
using System.IO;
|
using System.Linq;
|
using System.Threading.Tasks;
|
using Task = System.Threading.Tasks.Task;
|
|
namespace QtVsTools
|
{
|
public class QtHelp
|
{
|
public enum SourcePreference { Online, Offline }
|
|
public static QtHelp Instance
|
{
|
get;
|
private set;
|
}
|
|
public static void Initialize(Package package)
|
{
|
Instance = new QtHelp(package);
|
}
|
|
const int F1QtHelpId = 0x0502;
|
|
readonly Package package;
|
public static readonly Guid MainMenuGuid = new Guid("58f83fff-d39d-4c66-810b-2702e1f04e73");
|
|
QtHelp(Package pkg)
|
{
|
if (pkg == null)
|
throw new ArgumentNullException("package");
|
package = pkg;
|
|
var commandService = VsServiceProvider
|
.GetService<IMenuCommandService, OleMenuCommandService>();
|
if (commandService == null)
|
return;
|
|
var menuCommandID = new CommandID(MainMenuGuid, F1QtHelpId);
|
commandService.AddCommand(new MenuCommand(F1QtHelpEventHandler, menuCommandID));
|
}
|
|
IServiceProvider ServiceProvider
|
{
|
get { return package; }
|
}
|
|
static bool IsSuperfluousCharacter(string text)
|
{
|
switch (text) {
|
case " ":
|
case ";":
|
case ".":
|
case "<":
|
case ">":
|
case "{":
|
case "}":
|
case "(":
|
case ")":
|
case ":":
|
case ",":
|
case "/":
|
case "\\":
|
case "^":
|
case "%":
|
case "+":
|
case "-":
|
case "*":
|
case "\t":
|
case "&":
|
case "\"":
|
case "!":
|
case "[":
|
case "]":
|
case "|":
|
case "'":
|
case "~":
|
case "#":
|
case "=":
|
return true; // nothing we are interested in
|
}
|
return false;
|
}
|
|
static string GetString(DbDataReader reader, int index)
|
{
|
if (!reader.IsDBNull(index))
|
return reader.GetString(index);
|
return string.Empty;
|
}
|
|
void F1QtHelpEventHandler(object sender, EventArgs args)
|
{
|
QueryEditorContextHelp(true);
|
}
|
|
public static bool QueryEditorContextHelp(bool defaultTryOnline = false)
|
{
|
try {
|
var dte = VsServiceProvider.GetService<SDTE, DTE>();
|
var objTextDocument = dte?.ActiveDocument?.Object() as TextDocument;
|
if (objTextDocument == null)
|
return false;
|
|
var keyword = string.Empty;
|
var selection = objTextDocument.Selection;
|
if (selection.IsEmpty) { // no selection inside the document
|
var line = selection.ActivePoint.Line; // current line
|
var offset = selection.ActivePoint.LineCharOffset; // current char offset
|
|
selection.CharLeft(true); // try the character before the cursor
|
if (!selection.IsEmpty) {
|
keyword = selection.Text; // something in front of the cursor
|
selection.CharRight(true); // reset to origin
|
if (!IsSuperfluousCharacter(keyword)) {
|
// move the selection to the start of the word
|
selection.WordLeft(true);
|
selection.MoveToPoint(selection.TopPoint);
|
}
|
}
|
selection.WordRight(true); // select the word
|
keyword = selection.Text; // get the selected text
|
selection.MoveToLineAndOffset(line, offset); // reset
|
} else {
|
keyword = selection.Text;
|
}
|
|
keyword = keyword.Trim();
|
if (keyword.Length <= 1 || IsSuperfluousCharacter(keyword))
|
return false; // suppress single character, operators etc...
|
|
var qtVersion = "$(DefaultQtVersion)";
|
var project = HelperFunctions.GetSelectedQtProject(dte);
|
if (project == null) {
|
project = HelperFunctions.GetSelectedProject(dte);
|
if (project != null && HelperFunctions.IsQMakeProject(project)) {
|
var qmakeQtDir = HelperFunctions.GetQtDirFromQMakeProject(project);
|
qtVersion = QtVersionManager.The().GetQtVersionFromInstallDir(qmakeQtDir);
|
}
|
} else {
|
qtVersion = QtVersionManager.The().GetProjectQtVersion(project);
|
}
|
|
var info = QtVersionManager.The().GetVersionInfo(qtVersion);
|
var docPath = info?.QtInstallDocs;
|
if (string.IsNullOrEmpty(docPath) || !Directory.Exists(docPath))
|
return false;
|
|
var qchFiles = Directory.GetFiles(docPath, "*?.qch");
|
if (qchFiles.Length == 0)
|
return false;
|
|
var offline = QtVsToolsPackage.Instance.Options.HelpPreference == SourcePreference.Offline;
|
|
var linksForKeyword = string.Format("SELECT d.Title, f.Name, e.Name, "
|
+ "d.Name, a.Anchor FROM IndexTable a, FileNameTable d, FolderTable e, "
|
+ "NamespaceTable f WHERE a.FileId=d.FileId AND d.FolderId=e.Id AND "
|
+ "a.NamespaceId=f.Id AND a.Name='{0}'", keyword);
|
|
var links = new Dictionary<string, string>();
|
var builder = new SQLiteConnectionStringBuilder
|
{
|
ReadOnly = true
|
};
|
foreach (var file in qchFiles) {
|
builder.DataSource = file;
|
using (var connection = new SQLiteConnection(builder.ToString())) {
|
connection.Open();
|
using (var command = new SQLiteCommand(linksForKeyword, connection)) {
|
using (var reader =
|
Task.Run(async () => await command.ExecuteReaderAsync()).Result) {
|
while (reader.Read()) {
|
var title = GetString(reader, 0);
|
if (string.IsNullOrWhiteSpace(title))
|
title = keyword + ':' + GetString(reader, 3);
|
var path = string.Empty;
|
if (offline) {
|
path = "file:///" + Path.Combine(docPath,
|
GetString(reader, 2), GetString(reader, 3));
|
} else {
|
path = "https://" + Path.Combine("doc.qt.io",
|
$"qt-{info.qtMajor}", GetString(reader, 3));
|
}
|
if (!string.IsNullOrWhiteSpace(GetString(reader, 4)))
|
path += "#" + GetString(reader, 4);
|
links.Add(title, path);
|
}
|
}
|
}
|
}
|
}
|
|
var uri = string.Empty;
|
switch (links.Values.Count) {
|
case 0:
|
if (!offline && defaultTryOnline) {
|
uri = new UriBuilder($"https://doc.qt.io/qt-{info.qtMajor}/search-results.html")
|
{
|
Query = "q=" + keyword
|
}.ToString();
|
} else {
|
return false;
|
}
|
break;
|
case 1:
|
uri = links.First().Value;
|
break;
|
default:
|
var dialog = new QtHelpLinkChooser
|
{
|
Links = links,
|
Keyword = keyword,
|
ShowInTaskbar = false
|
};
|
if (!dialog.ShowModal().GetValueOrDefault())
|
return false;
|
uri = dialog.Link
|
.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
break;
|
}
|
|
if (string.IsNullOrEmpty(uri)) { // offline mode without a single search hit
|
VsShellUtilities.ShowMessageBox(Instance.ServiceProvider,
|
"Your search - " + keyword + " - did not match any documents.",
|
string.Empty, OLEMSGICON.OLEMSGICON_INFO, OLEMSGBUTTON.OLEMSGBUTTON_OK,
|
OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
|
} else {
|
var helpUri = new Uri(uri.Replace('\\', '/'));
|
if (helpUri.IsFile && !File.Exists(helpUri.LocalPath)) {
|
VsShellUtilities.ShowMessageBox(Instance.ServiceProvider,
|
"Your search - " + keyword + " - did match a document, but it could "
|
+ "not be found on disk. To use the online help, select: "
|
+ "Help | Set Qt Help Preference | Use Online Documentation",
|
string.Empty, OLEMSGICON.OLEMSGICON_INFO, OLEMSGBUTTON.OLEMSGBUTTON_OK,
|
OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
|
} else {
|
VsShellUtilities.OpenSystemBrowser(HelperFunctions.ChangePathFormat(uri));
|
}
|
}
|
} catch (Exception e) {
|
Messages.Print(
|
e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
|
}
|
return true;
|
}
|
}
|
}
|