Skip to content

Commit

Permalink
[FEATURE] Introduce template language for labels
Browse files Browse the repository at this point in the history
This implements https://mustache.github.io/mustache.5.html .
See the little manual for its features. It can be used in the labels of
pilots, sectors and airports for now.

Default label contents have been adapted and are now using some Unicode
symbols.

Fixes: #310

Thanks to all contributors of the library
[qt-mustache](https://github.com/robertknight/qt-mustache).
  • Loading branch information
jonaseberle committed Jun 21, 2023
1 parent d7c7dbc commit ad7bf76
Show file tree
Hide file tree
Showing 25 changed files with 2,030 additions and 1,060 deletions.
14 changes: 12 additions & 2 deletions QuteScoop.pro
Expand Up @@ -201,7 +201,12 @@ HEADERS += \
src/Net.h \
src/JobList.h \
src/MetarDelegate.h \
src/Platform.h
src/Platform.h \
src/mustache/Renderer.h \
src/mustache/contexts/AirportContext.h \
src/mustache/contexts/ControllerContext.h \
src/mustache/contexts/PilotContext.h \
src/mustache/external/qt-mustache/mustache.h
SOURCES += src/WhazzupData.cpp \
src/Whazzup.cpp \
src/Waypoint.cpp \
Expand Down Expand Up @@ -257,7 +262,12 @@ SOURCES += src/WhazzupData.cpp \
src/Net.cpp \
src/JobList.cpp \
src/MetarDelegate.cpp \
src/Platform.cpp
src/Platform.cpp \
src/mustache/Renderer.cpp \
src/mustache/contexts/AirportContext.cpp \
src/mustache/contexts/ControllerContext.cpp \
src/mustache/contexts/PilotContext.cpp \
src/mustache/external/qt-mustache/mustache.cpp
RESOURCES += src/Resources.qrc

# Report DESTDIR to user
Expand Down
2 changes: 1 addition & 1 deletion bin/uncrustify-check.sh
Expand Up @@ -4,5 +4,5 @@
#
# @see .github/workflows/build.yaml for used crustify version

uncrustify -q -c uncrustify.cfg --check src/*.{h,cpp} src/*/*.{h,cpp} \
uncrustify -q -c uncrustify.cfg --check src/*.{h,cpp} src/*/*.{h,cpp} src/*/*/*.{h,cpp} \
|| ( echo "There were linting issues."; false )
2 changes: 1 addition & 1 deletion bin/uncrustify-fix.sh
Expand Up @@ -3,4 +3,4 @@
#
# @see .github/workflows/build.yaml for used crustify version

uncrustify -q -c uncrustify.cfg --replace --no-backup src/*.{h,cpp} src/*/*.{h,cpp}
uncrustify -q -c uncrustify.cfg --replace --no-backup src/*.{h,cpp} src/*/*.{h,cpp} src/*/*/*.{h,cpp}
172 changes: 23 additions & 149 deletions src/Airport.cpp
Expand Up @@ -4,89 +4,15 @@
#include "NavData.h"
#include "Settings.h"
#include "dialogs/AirportDetails.h"
#include "src/mustache/Renderer.h"

const QRegularExpression Airport::pdcRegExp = QRegularExpression(
"PDC.{0,20}\\W([A-Z]{4})(\\W|$)", QRegularExpression::MultilineOption | QRegularExpression::InvertedGreedinessOption
);

const QHash<QString, std::function<QString(Airport*)> > Airport::placeholders {
{
"{code}", [](Airport* o)->QString {
return o->id;
}
},
{
"{traffic}", [](Airport* o)->QString {
return o->trafficString();
}
},
{
"{trafficUnfiltered}", [](Airport* o)->QString {
const auto ret = o->trafficUnfilteredString();
return ret.isEmpty()? "": "#" + o->trafficUnfilteredString();
}
},
{
"{controllers}", [](Airport* o)->QString {
return o->controllersString();
}
},
{
"{atis}", [](Airport* o)->QString {
return o->atisCodeString();
}
},
{
"{country}", [](Airport* o)->QString {
return o->countryCode;
}
},
{
"{prettyName}", [](Airport* o)->QString {
return o->prettyName();
}
},
{
"{name}", [](Airport* o)->QString {
return o->name;
}
},
{
"{city}", [](Airport* o)->QString {
return o->city;
}
},
{
"{frequencies}", [](Airport* o)->QString {
return o->frequencyString();
}
},
{
"{pdc}", [](Airport* o)->QString {
return o->pdcString("PDC@");
}
},
{
"{pdc-}", [](Airport* o)->QString {
return o->pdcString("@", false);
}
},
{
"{livestream}", [](Airport* o)->QString {
return o->livestreamString();
}
},
{
"{livestream-}", [](Airport* o)->QString {
return o->livestreamString(true);
}
},
};

Airport::Airport(const QStringList& list, unsigned int debugLineNumber)
: MapObject(),
_appDisplayList(0),
_twrDisplayList(0), _gndDisplayList(0), _delDisplayList(0) {
_appDisplayList(0), _twrDisplayList(0), _gndDisplayList(0), _delDisplayList(0) {
resetWhazzupStatus();

if (list.size() != 6) {
Expand All @@ -111,6 +37,8 @@ Airport::Airport(const QStringList& list, unsigned int debugLineNumber)
}

Airport::~Airport() {
MustacheQs::Renderer::teardownContext(this);

if (_appDisplayList != 0) {
glDeleteLists(_appDisplayList, 1);
}
Expand Down Expand Up @@ -302,33 +230,12 @@ void Airport::twrGl(const QColor &middleColor, const QColor &marginColor, const
}

const QString Airport::trafficString() const {
if (Settings::filterTraffic()) {
return trafficFilteredString();
} else {
return trafficUnfilteredString();
}
}
auto tmpl = "{#allArrs}{allArrs}{/allArrs}{^allArrs}-{/allArrs}/{#allDeps}{allDeps}{/allDeps}{^allDeps}-{/allDeps}";

const QString Airport::trafficFilteredString() const {
if (!active || congestion() == 0) {
return "";
}

return QString("%1/%2").arg(
nMaybeFilteredArrivals > 0? QString::number(nMaybeFilteredArrivals): "-",
nMaybeFilteredDepartures > 0? QString::number(nMaybeFilteredDepartures): "-"
);
}

const QString Airport::trafficUnfilteredString() const {
if (!active || congestion() == 0) {
return "";
if (Settings::filterTraffic()) {
tmpl = "{#arrs}{arrs}{/arrs}{^arrs}-{/arrs}/{#deps}{deps}{/deps}{^deps}-{/deps}";
}

return QString("%1/%2").arg(
arrivals.isEmpty()? "-": QString::number(arrivals.size()),
departures.isEmpty()? "-": QString::number(departures.size())
);
return MustacheQs::Renderer::render(tmpl, (QObject*) this);
}

const QString Airport::controllersString() const {
Expand Down Expand Up @@ -423,8 +330,8 @@ const QString Airport::pdcString(const QString &prepend, bool alwaysWithIdentifi
if (match.hasMatch()) {
auto logon = match.captured(1);
if (id == logon) {
// found perfect match
if (!alwaysWithIdentifier) {
// found perfect match
return prepend;
}
}
Expand Down Expand Up @@ -573,57 +480,33 @@ const QString Airport::prettyName() const {
}

QString Airport::mapLabel() const {
auto str = Settings::airportPrimaryContent();

for (auto i = placeholders.cbegin(), end = placeholders.cend(); i != end; ++i) {
if (str.contains(i.key())) {
str.replace(i.key(), i.value()((Airport*) this));
}
}

return str.trimmed();
auto tmpl = Settings::airportPrimaryContent();
return MustacheQs::Renderer::render(tmpl, (QObject*) this);
}

QString Airport::mapLabelHovered() const {
auto str = Settings::airportPrimaryContentHovered();

for (auto i = placeholders.cbegin(), end = placeholders.cend(); i != end; ++i) {
if (str.contains(i.key())) {
str.replace(i.key(), i.value()((Airport*) this));
}
}

return str.trimmed();
auto tmpl = Settings::airportPrimaryContentHovered();
return MustacheQs::Renderer::render(tmpl, (QObject*) this);
}

QStringList Airport::mapLabelSecondaryLines() const {
auto str = Settings::airportSecondaryContent();

for (auto i = placeholders.cbegin(), end = placeholders.cend(); i != end; ++i) {
if (str.contains(i.key())) {
str.replace(i.key(), i.value()((Airport*) this));
}
}

return Helpers::linesFilteredTrimmed(str);
auto tmpl = Settings::airportSecondaryContent();
return Helpers::linesFilteredTrimmed(
MustacheQs::Renderer::render(tmpl, (QObject*) this)
);
}

QStringList Airport::mapLabelSecondaryLinesHovered() const {
auto str = Settings::airportSecondaryContentHovered();

for (auto i = placeholders.cbegin(), end = placeholders.cend(); i != end; ++i) {
if (str.contains(i.key())) {
str.replace(i.key(), i.value()((Airport*) this));
}
}

return Helpers::linesFilteredTrimmed(str);
auto tmpl = Settings::airportSecondaryContentHovered();
return Helpers::linesFilteredTrimmed(
MustacheQs::Renderer::render(tmpl, (QObject*) this)
);
}

QString Airport::livestreamString(bool shortened) const {
QString Airport::livestreamString() const {
QStringList ret;
foreach (const auto c, allControllers()) {
const auto str = c->livestreamString(shortened);
const auto str = c->livestreamString();
if (!str.isEmpty()) {
ret << str;
}
Expand All @@ -640,15 +523,6 @@ const QString Airport::shortLabel() const {
);
}

const QString Airport::longLabel() const {
auto _trafficString = trafficString();
return QString("%1 (%2)%3").arg(
id,
prettyName(),
_trafficString.isEmpty()? "": " " + trafficString()
);
}

QString Airport::toolTip() const {
return QString("%1 (%2, %3)").arg(
id,
Expand Down
34 changes: 9 additions & 25 deletions src/Airport.h
Expand Up @@ -5,20 +5,7 @@
#include "MapObject.h"
#include "Metar.h"
#include "Pilot.h"

// that was to use the QJSEngine for the labels. But basically it was way too slow for us.
//class AirportApi: public QObject {
// Q_OBJECT
// public:
// AirportApi(Airport* _a);
// virtual ~AirportApi();
// Q_PROPERTY(QString traffic READ _traffic CONSTANT);
// Q_PROPERTY(QString controllers READ _controllers CONSTANT);
// private:
// QString _traffic();
// QString _controllers();
// Airport* m_airport;
//};
#include "src/mustache/contexts/AirportContext.h"

class Airport
: public MapObject {
Expand All @@ -28,7 +15,6 @@ class Airport
const static int symbologyTwrRadius_nm = 16;
const static int symbologyGndRadius_nm = 13;
const static int symbologyDelRadius_nm = 10;
static const QHash<QString, std::function<QString(Airport*)> > placeholders;
static const QRegularExpression pdcRegExp;

Airport(const QStringList &list, unsigned int debugLineNumber = 0);
Expand All @@ -45,14 +31,11 @@ class Airport

void showDetailsDialog();

QString livestreamString(bool shortened = false) const;
QString livestreamString() const;

const QString shortLabel() const;
const QString longLabel() const;
const QString prettyName() const;
const QString trafficString() const;
const QString trafficFilteredString() const;
const QString trafficUnfilteredString() const;
const QString controllersString() const;
const QString atisCodeString() const;
const QString frequencyString() const;
Expand All @@ -72,19 +55,20 @@ class Airport

void addController(Controller* c);

const GLuint &appDisplayList();
const GLuint &twrDisplayList();
const GLuint &gndDisplayList();
const GLuint &delDisplayList();
const GLuint& appDisplayList();
const GLuint& twrDisplayList();
const GLuint& gndDisplayList();
const GLuint& delDisplayList();

Metar metar;
QString id, name, city, countryCode;
bool showRoutes = false;
bool active;
private:
GLuint _appDisplayList, _twrDisplayList, _gndDisplayList, _delDisplayList;
void appGl(const QColor &middleColor, const QColor &marginColor, const QColor &borderColor, const GLfloat &borderLineWidth) const;
void twrGl(const QColor &middleColor, const QColor &marginColor, const QColor &borderColor, const GLfloat &borderLineWidth) const;

GLuint _appDisplayList, _twrDisplayList, _gndDisplayList, _delDisplayList;
};

#endif /*AIRPORT_H_*/
#endif
9 changes: 3 additions & 6 deletions src/Client.cpp
Expand Up @@ -15,17 +15,14 @@ const QRegularExpression Client::livestreamRegExp = QRegularExpression(
QRegularExpression::MultilineOption | QRegularExpression::CaseInsensitiveOption
);

QString Client::livestreamString(const QString& str, bool shortened) {
QString Client::livestreamString(const QString& str) {
auto matchIterator = livestreamRegExp.globalMatch(str);

// take last match. Helps with "live on twitch now: twitch/user"
while (matchIterator.hasNext()) {
auto match = matchIterator.next();
if (!matchIterator.hasNext()) {
QString network(match.captured(2) + match.captured(4) + match.captured(6));
if (shortened) {
return "~";
}
return network.toLower() + "/" + match.capturedRef(8);
}
}
Expand All @@ -34,7 +31,7 @@ QString Client::livestreamString(const QString& str, bool shortened) {
}

Client::Client(const QJsonObject& json, const WhazzupData*)
: server("") {
: callsign(""), userId(""), homeBase(""), server(""), rating(-99) {
callsign = json["callsign"].toString();
Q_ASSERT(!callsign.isNull());

Expand Down Expand Up @@ -116,7 +113,7 @@ QString Client::rank() const {
return "";
}

QString Client::livestreamString(bool) const {
QString Client::livestreamString() const {
return "";
}

Expand Down

0 comments on commit ad7bf76

Please sign in to comment.