mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 22:30:06 +00:00
Finish layer validation, including strong typing for different layer categories
This commit is contained in:
parent
8719d2d02a
commit
f222b4f666
3 changed files with 243 additions and 49 deletions
|
|
@ -44,9 +44,13 @@ static const std::string kDescriptionName_ {"description"};
|
||||||
static const std::string kMovableName_ {"movable"};
|
static const std::string kMovableName_ {"movable"};
|
||||||
static const std::string kDisplayedName_ {"displayed"};
|
static const std::string kDisplayedName_ {"displayed"};
|
||||||
|
|
||||||
typedef std::
|
typedef std::variant<std::monostate,
|
||||||
variant<std::monostate, types::Layer, awips::Phenomenon, std::string>
|
types::DataLayer,
|
||||||
LayerDescription;
|
types::InformationLayer,
|
||||||
|
types::MapLayer,
|
||||||
|
awips::Phenomenon,
|
||||||
|
std::string>
|
||||||
|
LayerDescription;
|
||||||
|
|
||||||
struct LayerInfo
|
struct LayerInfo
|
||||||
{
|
{
|
||||||
|
|
@ -59,23 +63,24 @@ struct LayerInfo
|
||||||
typedef boost::container::stable_vector<LayerInfo> LayerVector;
|
typedef boost::container::stable_vector<LayerInfo> LayerVector;
|
||||||
|
|
||||||
static const std::vector<LayerInfo> kDefaultLayers_ {
|
static const std::vector<LayerInfo> kDefaultLayers_ {
|
||||||
{types::LayerType::Information, types::Layer::MapOverlay, false},
|
{types::LayerType::Information, types::InformationLayer::MapOverlay, false},
|
||||||
{types::LayerType::Information, types::Layer::ColorTable, false},
|
{types::LayerType::Information, types::InformationLayer::ColorTable, false},
|
||||||
|
{types::LayerType::Data, types::DataLayer::RadarRange, true},
|
||||||
{types::LayerType::Alert, awips::Phenomenon::Tornado, true},
|
{types::LayerType::Alert, awips::Phenomenon::Tornado, true},
|
||||||
{types::LayerType::Alert, awips::Phenomenon::SnowSquall, true},
|
{types::LayerType::Alert, awips::Phenomenon::SnowSquall, true},
|
||||||
{types::LayerType::Alert, awips::Phenomenon::SevereThunderstorm, true},
|
{types::LayerType::Alert, awips::Phenomenon::SevereThunderstorm, true},
|
||||||
{types::LayerType::Alert, awips::Phenomenon::FlashFlood, true},
|
{types::LayerType::Alert, awips::Phenomenon::FlashFlood, true},
|
||||||
{types::LayerType::Alert, awips::Phenomenon::Marine, true},
|
{types::LayerType::Alert, awips::Phenomenon::Marine, true},
|
||||||
{types::LayerType::Map, types::Layer::MapSymbology, false},
|
{types::LayerType::Map, types::MapLayer::MapSymbology, false},
|
||||||
{types::LayerType::Radar, std::monostate {}, true},
|
{types::LayerType::Radar, std::monostate {}, true},
|
||||||
{types::LayerType::Map, types::Layer::MapUnderlay, false},
|
{types::LayerType::Map, types::MapLayer::MapUnderlay, false},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::vector<LayerInfo> kImmovableLayers_ {
|
static const std::vector<LayerInfo> kImmovableLayers_ {
|
||||||
{types::LayerType::Information, types::Layer::MapOverlay, false},
|
{types::LayerType::Information, types::InformationLayer::MapOverlay, false},
|
||||||
{types::LayerType::Information, types::Layer::ColorTable, false},
|
{types::LayerType::Information, types::InformationLayer::ColorTable, false},
|
||||||
{types::LayerType::Map, types::Layer::MapSymbology, false},
|
{types::LayerType::Map, types::MapLayer::MapSymbology, false},
|
||||||
{types::LayerType::Map, types::Layer::MapUnderlay, false},
|
{types::LayerType::Map, types::MapLayer::MapUnderlay, false},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::array<awips::Phenomenon, 5> kAlertPhenomena_ {
|
static const std::array<awips::Phenomenon, 5> kAlertPhenomena_ {
|
||||||
|
|
@ -206,8 +211,40 @@ void LayerModel::Impl::ReadLayerSettings()
|
||||||
|
|
||||||
void LayerModel::Impl::ValidateLayerSettings(LayerVector& layers)
|
void LayerModel::Impl::ValidateLayerSettings(LayerVector& layers)
|
||||||
{
|
{
|
||||||
|
// Validate layer properties
|
||||||
|
for (auto it = layers.begin(); it != layers.end();)
|
||||||
|
{
|
||||||
|
// If the layer is invalid, remove it
|
||||||
|
if (it->type_ == types::LayerType::Unknown ||
|
||||||
|
(std::holds_alternative<types::DataLayer>(it->description_) &&
|
||||||
|
std::get<types::DataLayer>(it->description_) ==
|
||||||
|
types::DataLayer::Unknown) ||
|
||||||
|
(std::holds_alternative<types::InformationLayer>(it->description_) &&
|
||||||
|
std::get<types::InformationLayer>(it->description_) ==
|
||||||
|
types::InformationLayer::Unknown) ||
|
||||||
|
(std::holds_alternative<types::MapLayer>(it->description_) &&
|
||||||
|
std::get<types::MapLayer>(it->description_) ==
|
||||||
|
types::MapLayer::Unknown) ||
|
||||||
|
(std::holds_alternative<awips::Phenomenon>(it->description_) &&
|
||||||
|
std::get<awips::Phenomenon>(it->description_) ==
|
||||||
|
awips::Phenomenon::Unknown))
|
||||||
|
{
|
||||||
|
// Erase the current layer and continue
|
||||||
|
it = layers.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure layers are appropriately marked movable
|
||||||
|
it->movable_ = (it->type_ != types::LayerType::Information &&
|
||||||
|
it->type_ != types::LayerType::Map);
|
||||||
|
|
||||||
|
// Continue to the next layer
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
// Validate immovable layers
|
// Validate immovable layers
|
||||||
std::vector<LayerVector::iterator> immovableIterators {};
|
std::vector<LayerVector::iterator> immovableIterators {};
|
||||||
|
LayerVector::iterator colorTableIterator {};
|
||||||
LayerVector::iterator mapSymbologyIterator {};
|
LayerVector::iterator mapSymbologyIterator {};
|
||||||
LayerVector::iterator mapUnderlayIterator {};
|
LayerVector::iterator mapUnderlayIterator {};
|
||||||
for (auto& immovableLayer : kImmovableLayers_)
|
for (auto& immovableLayer : kImmovableLayers_)
|
||||||
|
|
@ -253,15 +290,27 @@ void LayerModel::Impl::ValidateLayerSettings(LayerVector& layers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store positional iterators
|
// Store positional iterators
|
||||||
if (it->type_ == types::LayerType::Map)
|
if (it->type_ == types::LayerType::Information)
|
||||||
{
|
{
|
||||||
switch (std::get<types::Layer>(it->description_))
|
switch (std::get<types::InformationLayer>(it->description_))
|
||||||
{
|
{
|
||||||
case types::Layer::MapSymbology:
|
case types::InformationLayer::ColorTable:
|
||||||
|
colorTableIterator = it;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (it->type_ == types::LayerType::Map)
|
||||||
|
{
|
||||||
|
switch (std::get<types::MapLayer>(it->description_))
|
||||||
|
{
|
||||||
|
case types::MapLayer::MapSymbology:
|
||||||
mapSymbologyIterator = it;
|
mapSymbologyIterator = it;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case types::Layer::MapUnderlay:
|
case types::MapLayer::MapUnderlay:
|
||||||
mapUnderlayIterator = it;
|
mapUnderlayIterator = it;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -274,7 +323,36 @@ void LayerModel::Impl::ValidateLayerSettings(LayerVector& layers)
|
||||||
immovableIterators.push_back(it);
|
immovableIterators.push_back(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate data layers
|
||||||
|
std::vector<LayerVector::iterator> dataIterators {};
|
||||||
|
for (const auto& dataLayer : types::DataLayerIterator())
|
||||||
|
{
|
||||||
|
// Find the data layer
|
||||||
|
auto it = std::find_if(layers.begin(),
|
||||||
|
layers.end(),
|
||||||
|
[&dataLayer](const LayerInfo& layer)
|
||||||
|
{
|
||||||
|
return layer.type_ == types::LayerType::Data &&
|
||||||
|
std::get<types::DataLayer>(
|
||||||
|
layer.description_) == dataLayer;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it == layers.end())
|
||||||
|
{
|
||||||
|
// If this is the first data layer, insert after the color table layer,
|
||||||
|
// otherwise, insert after the previous data layer
|
||||||
|
LayerVector::iterator insertPosition = dataIterators.empty() ?
|
||||||
|
colorTableIterator + 1 :
|
||||||
|
dataIterators.back() + 1;
|
||||||
|
it =
|
||||||
|
layers.insert(insertPosition, {types::LayerType::Data, dataLayer});
|
||||||
|
}
|
||||||
|
|
||||||
|
dataIterators.push_back(it);
|
||||||
|
}
|
||||||
|
|
||||||
// Validate alert layers
|
// Validate alert layers
|
||||||
|
std::vector<LayerVector::iterator> alertIterators {};
|
||||||
for (auto& phenomenon : kAlertPhenomena_)
|
for (auto& phenomenon : kAlertPhenomena_)
|
||||||
{
|
{
|
||||||
// Find the alert layer
|
// Find the alert layer
|
||||||
|
|
@ -289,16 +367,24 @@ void LayerModel::Impl::ValidateLayerSettings(LayerVector& layers)
|
||||||
|
|
||||||
if (it == layers.end())
|
if (it == layers.end())
|
||||||
{
|
{
|
||||||
layers.insert(mapSymbologyIterator,
|
// Insert before the map symbology layer
|
||||||
{types::LayerType::Alert, phenomenon});
|
it = layers.insert(mapSymbologyIterator,
|
||||||
|
{types::LayerType::Alert, phenomenon});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alertIterators.push_back(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure layers are appropriately marked movable
|
// Validate the radar layer
|
||||||
for (auto& layer : layers)
|
auto it = std::find_if(layers.begin(),
|
||||||
|
layers.end(),
|
||||||
|
[](const LayerInfo& layer)
|
||||||
|
{ return layer.type_ == types::LayerType::Radar; });
|
||||||
|
if (it == layers.end())
|
||||||
{
|
{
|
||||||
layer.movable_ = (layer.type_ != types::LayerType::Information &&
|
// Insert before the map underlay layer
|
||||||
layer.type_ != types::LayerType::Map);
|
it = layers.insert(mapUnderlayIterator,
|
||||||
|
{types::LayerType::Radar, std::monostate {}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -470,10 +556,23 @@ QVariant LayerModel::data(const QModelIndex& index, int role) const
|
||||||
return QString::fromStdString(
|
return QString::fromStdString(
|
||||||
std::get<std::string>(layer.description_));
|
std::get<std::string>(layer.description_));
|
||||||
}
|
}
|
||||||
else if (std::holds_alternative<types::Layer>(layer.description_))
|
else if (std::holds_alternative<types::DataLayer>(
|
||||||
|
layer.description_))
|
||||||
{
|
{
|
||||||
return QString::fromStdString(types::GetLayerName(
|
return QString::fromStdString(types::GetDataLayerName(
|
||||||
std::get<types::Layer>(layer.description_)));
|
std::get<types::DataLayer>(layer.description_)));
|
||||||
|
}
|
||||||
|
else if (std::holds_alternative<types::InformationLayer>(
|
||||||
|
layer.description_))
|
||||||
|
{
|
||||||
|
return QString::fromStdString(types::GetInformationLayerName(
|
||||||
|
std::get<types::InformationLayer>(layer.description_)));
|
||||||
|
}
|
||||||
|
else if (std::holds_alternative<types::MapLayer>(
|
||||||
|
layer.description_))
|
||||||
|
{
|
||||||
|
return QString::fromStdString(types::GetMapLayerName(
|
||||||
|
std::get<types::MapLayer>(layer.description_)));
|
||||||
}
|
}
|
||||||
else if (std::holds_alternative<awips::Phenomenon>(
|
else if (std::holds_alternative<awips::Phenomenon>(
|
||||||
layer.description_))
|
layer.description_))
|
||||||
|
|
@ -873,9 +972,10 @@ void LayerModel::Impl::AddPlacefile(const std::string& name)
|
||||||
layers_.end(),
|
layers_.end(),
|
||||||
[](const LayerInfo& layerInfo)
|
[](const LayerInfo& layerInfo)
|
||||||
{
|
{
|
||||||
return std::holds_alternative<types::Layer>(layerInfo.description_) &&
|
return std::holds_alternative<types::InformationLayer>(
|
||||||
std::get<types::Layer>(layerInfo.description_) ==
|
layerInfo.description_) &&
|
||||||
types::Layer::ColorTable;
|
std::get<types::InformationLayer>(layerInfo.description_) ==
|
||||||
|
types::InformationLayer::ColorTable;
|
||||||
});
|
});
|
||||||
if (insertPosition != layers_.end())
|
if (insertPosition != layers_.end())
|
||||||
{
|
{
|
||||||
|
|
@ -899,10 +999,21 @@ void tag_invoke(boost::json::value_from_tag,
|
||||||
description = awips::GetPhenomenonCode(
|
description = awips::GetPhenomenonCode(
|
||||||
std::get<awips::Phenomenon>(record.description_));
|
std::get<awips::Phenomenon>(record.description_));
|
||||||
}
|
}
|
||||||
else if (std::holds_alternative<types::Layer>(record.description_))
|
else if (std::holds_alternative<types::DataLayer>(record.description_))
|
||||||
|
{
|
||||||
|
description = types::GetDataLayerName(
|
||||||
|
std::get<types::DataLayer>(record.description_));
|
||||||
|
}
|
||||||
|
else if (std::holds_alternative<types::InformationLayer>(
|
||||||
|
record.description_))
|
||||||
|
{
|
||||||
|
description = types::GetInformationLayerName(
|
||||||
|
std::get<types::InformationLayer>(record.description_));
|
||||||
|
}
|
||||||
|
else if (std::holds_alternative<types::MapLayer>(record.description_))
|
||||||
{
|
{
|
||||||
description =
|
description =
|
||||||
types::GetLayerName(std::get<types::Layer>(record.description_));
|
types::GetMapLayerName(std::get<types::MapLayer>(record.description_));
|
||||||
}
|
}
|
||||||
else if (std::holds_alternative<std::string>(record.description_))
|
else if (std::holds_alternative<std::string>(record.description_))
|
||||||
{
|
{
|
||||||
|
|
@ -940,10 +1051,17 @@ LayerInfo tag_invoke(boost::json::value_to_tag<LayerInfo>,
|
||||||
|
|
||||||
LayerDescription description {};
|
LayerDescription description {};
|
||||||
|
|
||||||
if (layerType == types::LayerType::Map ||
|
if (layerType == types::LayerType::Map)
|
||||||
layerType == types::LayerType::Information)
|
|
||||||
{
|
{
|
||||||
description = types::GetLayer(descriptionName);
|
description = types::GetMapLayer(descriptionName);
|
||||||
|
}
|
||||||
|
else if (layerType == types::LayerType::Information)
|
||||||
|
{
|
||||||
|
description = types::GetInformationLayer(descriptionName);
|
||||||
|
}
|
||||||
|
else if (layerType == types::LayerType::Data)
|
||||||
|
{
|
||||||
|
description = types::GetDataLayer(descriptionName);
|
||||||
}
|
}
|
||||||
else if (layerType == types::LayerType::Radar)
|
else if (layerType == types::LayerType::Radar)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,21 @@ static const std::unordered_map<LayerType, std::string> layerTypeName_ {
|
||||||
{LayerType::Alert, "Alert"},
|
{LayerType::Alert, "Alert"},
|
||||||
{LayerType::Placefile, "Placefile"},
|
{LayerType::Placefile, "Placefile"},
|
||||||
{LayerType::Information, "Information"},
|
{LayerType::Information, "Information"},
|
||||||
|
{LayerType::Data, "Data"},
|
||||||
{LayerType::Unknown, "?"}};
|
{LayerType::Unknown, "?"}};
|
||||||
|
|
||||||
static const std::unordered_map<Layer, std::string> layerName_ {
|
static const std::unordered_map<DataLayer, std::string> dataLayerName_ {
|
||||||
{Layer::MapOverlay, "Map Overlay"},
|
{DataLayer::RadarRange, "Radar Range"}, {DataLayer::Unknown, "?"}};
|
||||||
{Layer::ColorTable, "Color Table"},
|
|
||||||
{Layer::MapSymbology, "Map Symbology"},
|
static const std::unordered_map<InformationLayer, std::string>
|
||||||
{Layer::MapUnderlay, "Map Underlay"},
|
informationLayerName_ {{InformationLayer::MapOverlay, "Map Overlay"},
|
||||||
{Layer::Unknown, "?"}};
|
{InformationLayer::ColorTable, "Color Table"},
|
||||||
|
{InformationLayer::Unknown, "?"}};
|
||||||
|
|
||||||
|
static const std::unordered_map<MapLayer, std::string> mapLayerName_ {
|
||||||
|
{MapLayer::MapSymbology, "Map Symbology"},
|
||||||
|
{MapLayer::MapUnderlay, "Map Underlay"},
|
||||||
|
{MapLayer::Unknown, "?"}};
|
||||||
|
|
||||||
LayerType GetLayerType(const std::string& name)
|
LayerType GetLayerType(const std::string& name)
|
||||||
{
|
{
|
||||||
|
|
@ -49,27 +56,73 @@ std::string GetLayerTypeName(LayerType layerType)
|
||||||
return layerTypeName_.at(layerType);
|
return layerTypeName_.at(layerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
Layer GetLayer(const std::string& name)
|
DataLayer GetDataLayer(const std::string& name)
|
||||||
{
|
{
|
||||||
auto result =
|
auto result =
|
||||||
std::find_if(layerName_.cbegin(),
|
std::find_if(dataLayerName_.cbegin(),
|
||||||
layerName_.cend(),
|
dataLayerName_.cend(),
|
||||||
[&](const std::pair<Layer, std::string>& pair) -> bool
|
[&](const std::pair<DataLayer, std::string>& pair) -> bool
|
||||||
{ return boost::iequals(pair.second, name); });
|
{ return boost::iequals(pair.second, name); });
|
||||||
|
|
||||||
if (result != layerName_.cend())
|
if (result != dataLayerName_.cend())
|
||||||
{
|
{
|
||||||
return result->first;
|
return result->first;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Layer::Unknown;
|
return DataLayer::Unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetLayerName(Layer layer)
|
std::string GetDataLayerName(DataLayer layer)
|
||||||
{
|
{
|
||||||
return layerName_.at(layer);
|
return dataLayerName_.at(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
InformationLayer GetInformationLayer(const std::string& name)
|
||||||
|
{
|
||||||
|
auto result = std::find_if(
|
||||||
|
informationLayerName_.cbegin(),
|
||||||
|
informationLayerName_.cend(),
|
||||||
|
[&](const std::pair<InformationLayer, std::string>& pair) -> bool
|
||||||
|
{ return boost::iequals(pair.second, name); });
|
||||||
|
|
||||||
|
if (result != informationLayerName_.cend())
|
||||||
|
{
|
||||||
|
return result->first;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return InformationLayer::Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetInformationLayerName(InformationLayer layer)
|
||||||
|
{
|
||||||
|
return informationLayerName_.at(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
MapLayer GetMapLayer(const std::string& name)
|
||||||
|
{
|
||||||
|
auto result =
|
||||||
|
std::find_if(mapLayerName_.cbegin(),
|
||||||
|
mapLayerName_.cend(),
|
||||||
|
[&](const std::pair<MapLayer, std::string>& pair) -> bool
|
||||||
|
{ return boost::iequals(pair.second, name); });
|
||||||
|
|
||||||
|
if (result != mapLayerName_.cend())
|
||||||
|
{
|
||||||
|
return result->first;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return MapLayer::Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetMapLayerName(MapLayer layer)
|
||||||
|
{
|
||||||
|
return mapLayerName_.at(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace types
|
} // namespace types
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <scwx/util/iterator.hpp>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace scwx
|
namespace scwx
|
||||||
|
|
@ -16,13 +18,28 @@ enum class LayerType
|
||||||
Alert,
|
Alert,
|
||||||
Placefile,
|
Placefile,
|
||||||
Information,
|
Information,
|
||||||
|
Data,
|
||||||
Unknown
|
Unknown
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Layer
|
enum class DataLayer
|
||||||
|
{
|
||||||
|
RadarRange,
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
typedef scwx::util::
|
||||||
|
Iterator<DataLayer, DataLayer::RadarRange, DataLayer::RadarRange>
|
||||||
|
DataLayerIterator;
|
||||||
|
|
||||||
|
enum class InformationLayer
|
||||||
{
|
{
|
||||||
MapOverlay,
|
MapOverlay,
|
||||||
ColorTable,
|
ColorTable,
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MapLayer
|
||||||
|
{
|
||||||
MapSymbology,
|
MapSymbology,
|
||||||
MapUnderlay,
|
MapUnderlay,
|
||||||
Unknown
|
Unknown
|
||||||
|
|
@ -31,8 +48,14 @@ enum class Layer
|
||||||
LayerType GetLayerType(const std::string& name);
|
LayerType GetLayerType(const std::string& name);
|
||||||
std::string GetLayerTypeName(LayerType layerType);
|
std::string GetLayerTypeName(LayerType layerType);
|
||||||
|
|
||||||
Layer GetLayer(const std::string& name);
|
DataLayer GetDataLayer(const std::string& name);
|
||||||
std::string GetLayerName(Layer layer);
|
std::string GetDataLayerName(DataLayer layer);
|
||||||
|
|
||||||
|
InformationLayer GetInformationLayer(const std::string& name);
|
||||||
|
std::string GetInformationLayerName(InformationLayer layer);
|
||||||
|
|
||||||
|
MapLayer GetMapLayer(const std::string& name);
|
||||||
|
std::string GetMapLayerName(MapLayer layer);
|
||||||
|
|
||||||
} // namespace types
|
} // namespace types
|
||||||
} // namespace qt
|
} // namespace qt
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue