Merge remote-tracking branch 'main-source/next' into next
This commit is contained in:
commit
ab8ca2d832
31 changed files with 317 additions and 34 deletions
|
@ -17,6 +17,7 @@
|
|||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
@ -29,7 +30,9 @@
|
|||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/naref.h>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
|
||||
|
@ -266,6 +269,49 @@ int Addon::getLoadSequenceNumber() const
|
|||
void Addon::setLoadSequenceNumber(int num)
|
||||
{ _loadSequenceNumber = num; }
|
||||
|
||||
std::multimap<UrlType, QualifiedUrl> Addon::getUrls() const
|
||||
{
|
||||
std::multimap<UrlType, QualifiedUrl> res;
|
||||
|
||||
auto appendIfNonEmpty = [&res](UrlType type, string url, string detail = "") {
|
||||
if (!url.empty()) {
|
||||
res.emplace(type, QualifiedUrl(type, std::move(url), std::move(detail)));
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& author: _authors) {
|
||||
appendIfNonEmpty(UrlType::author, author->getUrl(), author->getName());
|
||||
}
|
||||
|
||||
for (const auto& maint: _maintainers) {
|
||||
appendIfNonEmpty(UrlType::maintainer, maint->getUrl(), maint->getName());
|
||||
}
|
||||
|
||||
appendIfNonEmpty(UrlType::homePage, getHomePage());
|
||||
appendIfNonEmpty(UrlType::download, getDownloadUrl());
|
||||
appendIfNonEmpty(UrlType::support, getSupportUrl());
|
||||
appendIfNonEmpty(UrlType::codeRepository, getCodeRepositoryUrl());
|
||||
appendIfNonEmpty(UrlType::license, getLicenseUrl());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<SGPropertyNode_ptr> Addon::getMenubarNodes() const
|
||||
{ return _menubarNodes; }
|
||||
|
||||
void Addon::setMenubarNodes(const std::vector<SGPropertyNode_ptr>& menubarNodes)
|
||||
{ _menubarNodes = menubarNodes; }
|
||||
|
||||
void Addon::addToFGMenubar() const
|
||||
{
|
||||
SGPropertyNode* menuRootNode = fgGetNode("/sim/menubar/default", true);
|
||||
|
||||
for (const auto& node: getMenubarNodes()) {
|
||||
SGPropertyNode* childNode = menuRootNode->addChild("menu");
|
||||
::copyProperties(node.ptr(), childNode);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Addon::str() const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
@ -311,9 +357,81 @@ Addon Addon::fromAddonDir(const SGPath& addonPath)
|
|||
addon.setSupportUrl(std::move(metadata.supportUrl));
|
||||
addon.setCodeRepositoryUrl(std::move(metadata.codeRepositoryUrl));
|
||||
|
||||
SGPath menuFile = addonPath / "addon-menubar-items.xml";
|
||||
|
||||
if (menuFile.exists()) {
|
||||
addon.setMenubarNodes(readMenubarItems(menuFile));
|
||||
}
|
||||
|
||||
return addon;
|
||||
}
|
||||
|
||||
// Static method
|
||||
std::vector<SGPropertyNode_ptr>
|
||||
Addon::readMenubarItems(const SGPath& menuFile)
|
||||
{
|
||||
SGPropertyNode rootNode;
|
||||
|
||||
try {
|
||||
readProperties(menuFile, &rootNode);
|
||||
} catch (const sg_exception &e) {
|
||||
throw errors::error_loading_menubar_items_file(
|
||||
"unable to load add-on menu bar items from file '" +
|
||||
menuFile.utf8Str() + "': " + e.getFormattedMessage());
|
||||
}
|
||||
|
||||
// Check the 'meta' section
|
||||
SGPropertyNode *metaNode = rootNode.getChild("meta");
|
||||
if (metaNode == nullptr) {
|
||||
throw errors::error_loading_menubar_items_file(
|
||||
"no /meta node found in add-on menu bar items file '" +
|
||||
menuFile.utf8Str() + "'");
|
||||
}
|
||||
|
||||
// Check the file type
|
||||
SGPropertyNode *fileTypeNode = metaNode->getChild("file-type");
|
||||
if (fileTypeNode == nullptr) {
|
||||
throw errors::error_loading_menubar_items_file(
|
||||
"no /meta/file-type node found in add-on menu bar items file '" +
|
||||
menuFile.utf8Str() + "'");
|
||||
}
|
||||
|
||||
string fileType = fileTypeNode->getStringValue();
|
||||
if (fileType != "FlightGear add-on menu bar items") {
|
||||
throw errors::error_loading_menubar_items_file(
|
||||
"Invalid /meta/file-type value for add-on menu bar items file '" +
|
||||
menuFile.utf8Str() + "': '" + fileType + "' "
|
||||
"(expected 'FlightGear add-on menu bar items')");
|
||||
}
|
||||
|
||||
// Check the format version
|
||||
SGPropertyNode *fmtVersionNode = metaNode->getChild("format-version");
|
||||
if (fmtVersionNode == nullptr) {
|
||||
throw errors::error_loading_menubar_items_file(
|
||||
"no /meta/format-version node found in add-on menu bar items file '" +
|
||||
menuFile.utf8Str() + "'");
|
||||
}
|
||||
|
||||
int formatVersion = fmtVersionNode->getIntValue();
|
||||
if (formatVersion != 1) {
|
||||
throw errors::error_loading_menubar_items_file(
|
||||
"unknown format version in add-on menu bar items file '" +
|
||||
menuFile.utf8Str() + "': " + std::to_string(formatVersion));
|
||||
}
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG,
|
||||
"Loaded add-on menu bar items from '" << menuFile.utf8Str() + "'");
|
||||
|
||||
SGPropertyNode *menubarItemsNode = rootNode.getChild("menubar-items");
|
||||
std::vector<SGPropertyNode_ptr> res;
|
||||
|
||||
if (menubarItemsNode != nullptr) {
|
||||
res = menubarItemsNode->getChildren("menu");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Static method
|
||||
void Addon::setupGhost(nasal::Hash& addonsModule)
|
||||
{
|
||||
|
|
|
@ -173,6 +173,12 @@ public:
|
|||
// Get all non-empty URLs pertaining to this add-on
|
||||
std::multimap<UrlType, QualifiedUrl> getUrls() const;
|
||||
|
||||
// Getter and setter for the menu bar item nodes of the add-on
|
||||
std::vector<SGPropertyNode_ptr> getMenubarNodes() const;
|
||||
void setMenubarNodes(const std::vector<SGPropertyNode_ptr>& menubarNodes);
|
||||
// Add the menus defined in addon-menubar-items.xml to /sim/menubar/default
|
||||
void addToFGMenubar() const;
|
||||
|
||||
// Simple string representation
|
||||
std::string str() const;
|
||||
|
||||
|
@ -186,6 +192,10 @@ private:
|
|||
static SGPath getMetadataFile(const SGPath& addonPath);
|
||||
SGPath getMetadataFile() const;
|
||||
|
||||
// Read all menus from addon-menubar-items.xml (under the add-on base path)
|
||||
static std::vector<SGPropertyNode_ptr>
|
||||
readMenubarItems(const SGPath& menuFile);
|
||||
|
||||
// The add-on identifier, in reverse DNS style. The AddonManager refuses to
|
||||
// register two add-ons with the same id in a given FlightGear session.
|
||||
std::string _id;
|
||||
|
@ -226,6 +236,8 @@ private:
|
|||
std::string _triggerProperty;
|
||||
// Semantics explained above
|
||||
int _loadSequenceNumber = -1;
|
||||
|
||||
std::vector<SGPropertyNode_ptr> _menubarNodes;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Addon& addon);
|
||||
|
|
|
@ -255,6 +255,14 @@ SGPropertyNode_ptr AddonManager::addonNode(const string& addonId) const
|
|||
return getAddon(addonId)->getAddonNode();
|
||||
}
|
||||
|
||||
void
|
||||
AddonManager::addAddonMenusToFGMenubar() const
|
||||
{
|
||||
for (const auto& addon: _registeredAddons) {
|
||||
addon->addToFGMenubar();
|
||||
}
|
||||
}
|
||||
|
||||
} // of namespace addons
|
||||
|
||||
} // of namespace flightgear
|
||||
|
|
|
@ -86,6 +86,10 @@ public:
|
|||
// Base node pertaining to the add-on in the Global Property Tree
|
||||
SGPropertyNode_ptr addonNode(const std::string& addonId) const;
|
||||
|
||||
// Add the 'menu' nodes defined by each registered add-on to
|
||||
// /sim/menubar/default
|
||||
void addAddonMenusToFGMenubar() const;
|
||||
|
||||
private:
|
||||
// Constructor called from createInstance() only
|
||||
explicit AddonManager() = default;
|
||||
|
|
|
@ -56,6 +56,7 @@ class error;
|
|||
class error_loading_config_file;
|
||||
class no_metadata_file_found;
|
||||
class error_loading_metadata_file;
|
||||
class error_loading_menubar_items_file;
|
||||
class duplicate_registration_attempt;
|
||||
class fg_version_too_old;
|
||||
class fg_version_too_recent;
|
||||
|
|
|
@ -50,6 +50,9 @@ class no_metadata_file_found : public error
|
|||
class error_loading_metadata_file : public error
|
||||
{ using error::error; };
|
||||
|
||||
class error_loading_menubar_items_file : public error
|
||||
{ using error::error; };
|
||||
|
||||
class duplicate_registration_attempt : public error
|
||||
{ using error::error; };
|
||||
|
||||
|
|
|
@ -84,7 +84,12 @@ CatalogRef AddCatalogDialog::addedCatalog()
|
|||
void AddCatalogDialog::setNonInteractiveMode()
|
||||
{
|
||||
m_nonInteractiveMode = true;
|
||||
ui->buttonBox->hide();
|
||||
ui->buttonBox->hide();
|
||||
}
|
||||
|
||||
void AddCatalogDialog::setUpdatingExistingCatalog()
|
||||
{
|
||||
m_updatingExistingCatalog = true;
|
||||
}
|
||||
|
||||
void AddCatalogDialog::setUrlAndDownload(QUrl url)
|
||||
|
@ -187,10 +192,23 @@ void AddCatalogDialog::accept()
|
|||
|
||||
void AddCatalogDialog::reject()
|
||||
{
|
||||
if (m_result && !m_result->id().empty()) {
|
||||
// user may have successfully download the catalog, but choosen
|
||||
// not to add it. so remove it here
|
||||
m_packageRoot->removeCatalogById(m_result->id());
|
||||
// user may have successfully download the catalog, but choosen
|
||||
// not to add it. so remove it here
|
||||
|
||||
// however, if we got in here as part of re-validating an existing
|
||||
// disabled catalog, *but* the re-validation also failed, we do not
|
||||
// want to remove it here.
|
||||
|
||||
if (m_result && !m_result->id().empty())
|
||||
{
|
||||
if (m_updatingExistingCatalog) {
|
||||
qWarning() << "cancelled add, but existing catalog so not removing:"
|
||||
<< QString::fromStdString(m_result->id());
|
||||
} else {
|
||||
qWarning() << "removed cancelled catalog:"
|
||||
<< QString::fromStdString(m_result->id());
|
||||
m_packageRoot->removeCatalogById(m_result->id());
|
||||
}
|
||||
}
|
||||
|
||||
QDialog::reject();
|
||||
|
|
|
@ -52,6 +52,13 @@ public:
|
|||
*/
|
||||
void setNonInteractiveMode();
|
||||
|
||||
/**
|
||||
* @brief setUpdatingExistingCatalog - indicate that this is an update or
|
||||
* fix of an existing catalog, in which case we will treat failures /
|
||||
* cancellation differently.
|
||||
*/
|
||||
void setUpdatingExistingCatalog();
|
||||
|
||||
void setUrlAndDownload(QUrl url);
|
||||
private slots:
|
||||
virtual void reject();
|
||||
|
@ -82,7 +89,7 @@ private:
|
|||
QUrl m_catalogUrl;
|
||||
simgear::pkg::CatalogRef m_result;
|
||||
bool m_nonInteractiveMode = false;
|
||||
|
||||
bool m_updatingExistingCatalog = false;
|
||||
std::unique_ptr<AddCatalogDelegate> m_delegate;
|
||||
};
|
||||
|
||||
|
|
|
@ -67,6 +67,8 @@ protected:
|
|||
qWarning() << "failed refresh of "
|
||||
<< QString::fromStdString(aCatalog->url()) << ":" << aReason << endl;
|
||||
}
|
||||
|
||||
m_model->catalogsRefreshed();
|
||||
}
|
||||
|
||||
void startInstall(InstallRef aInstall) override
|
||||
|
|
|
@ -121,6 +121,7 @@ signals:
|
|||
|
||||
void aircraftNeedingUpdatedChanged();
|
||||
|
||||
void catalogsRefreshed();
|
||||
public slots:
|
||||
void setShowUpdateAll(bool showUpdateAll);
|
||||
|
||||
|
|
|
@ -33,10 +33,13 @@
|
|||
// FlightGear
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
using namespace simgear::pkg;
|
||||
|
||||
CatalogListModel::CatalogListModel(QObject* pr, simgear::pkg::RootRef& rootRef) :
|
||||
QAbstractListModel(pr),
|
||||
m_packageRoot(rootRef)
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
|
||||
CatalogListModel::~CatalogListModel()
|
||||
|
@ -46,21 +49,39 @@ CatalogListModel::~CatalogListModel()
|
|||
void CatalogListModel::refresh()
|
||||
{
|
||||
beginResetModel();
|
||||
m_catalogs = m_packageRoot->allCatalogs();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
int CatalogListModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
return m_packageRoot->catalogs().size();
|
||||
return m_catalogs.size();
|
||||
}
|
||||
|
||||
QVariant CatalogListModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
simgear::pkg::CatalogRef cat = m_packageRoot->catalogs().at(index.row());
|
||||
simgear::pkg::CatalogRef cat = m_catalogs.at(index.row());
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
QString name = QString::fromStdString(cat->name());
|
||||
QString desc = QString::fromStdString(cat->description()).simplified();
|
||||
QString desc;
|
||||
if (cat->isEnabled()) {
|
||||
desc = QString::fromStdString(cat->description()).simplified();
|
||||
} else {
|
||||
switch (cat->status()) {
|
||||
case Delegate::FAIL_NOT_FOUND:
|
||||
desc = tr("The catalog data was not found on the server at the expected location (URL)");
|
||||
break;
|
||||
case Delegate::FAIL_VERSION:
|
||||
desc = tr("The catalog is not comaptible with the version of FlightGear");
|
||||
break;
|
||||
case Delegate::FAIL_HTTP_FORBIDDEN:
|
||||
desc = tr("The catalog server is blocking access from some reason (forbidden)");
|
||||
break;
|
||||
default:
|
||||
desc = tr("disabled due to an internal error");
|
||||
}
|
||||
}
|
||||
return tr("%1 - %2").arg(name).arg(desc);
|
||||
} else if (role == Qt::ToolTipRole) {
|
||||
return QString::fromStdString(cat->url());
|
||||
|
@ -81,3 +102,13 @@ bool CatalogListModel::setData(const QModelIndex &index, const QVariant &value,
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::ItemFlags CatalogListModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
Qt::ItemFlags r = Qt::ItemIsSelectable;
|
||||
const auto cat = m_catalogs.at(index.row());
|
||||
if (cat->isEnabled()) {
|
||||
r |= Qt::ItemIsEnabled;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -45,17 +45,20 @@ public:
|
|||
|
||||
void refresh();
|
||||
|
||||
virtual int rowCount(const QModelIndex& parent) const;
|
||||
int rowCount(const QModelIndex& parent) const override;
|
||||
|
||||
virtual QVariant data(const QModelIndex& index, int role) const;
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
|
||||
virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
|
||||
private slots:
|
||||
|
||||
|
||||
private:
|
||||
simgear::pkg::RootRef m_packageRoot;
|
||||
simgear::pkg::CatalogList m_catalogs;
|
||||
};
|
||||
|
||||
#endif // of FG_GUI_CATALOG_LIST_MODEL
|
||||
|
|
|
@ -104,7 +104,6 @@ LauncherMainWindow::LauncherMainWindow() :
|
|||
this, &LauncherMainWindow::onViewCommandLine);
|
||||
|
||||
m_serversModel = new MPServersModel(this);
|
||||
m_serversModel->refresh();
|
||||
|
||||
// keep the description QLabel in sync as the current item changes
|
||||
connect(m_ui->stateCombo,
|
||||
|
@ -151,7 +150,6 @@ LauncherMainWindow::LauncherMainWindow() :
|
|||
m_ui->locationHistory->setIcon(historyIcon);
|
||||
|
||||
m_aircraftModel = new AircraftItemModel(this);
|
||||
|
||||
m_installedAircraftModel = new AircraftProxyModel(this, m_aircraftModel);
|
||||
m_installedAircraftModel->setInstalledFilterEnabled(true);
|
||||
|
||||
|
@ -185,6 +183,9 @@ LauncherMainWindow::LauncherMainWindow() :
|
|||
this, &LauncherMainWindow::onAircraftPathsChanged);
|
||||
m_ui->stack->addWidget(addOnsPage);
|
||||
|
||||
connect (m_aircraftModel, &AircraftItemModel::catalogsRefreshed,
|
||||
addOnsPage, &AddOnsPage::onCatalogsRefreshed);
|
||||
|
||||
QSettings settings;
|
||||
LocalAircraftCache::instance()->setPaths(settings.value("aircraft-paths").toStringList());
|
||||
LocalAircraftCache::instance()->scanDirs();
|
||||
|
@ -1015,6 +1016,11 @@ void LauncherMainWindow::requestUpdateAllAircraft()
|
|||
});
|
||||
}
|
||||
|
||||
void LauncherMainWindow::queryMPServers()
|
||||
{
|
||||
m_serversModel->refresh();
|
||||
}
|
||||
|
||||
bool LauncherMainWindow::showNoOfficialHanger() const
|
||||
{
|
||||
return shouldShowOfficialCatalogMessage();
|
||||
|
|
|
@ -84,6 +84,8 @@ public:
|
|||
|
||||
Q_INVOKABLE void requestUpdateAllAircraft();
|
||||
|
||||
Q_INVOKABLE void queryMPServers();
|
||||
|
||||
bool showNoOfficialHanger() const;
|
||||
|
||||
Q_INVOKABLE void officialCatalogAction(QString s);
|
||||
|
|
|
@ -13,6 +13,13 @@ Section {
|
|||
+ "flight. This requires a moderately fast Internet connection to be usable. Your aircraft "
|
||||
+ "will be visible to other users online, and you will see their aircraft.")
|
||||
keywords: ["network", "mp", "multiplay","online"]
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
// kick off a server query now
|
||||
_launcher.queryMPServers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
|
@ -100,7 +107,12 @@ Section {
|
|||
}
|
||||
|
||||
onRestore: {
|
||||
// nothing to do, restoration is done by the C++ code
|
||||
if (enableMP.checked) {
|
||||
// only query servers if MP is enabled
|
||||
_launcher.queryMPServers();
|
||||
}
|
||||
|
||||
// restoration is done by the C++ code
|
||||
// in MPServersModel::restoreMPServerSelection
|
||||
}
|
||||
|
||||
|
|
|
@ -787,7 +787,7 @@ void MapWidget::paintRuler()
|
|||
SGVec2d acftPos = project(_aircraft);
|
||||
SGVec2d clickPos = project(_clickGeod);
|
||||
|
||||
glColor4f(0.0, 1.0, 1.0, 0.6);
|
||||
glColor3f(0.0, 1.0, 1.0);
|
||||
drawLine(acftPos, clickPos);
|
||||
|
||||
circleAtAlt(clickPos, 8, 10, 5);
|
||||
|
@ -812,7 +812,7 @@ void MapWidget::paintAircraftLocation(const SGGeod& aircraftPos)
|
|||
double hdg = fgGetDouble("/orientation/heading-deg");
|
||||
|
||||
glLineWidth(2.0);
|
||||
glColor4f(1.0, 1.0, 0.0, 1.0);
|
||||
glColor3f(1.0, 1.0, 0.0);
|
||||
glPushMatrix();
|
||||
glTranslated(loc.x(), loc.y(), 0.0);
|
||||
glRotatef(hdg - _upHeading, 0.0, 0.0, -1.0);
|
||||
|
@ -848,9 +848,9 @@ void MapWidget::paintRoute()
|
|||
}
|
||||
|
||||
if (w < _route->currentIndex()) {
|
||||
glColor4f(0.5, 0.5, 0.5, 0.7);
|
||||
glColor3f(0.5, 0.5, 0.5);
|
||||
} else {
|
||||
glColor4f(1.0, 0.0, 1.0, 1.0);
|
||||
glColor3f(1.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
flightgear::WayptRef wpt(_route->wayptAtIndex(w));
|
||||
|
@ -879,7 +879,7 @@ void MapWidget::paintRoute()
|
|||
}
|
||||
|
||||
SGVec2d p = project(g);
|
||||
glColor4f(1.0, 0.0, 1.0, 1.0);
|
||||
glColor3f(1.0, 0.0, 1.0);
|
||||
circleAtAlt(p, 8, 12, 5);
|
||||
|
||||
std::ostringstream legend;
|
||||
|
@ -912,7 +912,7 @@ void MapWidget::drawFlightHistory()
|
|||
// first pass, draw the actual lines
|
||||
glLineWidth(2.0);
|
||||
|
||||
glColor4f(0.0, 0.0, 1.0, 0.7);
|
||||
glColor3f(0.0, 0.0, 1.0);
|
||||
|
||||
glBegin(GL_LINE_STRIP);
|
||||
for (unsigned int i=0; i<_flightHistoryPath.size(); ++i) {
|
||||
|
@ -987,7 +987,7 @@ void MapWidget::drawLatLonGrid()
|
|||
int ix = 0;
|
||||
int iy = 0;
|
||||
|
||||
glColor4f(0.8, 0.8, 0.8, 1.0);
|
||||
glColor3f(0.8, 0.8, 0.8);
|
||||
glBegin(GL_LINES);
|
||||
bool didDraw;
|
||||
do {
|
||||
|
@ -1038,7 +1038,7 @@ void MapWidget::drawGPSData()
|
|||
SGGeod trackRadial;
|
||||
SGGeodesy::direct(_aircraft, gpsTrackDeg, _drawRangeNm * SG_NM_TO_METER, trackRadial, az2);
|
||||
|
||||
glColor4f(1.0, 1.0, 0.0, 1.0);
|
||||
glColor3f(1.0, 1.0, 0.0);
|
||||
glEnable(GL_LINE_STIPPLE);
|
||||
glLineStipple(1, 0x00FF);
|
||||
drawLine(project(_aircraft), project(trackRadial));
|
||||
|
@ -1049,7 +1049,7 @@ void MapWidget::drawGPSData()
|
|||
SGVec2d wp0Pos = project(wp0Geod);
|
||||
SGVec2d wp1Pos = project(wp1Geod);
|
||||
|
||||
glColor4f(1.0, 0.0, 1.0, 1.0);
|
||||
glColor3f(1.0, 0.0, 1.0);
|
||||
drawLine(wp0Pos, wp1Pos);
|
||||
|
||||
}
|
||||
|
@ -1387,7 +1387,7 @@ void MapWidget::drawRunwayPre(FGRunway* rwy)
|
|||
|
||||
glLineWidth(4.0);
|
||||
glColor3f(1.0, 0.0, 1.0);
|
||||
drawLine(p1, p2);
|
||||
drawLine(p1, p2);
|
||||
}
|
||||
|
||||
void MapWidget::drawRunway(FGRunway* rwy)
|
||||
|
|
|
@ -27,7 +27,8 @@ AddOnsPage::AddOnsPage(QWidget *parent, simgear::pkg::RootRef root) :
|
|||
|
||||
m_catalogsModel = new CatalogListModel(this, m_packageRoot);
|
||||
m_ui->catalogsList->setModel(m_catalogsModel);
|
||||
|
||||
connect(m_ui->catalogsList, &QListView::doubleClicked,
|
||||
this, &AddOnsPage::onCatalogDoubleClicked);
|
||||
// enable drag-drop to re-order the paths
|
||||
m_ui->sceneryPathsList->setDragEnabled(true);
|
||||
m_ui->sceneryPathsList->setDragDropMode(QAbstractItemView::InternalMove);
|
||||
|
@ -245,7 +246,12 @@ void AddOnsPage::addDefaultCatalog(QWidget* pr, bool silent)
|
|||
}
|
||||
dlg->setUrlAndDownload(url);
|
||||
dlg->exec();
|
||||
}
|
||||
|
||||
void AddOnsPage::onCatalogsRefreshed()
|
||||
{
|
||||
m_catalogsModel->refresh();
|
||||
updateUi();
|
||||
}
|
||||
|
||||
void AddOnsPage::onRemoveCatalog()
|
||||
|
@ -280,6 +286,18 @@ void AddOnsPage::onRemoveCatalog()
|
|||
updateUi();
|
||||
}
|
||||
|
||||
void AddOnsPage::onCatalogDoubleClicked(const QModelIndex& index)
|
||||
{
|
||||
if ((index.flags() & Qt::ItemIsEnabled) == false) {
|
||||
// re-validate existing catalog
|
||||
QScopedPointer<AddCatalogDialog> dlg(new AddCatalogDialog(this, m_packageRoot));
|
||||
dlg->setUpdatingExistingCatalog();
|
||||
dlg->setUrlAndDownload(index.data(CatalogUrlRole).toUrl());
|
||||
dlg->exec();
|
||||
m_catalogsModel->refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void AddOnsPage::onInstallScenery()
|
||||
{
|
||||
QSettings settings;
|
||||
|
|
|
@ -22,6 +22,9 @@ public:
|
|||
|
||||
static void addDefaultCatalog(QWidget* pr, bool silent);
|
||||
|
||||
public slots:
|
||||
void onCatalogsRefreshed();
|
||||
|
||||
signals:
|
||||
void sceneryPathsChanged();
|
||||
void aircraftPathsChanged();
|
||||
|
@ -43,6 +46,8 @@ private slots:
|
|||
|
||||
void saveAircraftPaths();
|
||||
void saveSceneryPaths();
|
||||
|
||||
void onCatalogDoubleClicked(const QModelIndex& index);
|
||||
private:
|
||||
void updateUi();
|
||||
|
||||
|
|
|
@ -318,7 +318,8 @@ QVariantList QmlAircraftInfo::previews() const
|
|||
{
|
||||
if (_item) {
|
||||
QVariantList result;
|
||||
Q_FOREACH(QUrl u, _item->previews) {
|
||||
auto actualItem = resolveItem();
|
||||
Q_FOREACH(QUrl u, actualItem->previews) {
|
||||
result.append(u);
|
||||
}
|
||||
return result;
|
||||
|
@ -488,7 +489,9 @@ void QmlAircraftInfo::setUri(QUrl u)
|
|||
auto ident = u.path().toStdString();
|
||||
try {
|
||||
_package = globals->packageRoot()->getPackageById(ident);
|
||||
_variant = _package->indexOfVariant(ident);
|
||||
if (_package) {
|
||||
_variant = _package->indexOfVariant(ident);
|
||||
}
|
||||
} catch (sg_exception&) {
|
||||
qWarning() << "couldn't find package/variant for " << u;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <Add-ons/AddonManager.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#if defined(SG_UNIX) && !defined(SG_MAC)
|
||||
|
@ -83,6 +84,16 @@ NewGUI::init ()
|
|||
readDir(aircraftDialogDir);
|
||||
}
|
||||
|
||||
// Read XML dialogs made available by registered add-ons
|
||||
const auto& addonManager = flightgear::addons::AddonManager::instance();
|
||||
for (const auto& addon: addonManager->registeredAddons()) {
|
||||
SGPath addonDialogDir = addon->getBasePath() / "gui/dialogs";
|
||||
|
||||
if (addonDialogDir.exists()) {
|
||||
readDir(addonDialogDir);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix for http://code.google.com/p/flightgear-bugs/issues/detail?id=947
|
||||
fgGetNode("sim/menubar")->setAttribute(SGPropertyNode::PRESERVE, true);
|
||||
_menubar->init();
|
||||
|
|
|
@ -95,6 +95,7 @@ Item {
|
|||
width: parent.width
|
||||
text: root.__showAlternateText ? root.alternateText()
|
||||
: model.description
|
||||
font.pixelSize: Style.baseFontPixelSize
|
||||
maximumLineCount: 3
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
|
|
|
@ -100,7 +100,7 @@ Rectangle {
|
|||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
visible: aircraft.description != ""
|
||||
font.pixelSize: 14
|
||||
font.pixelSize: Style.baseFontPixelSize
|
||||
}
|
||||
|
||||
Text {
|
||||
|
@ -110,7 +110,7 @@ Rectangle {
|
|||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
wrapMode: Text.WordWrap
|
||||
visible: (aircraft.authors != undefined)
|
||||
|
||||
font.pixelSize: Style.baseFontPixelSize
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,6 +146,7 @@ Rectangle {
|
|||
Text {
|
||||
id: ratingsLabel
|
||||
text: qsTr("Ratings:")
|
||||
font.pixelSize: Style.baseFontPixelSize
|
||||
}
|
||||
|
||||
|
||||
|
@ -192,6 +193,7 @@ Rectangle {
|
|||
width: parent.width
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
visible: aircraft.pathOnDisk != undefined
|
||||
font.pixelSize: Style.baseFontPixelSize
|
||||
}
|
||||
|
||||
} // main layout column
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import QtQuick 2.0
|
||||
import FlightGear.Launcher 1.0
|
||||
import "." // -> forces the qmldir to be loaded
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
@ -143,6 +144,7 @@ Item {
|
|||
anchors.leftMargin: 6
|
||||
anchors.verticalCenter: button.verticalCenter
|
||||
text: "Size: " + (root.packageSize / 0x100000).toFixed(1) + "MB"
|
||||
font.pixelSize: Style.baseFontPixelSize
|
||||
}
|
||||
|
||||
Column {
|
||||
|
@ -185,6 +187,7 @@ Item {
|
|||
visible: false
|
||||
text: (compact ? "" : "Downloaded ") + (root.downloadedBytes / 0x100000).toFixed(1) +
|
||||
"MB of " + (root.packageSize / 0x100000).toFixed(1) + "MB";
|
||||
font.pixelSize: Style.baseFontPixelSize
|
||||
}
|
||||
} // item container for progress bar and text
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ Item {
|
|||
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: root.title + ":"
|
||||
font.pixelSize: Style.baseFontPixelSize
|
||||
}
|
||||
|
||||
Row {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import QtQuick 2.0
|
||||
import QtQuick.Window 2.0
|
||||
import "." // -> forces the qmldir to be loaded
|
||||
|
||||
import FlightGear.Launcher 1.0
|
||||
|
||||
|
@ -37,8 +38,9 @@ Rectangle {
|
|||
anchors.rightMargin: 4
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: 24
|
||||
font.pixelSize: Style.baseFontPixelSize * 2
|
||||
text: aircraftInfo.name
|
||||
fontSizeMode: Text.Fit
|
||||
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
|
|
|
@ -20,6 +20,7 @@ Rectangle {
|
|||
anchors.centerIn: parent
|
||||
color: "white"
|
||||
text: (mouse.containsMouse && hoverText != "") ? root.hoverText : root.text
|
||||
font.pixelSize: Style.baseFontPixelSize
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
|
|
|
@ -20,6 +20,7 @@ Item {
|
|||
id: labelText
|
||||
width: parent.width - (emptyTrack.width + Style.margin)
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pixelSize: Style.baseFontPixelSize
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
|
|
@ -18,5 +18,7 @@ QtObject
|
|||
readonly property string inactiveThemeColor: "#9f9f9f"
|
||||
|
||||
readonly property string baseTextColor: "#3f3f3f"
|
||||
|
||||
readonly property int baseFontPixelSize: 12
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ Item {
|
|||
anchors.left: track.right
|
||||
anchors.leftMargin: Style.margin
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: Style.baseFontPixelSize
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
|
|
|
@ -585,6 +585,9 @@ int fgMainInit( int argc, char **argv )
|
|||
SG_LOG(SG_GENERAL, SG_INFO,
|
||||
"EmbeddedResourceManager: selected locale '" << locale << "'");
|
||||
|
||||
// Copy the property nodes for the menus added by registered add-ons
|
||||
addons::AddonManager::instance()->addAddonMenusToFGMenubar();
|
||||
|
||||
// Initialize the Window/Graphics environment.
|
||||
fgOSInit(&argc, argv);
|
||||
_bootstrap_OSInit++;
|
||||
|
|
|
@ -238,7 +238,8 @@ void FGHTTPClient::postinit()
|
|||
.method("refresh", &pkg::Catalog::refresh)
|
||||
.method("needingUpdate", &pkg::Catalog::packagesNeedingUpdate)
|
||||
.member("installed", &pkg::Catalog::installedPackages)
|
||||
.method("search", &f_catalog_search);
|
||||
.method("search", &f_catalog_search)
|
||||
.member("enabled", &pkg::Catalog::isEnabled);
|
||||
|
||||
NasalPackage::init("Package")
|
||||
.member("id", &pkg::Package::id)
|
||||
|
|
Loading…
Reference in a new issue