mirror of
https://github.com/vcmi/vcmi.git
synced 2025-02-03 13:01:33 +02:00
Removed pointer to current SDL_Event from GuiHandler
This commit is contained in:
parent
4d1c338503
commit
50a0fc4fb2
@ -192,27 +192,27 @@ void CGuiHandler::handleEvents()
|
||||
while(!SDLEventsQueue.empty())
|
||||
{
|
||||
continueEventHandling = true;
|
||||
auto ev = SDLEventsQueue.front();
|
||||
current = &ev;
|
||||
SDL_Event currentEvent = SDLEventsQueue.front();
|
||||
cursorPosition = Point(currentEvent.motion.x, currentEvent.motion.y);
|
||||
SDLEventsQueue.pop();
|
||||
|
||||
// In a sequence of mouse motion events, skip all but the last one.
|
||||
// This prevents freezes when every motion event takes longer to handle than interval at which
|
||||
// the events arrive (like dragging on the minimap in world view, with redraw at every event)
|
||||
// so that the events would start piling up faster than they can be processed.
|
||||
if ((ev.type == SDL_MOUSEMOTION) && !SDLEventsQueue.empty() && (SDLEventsQueue.front().type == SDL_MOUSEMOTION))
|
||||
if ((currentEvent.type == SDL_MOUSEMOTION) && !SDLEventsQueue.empty() && (SDLEventsQueue.front().type == SDL_MOUSEMOTION))
|
||||
continue;
|
||||
|
||||
handleCurrentEvent();
|
||||
handleCurrentEvent(currentEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void CGuiHandler::handleCurrentEvent()
|
||||
void CGuiHandler::handleCurrentEvent(const SDL_Event & current )
|
||||
{
|
||||
if(current->type == SDL_KEYDOWN || current->type == SDL_KEYUP)
|
||||
if(current.type == SDL_KEYDOWN || current.type == SDL_KEYUP)
|
||||
{
|
||||
SDL_KeyboardEvent key = current->key;
|
||||
if(current->type == SDL_KEYDOWN && key.keysym.sym >= SDLK_F1 && key.keysym.sym <= SDLK_F15 && settings["session"]["spectate"].Bool())
|
||||
SDL_KeyboardEvent key = current.key;
|
||||
if(current.type == SDL_KEYDOWN && key.keysym.sym >= SDLK_F1 && key.keysym.sym <= SDLK_F15 && settings["session"]["spectate"].Bool())
|
||||
{
|
||||
//TODO: we need some central place for all interface-independent hotkeys
|
||||
Settings s = settings.write["session"];
|
||||
@ -275,24 +275,24 @@ void CGuiHandler::handleCurrentEvent()
|
||||
if(vstd::contains(keyinterested,*i) && (!keysCaptured || (*i)->captureThisEvent(key)))
|
||||
(**i).keyPressed(key);
|
||||
}
|
||||
else if(current->type == SDL_MOUSEMOTION)
|
||||
else if(current.type == SDL_MOUSEMOTION)
|
||||
{
|
||||
handleMouseMotion();
|
||||
handleMouseMotion(current);
|
||||
}
|
||||
else if(current->type == SDL_MOUSEBUTTONDOWN)
|
||||
else if(current.type == SDL_MOUSEBUTTONDOWN)
|
||||
{
|
||||
switch(current->button.button)
|
||||
switch(current.button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT:
|
||||
{
|
||||
auto doubleClicked = false;
|
||||
if(lastClick == current->motion && (SDL_GetTicks() - lastClickTime) < 300)
|
||||
if(lastClick == getCursorPosition() && (SDL_GetTicks() - lastClickTime) < 300)
|
||||
{
|
||||
std::list<CIntObject*> hlp = doubleClickInterested;
|
||||
for(auto i = hlp.begin(); i != hlp.end() && continueEventHandling; i++)
|
||||
{
|
||||
if(!vstd::contains(doubleClickInterested, *i)) continue;
|
||||
if((*i)->pos.isInside(current->motion.x, current->motion.y))
|
||||
if((*i)->pos.isInside(current.motion.x, current.motion.y))
|
||||
{
|
||||
(*i)->onDoubleClick();
|
||||
doubleClicked = true;
|
||||
@ -301,7 +301,7 @@ void CGuiHandler::handleCurrentEvent()
|
||||
|
||||
}
|
||||
|
||||
lastClick = current->motion;
|
||||
lastClick = current.motion;
|
||||
lastClickTime = SDL_GetTicks();
|
||||
|
||||
if(!doubleClicked)
|
||||
@ -318,7 +318,7 @@ void CGuiHandler::handleCurrentEvent()
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(current->type == SDL_MOUSEWHEEL)
|
||||
else if(current.type == SDL_MOUSEWHEEL)
|
||||
{
|
||||
std::list<CIntObject*> hlp = wheelInterested;
|
||||
for(auto i = hlp.begin(); i != hlp.end() && continueEventHandling; i++)
|
||||
@ -327,27 +327,27 @@ void CGuiHandler::handleCurrentEvent()
|
||||
// SDL doesn't have the proper values for mouse positions on SDL_MOUSEWHEEL, refetch them
|
||||
int x = 0, y = 0;
|
||||
SDL_GetMouseState(&x, &y);
|
||||
(*i)->wheelScrolled(current->wheel.y < 0, (*i)->pos.isInside(x, y));
|
||||
(*i)->wheelScrolled(current.wheel.y < 0, (*i)->pos.isInside(x, y));
|
||||
}
|
||||
}
|
||||
else if(current->type == SDL_TEXTINPUT)
|
||||
else if(current.type == SDL_TEXTINPUT)
|
||||
{
|
||||
for(auto it : textInterested)
|
||||
{
|
||||
it->textInputed(current->text);
|
||||
it->textInputed(current.text);
|
||||
}
|
||||
}
|
||||
else if(current->type == SDL_TEXTEDITING)
|
||||
else if(current.type == SDL_TEXTEDITING)
|
||||
{
|
||||
for(auto it : textInterested)
|
||||
{
|
||||
it->textEdited(current->edit);
|
||||
it->textEdited(current.edit);
|
||||
}
|
||||
}
|
||||
//todo: muiltitouch
|
||||
else if(current->type == SDL_MOUSEBUTTONUP)
|
||||
else if(current.type == SDL_MOUSEBUTTONUP)
|
||||
{
|
||||
switch(current->button.button)
|
||||
switch(current.button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT:
|
||||
handleMouseButtonClick(lclickable, EIntObjMouseBtnType::LEFT, false);
|
||||
@ -360,7 +360,6 @@ void CGuiHandler::handleCurrentEvent()
|
||||
break;
|
||||
}
|
||||
}
|
||||
current = nullptr;
|
||||
} //event end
|
||||
|
||||
void CGuiHandler::handleMouseButtonClick(CIntObjectList & interestedObjs, EIntObjMouseBtnType btn, bool isPressed)
|
||||
@ -373,7 +372,7 @@ void CGuiHandler::handleMouseButtonClick(CIntObjectList & interestedObjs, EIntOb
|
||||
auto prev = (*i)->mouseState(btn);
|
||||
if(!isPressed)
|
||||
(*i)->updateMouseState(btn, isPressed);
|
||||
if((*i)->pos.isInside(current->motion.x, current->motion.y))
|
||||
if((*i)->pos.isInside(getCursorPosition()))
|
||||
{
|
||||
if(isPressed)
|
||||
(*i)->updateMouseState(btn, isPressed);
|
||||
@ -384,13 +383,13 @@ void CGuiHandler::handleMouseButtonClick(CIntObjectList & interestedObjs, EIntOb
|
||||
}
|
||||
}
|
||||
|
||||
void CGuiHandler::handleMouseMotion()
|
||||
void CGuiHandler::handleMouseMotion(const SDL_Event & current)
|
||||
{
|
||||
//sending active, hovered hoverable objects hover() call
|
||||
std::vector<CIntObject*> hlp;
|
||||
for(auto & elem : hoverable)
|
||||
{
|
||||
if(elem->pos.isInside(current->motion.x, current->motion.y))
|
||||
if(elem->pos.isInside(getCursorPosition()))
|
||||
{
|
||||
if (!(elem)->hovered)
|
||||
hlp.push_back((elem));
|
||||
@ -407,7 +406,7 @@ void CGuiHandler::handleMouseMotion()
|
||||
elem->hovered = true;
|
||||
}
|
||||
|
||||
handleMoveInterested(current->motion);
|
||||
handleMoveInterested(current.motion);
|
||||
}
|
||||
|
||||
void CGuiHandler::simpleRedraw()
|
||||
@ -491,7 +490,6 @@ CGuiHandler::CGuiHandler()
|
||||
{
|
||||
continueEventHandling = true;
|
||||
curInt = nullptr;
|
||||
current = nullptr;
|
||||
statusbar = nullptr;
|
||||
|
||||
// Creates the FPS manager and sets the framerate to 48 which is doubled the value of the original Heroes 3 FPS rate
|
||||
@ -512,6 +510,11 @@ void CGuiHandler::breakEventHandling()
|
||||
continueEventHandling = false;
|
||||
}
|
||||
|
||||
const Point & CGuiHandler::getCursorPosition() const
|
||||
{
|
||||
return cursorPosition;
|
||||
}
|
||||
|
||||
void CGuiHandler::drawFPSCounter()
|
||||
{
|
||||
const static SDL_Color yellow = {255, 255, 0, 0};
|
||||
|
@ -70,26 +70,32 @@ public:
|
||||
std::shared_ptr<IStatusBar> statusbar;
|
||||
|
||||
private:
|
||||
Point cursorPosition;
|
||||
|
||||
std::vector<std::shared_ptr<IShowActivatable>> disposed;
|
||||
|
||||
std::atomic<bool> continueEventHandling;
|
||||
typedef std::list<CIntObject*> CIntObjectList;
|
||||
|
||||
//active GUI elements (listening for events
|
||||
CIntObjectList lclickable,
|
||||
rclickable,
|
||||
mclickable,
|
||||
hoverable,
|
||||
keyinterested,
|
||||
motioninterested,
|
||||
timeinterested,
|
||||
wheelInterested,
|
||||
doubleClickInterested,
|
||||
textInterested;
|
||||
CIntObjectList lclickable;
|
||||
CIntObjectList rclickable;
|
||||
CIntObjectList mclickable;
|
||||
CIntObjectList hoverable;
|
||||
CIntObjectList keyinterested;
|
||||
CIntObjectList motioninterested;
|
||||
CIntObjectList timeinterested;
|
||||
CIntObjectList wheelInterested;
|
||||
CIntObjectList doubleClickInterested;
|
||||
CIntObjectList textInterested;
|
||||
|
||||
|
||||
void handleMouseButtonClick(CIntObjectList & interestedObjs, EIntObjMouseBtnType btn, bool isPressed);
|
||||
void processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb);
|
||||
void handleCurrentEvent(const SDL_Event & current);
|
||||
void handleMouseMotion(const SDL_Event & current);
|
||||
void handleMoveInterested( const SDL_MouseMotionEvent & motion );
|
||||
|
||||
public:
|
||||
void handleElementActivate(CIntObject * elem, ui16 activityFlag);
|
||||
void handleElementDeActivate(CIntObject * elem, ui16 activityFlag);
|
||||
@ -98,7 +104,8 @@ public:
|
||||
//objs to blit
|
||||
std::vector<std::shared_ptr<IShowActivatable>> objsToBlit;
|
||||
|
||||
SDL_Event * current; //current event - can be set to nullptr to stop handling event
|
||||
const Point & getCursorPosition() const;
|
||||
|
||||
IUpdateable *curInt;
|
||||
|
||||
Point lastClick;
|
||||
@ -132,9 +139,6 @@ public:
|
||||
|
||||
void updateTime(); //handles timeInterested
|
||||
void handleEvents(); //takes events from queue and calls interested objects
|
||||
void handleCurrentEvent();
|
||||
void handleMouseMotion();
|
||||
void handleMoveInterested( const SDL_MouseMotionEvent & motion );
|
||||
void fakeMouseMove();
|
||||
void breakEventHandling(); //current event won't be propagated anymore
|
||||
void drawFPSCounter(); // draws the FPS to the upper left corner of the screen
|
||||
|
@ -66,12 +66,6 @@ SDL_Rect CSDL_Ext::toSDL(const Rect & rect)
|
||||
return result;
|
||||
}
|
||||
|
||||
Point CSDL_Ext::fromSDL(const SDL_MouseMotionEvent & motion)
|
||||
{
|
||||
return { motion.x, motion.y };
|
||||
}
|
||||
|
||||
|
||||
void CSDL_Ext::setColors(SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors)
|
||||
{
|
||||
SDL_SetPaletteColors(surface->format->palette,colors,firstcolor,ncolors);
|
||||
@ -583,6 +577,11 @@ std::string CSDL_Ext::processStr(std::string str, std::vector<std::string> & tor
|
||||
return str;
|
||||
}
|
||||
|
||||
bool CSDL_Ext::isTransparent( SDL_Surface * srf, const Point & position )
|
||||
{
|
||||
return isTransparent(srf, position.x, position.y);
|
||||
}
|
||||
|
||||
bool CSDL_Ext::isTransparent( SDL_Surface * srf, int x, int y )
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= srf->w || y >= srf->h)
|
||||
|
@ -73,9 +73,6 @@ public:
|
||||
namespace CSDL_Ext
|
||||
{
|
||||
|
||||
/// creates Point using provided event
|
||||
Point fromSDL(const SDL_MouseMotionEvent & motion);
|
||||
|
||||
/// creates Rect using provided rect
|
||||
Rect fromSDL(const SDL_Rect & rect);
|
||||
|
||||
@ -137,6 +134,7 @@ typedef void (*TColorPutterAlpha)(Uint8 *&ptr, const Uint8 & R, const Uint8 & G,
|
||||
SDL_Surface * horizontalFlip(SDL_Surface * toRot); //horizontal flip
|
||||
Uint32 getPixel(SDL_Surface *surface, const int & x, const int & y, bool colorByte = false);
|
||||
bool isTransparent(SDL_Surface * srf, int x, int y); //checks if surface is transparent at given position
|
||||
bool isTransparent(SDL_Surface * srf, const Point & position); //checks if surface is transparent at given position
|
||||
|
||||
Uint8 *getPxPtr(const SDL_Surface * const &srf, const int x, const int y);
|
||||
TColorPutter getPutterFor(SDL_Surface * const &dest, int incrementing); //incrementing: -1, 0, 1
|
||||
|
@ -525,7 +525,7 @@ void CBonusSelection::CRegion::clickLeft(tribool down, bool previousState)
|
||||
if(indeterminate(down))
|
||||
return;
|
||||
|
||||
if(!down && selectable && !CSDL_Ext::isTransparent(graphicsNotSelected->getSurface(), GH.current->motion.x - pos.x, GH.current->motion.y - pos.y))
|
||||
if(!down && selectable && !CSDL_Ext::isTransparent(graphicsNotSelected->getSurface(), GH.getCursorPosition() - pos.topLeft()))
|
||||
{
|
||||
CSH->setCampaignMap(idOfMapAndRegion);
|
||||
}
|
||||
@ -535,7 +535,7 @@ void CBonusSelection::CRegion::clickRight(tribool down, bool previousState)
|
||||
{
|
||||
// FIXME: For some reason "down" is only ever contain indeterminate_value
|
||||
auto text = CSH->si->campState->camp->scenarios[idOfMapAndRegion].regionText;
|
||||
if(!CSDL_Ext::isTransparent(graphicsNotSelected->getSurface(), GH.current->motion.x - pos.x, GH.current->motion.y - pos.y) && text.size())
|
||||
if(!CSDL_Ext::isTransparent(graphicsNotSelected->getSurface(), GH.getCursorPosition() - pos.topLeft()) && text.size())
|
||||
{
|
||||
CRClickPopup::createAndPush(text);
|
||||
}
|
||||
|
@ -273,9 +273,9 @@ void SelectionTab::clickLeft(tribool down, bool previousState)
|
||||
select(line);
|
||||
}
|
||||
#ifdef VCMI_IOS
|
||||
// focus input field if clicked inside it
|
||||
else if(inputName && inputName->active && inputNameRect.isInside(GH.current->button.x, GH.current->button.y))
|
||||
inputName->giveFocus();
|
||||
// focus input field if clicked inside it
|
||||
else if(inputName && inputName->active && inputNameRect.isInside(GH.getCursorPosition()))
|
||||
inputName->giveFocus();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -454,8 +454,7 @@ void SelectionTab::updateListItems()
|
||||
int SelectionTab::getLine()
|
||||
{
|
||||
int line = -1;
|
||||
Point clickPos(GH.current->button.x, GH.current->button.y);
|
||||
clickPos = clickPos - pos.topLeft();
|
||||
Point clickPos = GH.getCursorPosition() - pos.topLeft();
|
||||
|
||||
// Ignore clicks on save name area
|
||||
int maxPosY;
|
||||
|
@ -229,7 +229,7 @@ void CHeroList::CHeroItem::open()
|
||||
|
||||
void CHeroList::CHeroItem::showTooltip()
|
||||
{
|
||||
CRClickPopup::createAndPush(hero, CSDL_Ext::fromSDL(GH.current->motion));
|
||||
CRClickPopup::createAndPush(hero, GH.getCursorPosition());
|
||||
}
|
||||
|
||||
std::string CHeroList::CHeroItem::getHoverText()
|
||||
@ -321,7 +321,7 @@ void CTownList::CTownItem::open()
|
||||
|
||||
void CTownList::CTownItem::showTooltip()
|
||||
{
|
||||
CRClickPopup::createAndPush(town, CSDL_Ext::fromSDL(GH.current->motion));
|
||||
CRClickPopup::createAndPush(town, GH.getCursorPosition());
|
||||
}
|
||||
|
||||
std::string CTownList::CTownItem::getHoverText()
|
||||
@ -531,8 +531,8 @@ CMinimap::CMinimap(const Rect & position)
|
||||
int3 CMinimap::translateMousePosition()
|
||||
{
|
||||
// 0 = top-left corner, 1 = bottom-right corner
|
||||
double dx = double(GH.current->motion.x - pos.x) / pos.w;
|
||||
double dy = double(GH.current->motion.y - pos.y) / pos.h;
|
||||
double dx = double(GH.getCursorPosition().x - pos.x) / pos.w;
|
||||
double dy = double(GH.getCursorPosition().y - pos.y) / pos.h;
|
||||
|
||||
int3 mapSizes = LOCPLINT->cb->getMapSize();
|
||||
|
||||
|
@ -503,7 +503,7 @@ void CVolumeSlider::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
if (down)
|
||||
{
|
||||
double px = GH.current->motion.x - pos.x;
|
||||
double px = GH.getCursorPosition().x - pos.x;
|
||||
double rx = px / static_cast<double>(pos.w);
|
||||
// setVolume is out of 100
|
||||
setVolume((int)(rx * 100));
|
||||
@ -522,7 +522,7 @@ void CVolumeSlider::clickRight(tribool down, bool previousState)
|
||||
{
|
||||
if (down)
|
||||
{
|
||||
double px = GH.current->motion.x - pos.x;
|
||||
double px = GH.getCursorPosition().x - pos.x;
|
||||
int index = static_cast<int>(px / static_cast<double>(pos.w) * animImage->size());
|
||||
|
||||
size_t helpIndex = index + (mode == MUSIC ? 326 : 336);
|
||||
@ -664,12 +664,12 @@ void CSlider::clickLeft(tribool down, bool previousState)
|
||||
double rw = 0;
|
||||
if(horizontal)
|
||||
{
|
||||
pw = GH.current->motion.x-pos.x-25;
|
||||
pw = GH.getCursorPosition().x-pos.x-25;
|
||||
rw = pw / static_cast<double>(pos.w - 48);
|
||||
}
|
||||
else
|
||||
{
|
||||
pw = GH.current->motion.y-pos.y-24;
|
||||
pw = GH.getCursorPosition().y-pos.y-24;
|
||||
rw = pw / (pos.h-48);
|
||||
}
|
||||
if(pw < -8 || pw > (horizontal ? pos.w : pos.h) - 40)
|
||||
|
@ -209,7 +209,7 @@ bool CTerrainRect::handleSwipeStateChange(bool btnPressed)
|
||||
{
|
||||
if(btnPressed)
|
||||
{
|
||||
swipeInitialRealPos = int3(GH.current->motion.x, GH.current->motion.y, 0);
|
||||
swipeInitialRealPos = int3(GH.getCursorPosition().x, GH.getCursorPosition().y, 0);
|
||||
swipeInitialMapPos = int3(adventureInt->position);
|
||||
return true;
|
||||
}
|
||||
@ -431,10 +431,7 @@ int3 CTerrainRect::whichTileIsIt(const int x, const int y)
|
||||
|
||||
int3 CTerrainRect::whichTileIsIt()
|
||||
{
|
||||
if(GH.current)
|
||||
return whichTileIsIt(GH.current->motion.x,GH.current->motion.y);
|
||||
else
|
||||
return int3(-1);
|
||||
return whichTileIsIt(GH.getCursorPosition().x, GH.getCursorPosition().y);
|
||||
}
|
||||
|
||||
int3 CTerrainRect::tileCountOnScreen()
|
||||
@ -1833,7 +1830,7 @@ void CAdvMapInt::tileRClicked(const int3 &mapPos)
|
||||
return;
|
||||
}
|
||||
|
||||
CRClickPopup::createAndPush(obj, CSDL_Ext::fromSDL(GH.current->motion), ETextAlignment::CENTER);
|
||||
CRClickPopup::createAndPush(obj, GH.getCursorPosition(), ETextAlignment::CENTER);
|
||||
}
|
||||
|
||||
void CAdvMapInt::enterCastingMode(const CSpell * sp)
|
||||
|
@ -127,7 +127,7 @@ void CBuildingRect::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
if(previousState && getBuilding() && area && !down && (parent->selectedBuilding==this))
|
||||
{
|
||||
if(!CSDL_Ext::isTransparent(area, GH.current->motion.x-pos.x, GH.current->motion.y-pos.y)) //inside building image
|
||||
if(!CSDL_Ext::isTransparent(area, GH.getCursorPosition() - pos.topLeft())) //inside building image
|
||||
{
|
||||
auto building = getBuilding();
|
||||
parent->buildingClicked(building->bid, building->subId, building->upgrade);
|
||||
@ -139,7 +139,7 @@ void CBuildingRect::clickRight(tribool down, bool previousState)
|
||||
{
|
||||
if((!area) || (!((bool)down)) || (this!=parent->selectedBuilding) || getBuilding() == nullptr)
|
||||
return;
|
||||
if( !CSDL_Ext::isTransparent(area, GH.current->motion.x-pos.x, GH.current->motion.y-pos.y) ) //inside building image
|
||||
if( !CSDL_Ext::isTransparent(area, GH.getCursorPosition() - pos.topLeft()) ) //inside building image
|
||||
{
|
||||
BuildingID bid = getBuilding()->bid;
|
||||
const CBuilding *bld = town->town->buildings.at(bid);
|
||||
@ -256,7 +256,7 @@ void CBuildingRect::mouseMoved (const SDL_MouseMotionEvent & sEvent)
|
||||
{
|
||||
if(area && pos.isInside(sEvent.x, sEvent.y))
|
||||
{
|
||||
if(CSDL_Ext::isTransparent(area, GH.current->motion.x-pos.x, GH.current->motion.y-pos.y)) //hovered pixel is inside this building
|
||||
if(CSDL_Ext::isTransparent(area, GH.getCursorPosition() - pos.topLeft())) //hovered pixel is inside this building
|
||||
{
|
||||
if(parent->selectedBuilding == this)
|
||||
{
|
||||
|
@ -306,7 +306,7 @@ void CRClickPopup::createAndPush(const std::string &txt, const CInfoWindow::TCom
|
||||
player = PlayerColor(1);
|
||||
|
||||
auto temp = std::make_shared<CInfoWindow>(txt, player, comps);
|
||||
temp->center(CSDL_Ext::fromSDL(GH.current->motion)); //center on mouse
|
||||
temp->center(GH.getCursorPosition()); //center on mouse
|
||||
#ifdef VCMI_IOS
|
||||
// TODO: enable also for android?
|
||||
temp->moveBy({0, -temp->pos.h / 2});
|
||||
|
Loading…
x
Reference in New Issue
Block a user