mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
#864 Moved summary files into separate folder
This commit is contained in:
@@ -0,0 +1,468 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2016 Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RimSummaryCurveAppearanceCalculator.h"
|
||||
#include "RimSummaryCurve.h"
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace caf
|
||||
{
|
||||
template<>
|
||||
void caf::AppEnum< RimSummaryCurveAppearanceCalculator::CurveAppearanceType >::setUp()
|
||||
{
|
||||
addItem(RimSummaryCurveAppearanceCalculator::NONE, "NONE", "None");
|
||||
addItem(RimSummaryCurveAppearanceCalculator::COLOR, "COLOR", "Color");
|
||||
addItem(RimSummaryCurveAppearanceCalculator::SYMBOL, "SYMBOL", "Symbols");
|
||||
addItem(RimSummaryCurveAppearanceCalculator::LINE_STYLE, "LINE_STYLE", "Line Style");
|
||||
addItem(RimSummaryCurveAppearanceCalculator::GRADIENT, "GRADIENT", "Gradient");
|
||||
addItem(RimSummaryCurveAppearanceCalculator::LINE_THICKNESS, "LINE_THICKNESS", "Line Thickness");
|
||||
|
||||
setDefault(RimSummaryCurveAppearanceCalculator::NONE);
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimSummaryCurveAppearanceCalculator::RimSummaryCurveAppearanceCalculator(const std::set<std::pair<RimSummaryCase*, RifEclipseSummaryAddress> >& curveDefinitions)
|
||||
{
|
||||
for(const std::pair<RimSummaryCase*, RifEclipseSummaryAddress>& curveDef : curveDefinitions)
|
||||
{
|
||||
if(curveDef.first) m_caseToAppearanceIdxMap[curveDef.first] = -1;
|
||||
if(!curveDef.second.wellName().empty()) m_welToAppearanceIdxMap[curveDef.second.wellName()] = -1;
|
||||
if(!curveDef.second.wellGroupName().empty()) m_grpToAppearanceIdxMap[curveDef.second.wellGroupName()] = -1;
|
||||
if(!(curveDef.second.regionNumber() == -1)) m_regToAppearanceIdxMap[curveDef.second.regionNumber()] = -1;
|
||||
|
||||
if(!curveDef.second.quantityName().empty())
|
||||
{
|
||||
std::string varname = curveDef.second.quantityName();
|
||||
m_varToAppearanceIdxMap[varname] = -1;
|
||||
|
||||
// Indexes for sub color ranges
|
||||
char secondChar = 0;
|
||||
if(varname.size() > 1)
|
||||
{
|
||||
secondChar = varname[1];
|
||||
if ( secondChar != 'W'
|
||||
&& secondChar != 'O'
|
||||
&& secondChar != 'G'
|
||||
&& secondChar != 'V')
|
||||
{
|
||||
secondChar = 0; // Consider all others as one group for coloring
|
||||
}
|
||||
}
|
||||
m_secondCharToVarToAppearanceIdxMap[secondChar][varname] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
m_caseCount = m_caseToAppearanceIdxMap.size();
|
||||
m_variableCount = m_varToAppearanceIdxMap .size();
|
||||
m_wellCount = m_welToAppearanceIdxMap .size();
|
||||
m_groupCount = m_grpToAppearanceIdxMap .size();
|
||||
m_regionCount = m_regToAppearanceIdxMap .size();
|
||||
|
||||
// Select the default appearance type for each data "dimension"
|
||||
|
||||
m_caseAppearanceType = NONE;
|
||||
m_varAppearanceType = NONE;
|
||||
m_wellAppearanceType = NONE;
|
||||
m_groupAppearanceType = NONE;
|
||||
m_regionAppearanceType = NONE;
|
||||
|
||||
std::set<RimSummaryCurveAppearanceCalculator::CurveAppearanceType> unusedAppearTypes;
|
||||
unusedAppearTypes.insert(COLOR);
|
||||
unusedAppearTypes.insert(GRADIENT);
|
||||
unusedAppearTypes.insert(LINE_STYLE);
|
||||
unusedAppearTypes.insert(SYMBOL);
|
||||
unusedAppearTypes.insert(LINE_THICKNESS);
|
||||
|
||||
m_dimensionCount = 0;
|
||||
if(m_variableCount > 1) { m_varAppearanceType = *(unusedAppearTypes.begin()); unusedAppearTypes.erase(unusedAppearTypes.begin()); m_dimensionCount++; }
|
||||
if(m_caseCount > 1) { m_caseAppearanceType = *(unusedAppearTypes.begin()); unusedAppearTypes.erase(unusedAppearTypes.begin()); m_dimensionCount++; }
|
||||
if(m_wellCount > 1) { m_wellAppearanceType = *(unusedAppearTypes.begin()); unusedAppearTypes.erase(unusedAppearTypes.begin()); m_dimensionCount++; }
|
||||
if(m_groupCount > 1) { m_groupAppearanceType = *(unusedAppearTypes.begin()); unusedAppearTypes.erase(unusedAppearTypes.begin()); m_dimensionCount++; }
|
||||
if(m_regionCount > 1) { m_regionAppearanceType = *(unusedAppearTypes.begin()); unusedAppearTypes.erase(unusedAppearTypes.begin()); m_dimensionCount++; }
|
||||
|
||||
if (m_dimensionCount == 0) m_varAppearanceType = COLOR; // basically one curve
|
||||
|
||||
// Assign increasing indexes
|
||||
{ int idx = 0; for(auto& pair : m_caseToAppearanceIdxMap) pair.second = idx++; }
|
||||
{ int idx = 0; for(auto& pair : m_varToAppearanceIdxMap) pair.second = idx++; }
|
||||
{ int idx = 0; for(auto& pair : m_welToAppearanceIdxMap) pair.second = idx++; }
|
||||
{ int idx = 0; for(auto& pair : m_grpToAppearanceIdxMap) pair.second = idx++; }
|
||||
{ int idx = 0; for(auto& pair : m_regToAppearanceIdxMap) pair.second = idx++; }
|
||||
|
||||
for (auto& charMapPair : m_secondCharToVarToAppearanceIdxMap)
|
||||
{ int idx = 0; for(auto& pair : charMapPair.second) pair.second = idx++; }
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSummaryCurveAppearanceCalculator::assignDimensions( CurveAppearanceType caseAppearance,
|
||||
CurveAppearanceType variAppearance,
|
||||
CurveAppearanceType wellAppearance,
|
||||
CurveAppearanceType gropAppearance,
|
||||
CurveAppearanceType regiAppearance)
|
||||
{
|
||||
m_caseAppearanceType = caseAppearance;
|
||||
m_varAppearanceType = variAppearance;
|
||||
m_wellAppearanceType = wellAppearance;
|
||||
m_groupAppearanceType = gropAppearance;
|
||||
m_regionAppearanceType = regiAppearance;
|
||||
|
||||
// Update the dimensionCount
|
||||
m_dimensionCount = 0;
|
||||
if(m_caseAppearanceType != NONE) ++m_dimensionCount;
|
||||
if(m_varAppearanceType != NONE) ++m_dimensionCount;
|
||||
if(m_wellAppearanceType != NONE) ++m_dimensionCount;
|
||||
if(m_groupAppearanceType != NONE) ++m_dimensionCount;
|
||||
if(m_regionAppearanceType != NONE) ++m_dimensionCount;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSummaryCurveAppearanceCalculator::getDimensions(CurveAppearanceType* caseAppearance,
|
||||
CurveAppearanceType* variAppearance,
|
||||
CurveAppearanceType* wellAppearance,
|
||||
CurveAppearanceType* gropAppearance,
|
||||
CurveAppearanceType* regiAppearance) const
|
||||
{
|
||||
*caseAppearance = m_caseAppearanceType ;
|
||||
*variAppearance = m_varAppearanceType ;
|
||||
*wellAppearance = m_wellAppearanceType ;
|
||||
*gropAppearance = m_groupAppearanceType ;
|
||||
*regiAppearance = m_regionAppearanceType;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSummaryCurveAppearanceCalculator::setupCurveLook(RimSummaryCurve* curve)
|
||||
{
|
||||
m_currentCurveBaseColor = cvf::Color3f(0, 0, 0);
|
||||
m_currentCurveGradient = 0.0f;
|
||||
|
||||
int caseAppearanceIdx = m_caseToAppearanceIdxMap[curve->summaryCase()];
|
||||
int varAppearanceIdx = m_varToAppearanceIdxMap[curve->summaryAddress().quantityName()];
|
||||
int welAppearanceIdx = m_welToAppearanceIdxMap[curve->summaryAddress().wellName()];
|
||||
int grpAppearanceIdx = m_grpToAppearanceIdxMap[curve->summaryAddress().wellGroupName()];
|
||||
int regAppearanceIdx = m_regToAppearanceIdxMap[curve->summaryAddress().regionNumber()];
|
||||
|
||||
// Remove index for curves without value at the specific dimension
|
||||
if(curve->summaryAddress().wellName().empty()) welAppearanceIdx = -1;
|
||||
if(curve->summaryAddress().wellGroupName().empty()) grpAppearanceIdx = -1;
|
||||
if(curve->summaryAddress().regionNumber() < 0) regAppearanceIdx = -1;
|
||||
|
||||
setOneCurveAppearance(m_caseAppearanceType, m_caseCount, caseAppearanceIdx, curve);
|
||||
setOneCurveAppearance(m_wellAppearanceType, m_wellCount, welAppearanceIdx, curve);
|
||||
setOneCurveAppearance(m_groupAppearanceType, m_groupCount, grpAppearanceIdx, curve);
|
||||
setOneCurveAppearance(m_regionAppearanceType, m_regionCount, regAppearanceIdx, curve);
|
||||
|
||||
if (m_varAppearanceType == COLOR && m_secondCharToVarToAppearanceIdxMap.size() > 1)
|
||||
{
|
||||
int subColorIndex = -1;
|
||||
char secondChar = 0;
|
||||
std::string varname = curve->summaryAddress().quantityName();
|
||||
if (varname.size() > 1) secondChar = varname[1];
|
||||
|
||||
subColorIndex = m_secondCharToVarToAppearanceIdxMap[secondChar][varname];
|
||||
|
||||
if (secondChar == 'W')
|
||||
{
|
||||
// Pick blue
|
||||
m_currentCurveBaseColor = cycledBlueColor(subColorIndex);
|
||||
}
|
||||
else if (secondChar == 'O')
|
||||
{
|
||||
// Pick Green
|
||||
m_currentCurveBaseColor = cycledGreenColor(subColorIndex);
|
||||
}
|
||||
else if (secondChar == 'G')
|
||||
{
|
||||
// Pick Red
|
||||
m_currentCurveBaseColor = cycledRedColor(subColorIndex);
|
||||
}
|
||||
else if(secondChar == 'V')
|
||||
{
|
||||
// Pick Brown
|
||||
m_currentCurveBaseColor = cycledBrownColor(subColorIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentCurveBaseColor = cycledNoneRGBBrColor(subColorIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setOneCurveAppearance(m_varAppearanceType, m_variableCount, varAppearanceIdx, curve);
|
||||
}
|
||||
|
||||
curve->setColor(gradeColor(m_currentCurveBaseColor, m_currentCurveGradient));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSummaryCurveAppearanceCalculator::setOneCurveAppearance(CurveAppearanceType appeaType, size_t totalCount, int appeaIdx, RimSummaryCurve* curve)
|
||||
{
|
||||
switch(appeaType)
|
||||
{
|
||||
case NONE:
|
||||
break;
|
||||
case COLOR:
|
||||
m_currentCurveBaseColor = cycledPaletteColor(appeaIdx);
|
||||
break;
|
||||
case GRADIENT:
|
||||
m_currentCurveGradient = gradient(totalCount, appeaIdx);
|
||||
break;
|
||||
case LINE_STYLE:
|
||||
curve->setLineStyle(cycledLineStyle(appeaIdx));
|
||||
break;
|
||||
case SYMBOL:
|
||||
curve->setSymbol(cycledSymbol(appeaIdx));
|
||||
break;
|
||||
case LINE_THICKNESS:
|
||||
curve->setLineThickness(cycledLineThickness(appeaIdx));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Color3f RimSummaryCurveAppearanceCalculator::cycledPaletteColor(int colorIndex)
|
||||
{
|
||||
if (colorIndex < 0) return cvf::Color3f::BLACK;
|
||||
|
||||
static const int colorCount = 15;
|
||||
static const cvf::ubyte colorData[][3] =
|
||||
{
|
||||
{ 0, 112, 136 }, // Dark Green-Blue
|
||||
{ 202, 0, 0 }, // Red
|
||||
{ 78, 204, 0 }, // Clear Green
|
||||
{ 236, 118, 0 }, // Orange
|
||||
{ 0 , 0, 0 }, // Black
|
||||
{ 56, 56, 255 }, // Vivid Blue
|
||||
{ 248, 0, 170 }, // Magenta
|
||||
{ 169, 2, 240 }, // Purple
|
||||
{ 0, 221, 221 }, // Turquoise
|
||||
{ 201, 168, 206 }, // Light Violet
|
||||
{ 0, 205, 68 }, // Bluish Green
|
||||
{ 236, 188, 0 }, // Mid Yellow
|
||||
{ 51, 204, 255 }, // Bluer Turquoise
|
||||
{ 164, 193, 0 }, // Mid Yellowish Green
|
||||
{ 0, 143, 239 }, // Dark Light Blue
|
||||
};
|
||||
|
||||
int paletteIdx = colorIndex % colorCount;
|
||||
|
||||
cvf::Color3ub ubColor(colorData[paletteIdx][0], colorData[paletteIdx][1], colorData[paletteIdx][2]);
|
||||
cvf::Color3f cvfColor(ubColor);
|
||||
return cvfColor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Color3f RimSummaryCurveAppearanceCalculator::cycledNoneRGBBrColor(int colorIndex)
|
||||
{
|
||||
if(colorIndex < 0) return cvf::Color3f::BLACK;
|
||||
|
||||
static const int colorCount = 7;
|
||||
static const cvf::ubyte colorData[][3] =
|
||||
{
|
||||
{ 236, 118, 0 }, // Orange
|
||||
{ 0 , 0, 0 }, // Black
|
||||
{ 248, 0, 170 }, // Magenta
|
||||
{ 236, 188, 0 }, // Mid Yellow
|
||||
{ 169, 2, 240 }, // Purple
|
||||
{ 0, 221, 221 }, // Turquoise
|
||||
{ 201, 168, 206 }, // Light Violet
|
||||
};
|
||||
|
||||
int paletteIdx = colorIndex % colorCount;
|
||||
|
||||
cvf::Color3ub ubColor(colorData[paletteIdx][0], colorData[paletteIdx][1], colorData[paletteIdx][2]);
|
||||
cvf::Color3f cvfColor(ubColor);
|
||||
return cvfColor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Color3f RimSummaryCurveAppearanceCalculator::cycledGreenColor(int colorIndex)
|
||||
{
|
||||
if(colorIndex < 0) return cvf::Color3f::BLACK;
|
||||
|
||||
static const int colorCount = 3;
|
||||
static const cvf::ubyte colorData[][3] =
|
||||
{
|
||||
{ 78, 204, 0 }, // Clear Green
|
||||
{ 164, 193, 0 }, // Mid Yellowish Green
|
||||
{ 0, 205, 68 } // Bluish Green
|
||||
};
|
||||
|
||||
int paletteIdx = colorIndex % colorCount;
|
||||
|
||||
cvf::Color3ub ubColor(colorData[paletteIdx][0], colorData[paletteIdx][1], colorData[paletteIdx][2]);
|
||||
cvf::Color3f cvfColor(ubColor);
|
||||
return cvfColor;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Color3f RimSummaryCurveAppearanceCalculator::cycledBlueColor(int colorIndex)
|
||||
{
|
||||
if(colorIndex < 0) return cvf::Color3f::BLACK;
|
||||
|
||||
static const int colorCount = 3;
|
||||
static const cvf::ubyte colorData[][3] =
|
||||
{
|
||||
{ 56, 56, 255 }, // Vivid Blue
|
||||
{ 0, 143, 239 }, // Dark Light Blue
|
||||
{ 153, 153, 255 }, // Off Light Blue
|
||||
};
|
||||
|
||||
int paletteIdx = colorIndex % colorCount;
|
||||
|
||||
cvf::Color3ub ubColor(colorData[paletteIdx][0], colorData[paletteIdx][1], colorData[paletteIdx][2]);
|
||||
cvf::Color3f cvfColor(ubColor);
|
||||
return cvfColor;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Color3f RimSummaryCurveAppearanceCalculator::cycledRedColor(int colorIndex)
|
||||
{
|
||||
if(colorIndex < 0) return cvf::Color3f::BLACK;
|
||||
|
||||
static const int colorCount = 3;
|
||||
static const cvf::ubyte colorData[][3] =
|
||||
{
|
||||
{ 202, 0, 0 }, // Off Red
|
||||
{ 255, 51, 51}, // Bright Red
|
||||
{ 255, 102, 102 } // Light Red
|
||||
};
|
||||
|
||||
int paletteIdx = colorIndex % colorCount;
|
||||
|
||||
cvf::Color3ub ubColor(colorData[paletteIdx][0], colorData[paletteIdx][1], colorData[paletteIdx][2]);
|
||||
cvf::Color3f cvfColor(ubColor);
|
||||
return cvfColor;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Color3f RimSummaryCurveAppearanceCalculator::cycledBrownColor(int colorIndex)
|
||||
{
|
||||
if(colorIndex < 0) return cvf::Color3f::BLACK;
|
||||
|
||||
static const int colorCount = 3;
|
||||
static const cvf::ubyte colorData[][3] =
|
||||
{
|
||||
{186, 101, 44},
|
||||
{ 99, 53, 23 }, // Highway Brown
|
||||
{ 103, 56, 24} // Dark Brown
|
||||
};
|
||||
|
||||
int paletteIdx = colorIndex % colorCount;
|
||||
|
||||
cvf::Color3ub ubColor(colorData[paletteIdx][0], colorData[paletteIdx][1], colorData[paletteIdx][2]);
|
||||
cvf::Color3f cvfColor(ubColor);
|
||||
return cvfColor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimPlotCurve::LineStyleEnum RimSummaryCurveAppearanceCalculator::cycledLineStyle(int index)
|
||||
{
|
||||
if (index < 0) return RimPlotCurve::STYLE_SOLID;
|
||||
|
||||
return caf::AppEnum<RimPlotCurve::LineStyleEnum>::fromIndex(1 + (index % (caf::AppEnum<RimPlotCurve::LineStyleEnum>::size() - 1)));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimPlotCurve::PointSymbolEnum RimSummaryCurveAppearanceCalculator::cycledSymbol(int index)
|
||||
{
|
||||
if (index < 0) return RimPlotCurve::SYMBOL_NONE;
|
||||
|
||||
return caf::AppEnum<RimPlotCurve::PointSymbolEnum>::fromIndex(1 + (index % (caf::AppEnum<RimPlotCurve::PointSymbolEnum>::size() - 1)));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
int RimSummaryCurveAppearanceCalculator::cycledLineThickness(int index)
|
||||
{
|
||||
static const int thicknessCount = 3;
|
||||
static const int thicknesses[] ={ 1, 3, 5 };
|
||||
|
||||
if (index < 0) return 1;
|
||||
return (thicknesses[(index) % 3]);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
float RimSummaryCurveAppearanceCalculator::gradient(size_t totalCount, int index)
|
||||
{
|
||||
if(totalCount == 1 || index < 0) return 0.0f;
|
||||
|
||||
const float darkLimit = -0.45f;
|
||||
const float lightLimit = 0.7f;
|
||||
float totalSpan = lightLimit - darkLimit;
|
||||
float step = totalSpan / (totalCount -1);
|
||||
return darkLimit + (index * step);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Color3f RimSummaryCurveAppearanceCalculator::gradeColor(const cvf::Color3f& color, float factor)
|
||||
{
|
||||
CVF_ASSERT(-1.0 <= factor && factor <= 1.0);
|
||||
|
||||
cvf::Vec3f orgC(color.r(), color.g(), color.b());
|
||||
cvf::Vec3f targetC;
|
||||
if(factor < 0)
|
||||
{
|
||||
targetC = cvf::Vec3f(0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetC = cvf::Vec3f(1, 1, 1);
|
||||
}
|
||||
|
||||
cvf::Vec3f newColor = ((float)fabs(factor)) * (targetC - orgC) + orgC;
|
||||
|
||||
return cvf::Color3f(newColor[0], newColor[1], newColor[2]);
|
||||
}
|
||||
Reference in New Issue
Block a user