mirror of
https://github.com/minetest/irrlicht.git
synced 2025-06-28 06:20:21 +02:00
Remove core::list and replace uses with std::list (#105)
This commit is contained in:
@ -71,7 +71,7 @@ void CBoneSceneNode::OnAnimate(u32 timeMs)
|
||||
//updateAbsolutePosition();
|
||||
|
||||
// perform the post render process on all children
|
||||
ISceneNodeList::Iterator it = Children.begin();
|
||||
ISceneNodeList::iterator it = Children.begin();
|
||||
for (; it != Children.end(); ++it)
|
||||
(*it)->OnAnimate(timeMs);
|
||||
}
|
||||
@ -82,7 +82,7 @@ void CBoneSceneNode::helper_updateAbsolutePositionOfAllChildren(ISceneNode *Node
|
||||
{
|
||||
Node->updateAbsolutePosition();
|
||||
|
||||
ISceneNodeList::ConstIterator it = Node->getChildren().begin();
|
||||
ISceneNodeList::const_iterator it = Node->getChildren().begin();
|
||||
for (; it != Node->getChildren().end(); ++it)
|
||||
{
|
||||
helper_updateAbsolutePositionOfAllChildren( (*it) );
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "CMemoryFile.h"
|
||||
#include "CLimitReadFile.h"
|
||||
#include "CWriteFile.h"
|
||||
#include "irrList.h"
|
||||
#include <list>
|
||||
|
||||
#if defined (__STRICT_ANSI__)
|
||||
#error Compiling with __STRICT_ANSI__ not supported. g++ does set this when compiling with -std=c++11 or -std=c++0x. Use instead -std=gnu++11 or -std=gnu++0x. Or use -U__STRICT_ANSI__ to disable strict ansi.
|
||||
@ -714,11 +714,10 @@ path CFileSystem::getRelativeFilename(const path& filename, const path& director
|
||||
io::path path1, file, ext;
|
||||
core::splitFilename(getAbsolutePath(filename), &path1, &file, &ext);
|
||||
io::path path2(getAbsolutePath(directory));
|
||||
core::list<io::path> list1, list2;
|
||||
std::list<io::path> list1, list2;
|
||||
path1.split(list1, _IRR_TEXT("/\\"), 2);
|
||||
path2.split(list2, _IRR_TEXT("/\\"), 2);
|
||||
u32 i=0;
|
||||
core::list<io::path>::ConstIterator it1,it2;
|
||||
std::list<io::path>::const_iterator it1,it2;
|
||||
it1=list1.begin();
|
||||
it2=list2.begin();
|
||||
|
||||
@ -742,19 +741,19 @@ path CFileSystem::getRelativeFilename(const path& filename, const path& director
|
||||
#endif
|
||||
|
||||
|
||||
for (; i<list1.size() && i<list2.size()
|
||||
for (; it1 != list1.end() && it2 != list2.end()
|
||||
#if defined (_IRR_WINDOWS_API_)
|
||||
&& (io::path(*it1).make_lower()==io::path(*it2).make_lower())
|
||||
#else
|
||||
&& (*it1==*it2)
|
||||
#endif
|
||||
; ++i)
|
||||
;)
|
||||
{
|
||||
++it1;
|
||||
++it2;
|
||||
}
|
||||
path1=_IRR_TEXT("");
|
||||
for (; i<list2.size(); ++i)
|
||||
for (; it2 != list2.end(); ++it2)
|
||||
path1 += _IRR_TEXT("../");
|
||||
while (it1 != list1.end())
|
||||
{
|
||||
|
@ -387,11 +387,7 @@ void CGUIEnvironment::clear()
|
||||
HoveredNoSubelement = 0;
|
||||
}
|
||||
|
||||
// get the root's children in case the root changes in future
|
||||
const core::list<IGUIElement*>& children = getRootGUIElement()->getChildren();
|
||||
|
||||
while (!children.empty())
|
||||
(*children.getLast())->remove();
|
||||
getRootGUIElement()->removeAllChildren();
|
||||
}
|
||||
|
||||
|
||||
|
@ -57,17 +57,12 @@ bool CGUIModalScreen::isVisible() const
|
||||
}
|
||||
|
||||
// any child visible?
|
||||
bool visible = false;
|
||||
core::list<IGUIElement*>::ConstIterator it = Children.begin();
|
||||
for (; it != Children.end(); ++it)
|
||||
for (const auto& child : Children)
|
||||
{
|
||||
if ( (*it)->isVisible() )
|
||||
{
|
||||
visible = true;
|
||||
break;
|
||||
}
|
||||
if ( child->isVisible() )
|
||||
return true;
|
||||
}
|
||||
return visible;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CGUIModalScreen::isPointInside(const core::position2d<s32>& point) const
|
||||
@ -98,7 +93,7 @@ bool CGUIModalScreen::OnEvent(const SEvent& event)
|
||||
if ( !canTakeFocus(event.GUIEvent.Caller))
|
||||
{
|
||||
if ( !Children.empty() )
|
||||
Environment->setFocus(*(Children.begin()));
|
||||
Environment->setFocus(Children.front());
|
||||
else
|
||||
Environment->setFocus(this);
|
||||
}
|
||||
@ -110,7 +105,7 @@ bool CGUIModalScreen::OnEvent(const SEvent& event)
|
||||
if ( isMyChild(event.GUIEvent.Caller) )
|
||||
{
|
||||
if ( !Children.empty() )
|
||||
Environment->setFocus(*(Children.begin()));
|
||||
Environment->setFocus(Children.front());
|
||||
else
|
||||
Environment->setFocus(this);
|
||||
}
|
||||
@ -171,15 +166,14 @@ void CGUIModalScreen::draw()
|
||||
u32 now = os::Timer::getTime();
|
||||
if (BlinkMode && now - MouseDownTime < 300 && (now / 70)%2)
|
||||
{
|
||||
core::list<IGUIElement*>::Iterator it = Children.begin();
|
||||
core::rect<s32> r;
|
||||
video::SColor c = Environment->getSkin()->getColor(gui::EGDC_3D_HIGH_LIGHT);
|
||||
|
||||
for (; it != Children.end(); ++it)
|
||||
for (auto child : Children)
|
||||
{
|
||||
if ((*it)->isVisible())
|
||||
if (child->isVisible())
|
||||
{
|
||||
r = (*it)->getAbsolutePosition();
|
||||
r = child->getAbsolutePosition();
|
||||
r.LowerRightCorner.X += 1;
|
||||
r.LowerRightCorner.Y += 1;
|
||||
r.UpperLeftCorner.X -= 1;
|
||||
|
@ -34,11 +34,8 @@ CGUIToolBar::CGUIToolBar(IGUIEnvironment* environment, IGUIElement* parent, s32
|
||||
parentwidth = Parent->getAbsolutePosition().getWidth();
|
||||
s32 parentheight = Parent->getAbsolutePosition().getHeight();
|
||||
|
||||
const core::list<IGUIElement*>& children = parent->getChildren();
|
||||
core::list<IGUIElement*>::ConstIterator it = children.begin();
|
||||
for (; it != children.end(); ++it)
|
||||
for (const auto& e : parent->getChildren())
|
||||
{
|
||||
const IGUIElement* e = *it;
|
||||
if ( e->hasType(EGUIET_CONTEXT_MENU)
|
||||
|| e->hasType(EGUIET_MENU)
|
||||
|| e->hasType(EGUIET_TOOL_BAR) )
|
||||
|
@ -66,11 +66,10 @@ void CGUITreeViewNode::setIcon( const wchar_t* icon )
|
||||
|
||||
void CGUITreeViewNode::clearChildren()
|
||||
{
|
||||
core::list<CGUITreeViewNode*>::Iterator it;
|
||||
|
||||
for( it = Children.begin(); it != Children.end(); it++ )
|
||||
for (auto child : Children)
|
||||
{
|
||||
( *it )->drop();
|
||||
child->Parent = nullptr;
|
||||
child->drop();
|
||||
}
|
||||
Children.clear();
|
||||
}
|
||||
@ -83,9 +82,8 @@ IGUITreeViewNode* CGUITreeViewNode::addChildBack(
|
||||
void* data /*= 0*/,
|
||||
IReferenceCounted* data2 /*= 0*/ )
|
||||
{
|
||||
CGUITreeViewNode* newChild = new CGUITreeViewNode( Owner, this );
|
||||
|
||||
Children.push_back( newChild );
|
||||
auto newChild = new CGUITreeViewNode( Owner, this );
|
||||
newChild->ParentPos = Children.insert(Children.end(), newChild);
|
||||
newChild->Text = text;
|
||||
newChild->Icon = icon;
|
||||
newChild->ImageIndex = imageIndex;
|
||||
@ -107,9 +105,8 @@ IGUITreeViewNode* CGUITreeViewNode::addChildFront(
|
||||
void* data /*= 0*/,
|
||||
IReferenceCounted* data2 /*= 0*/ )
|
||||
{
|
||||
CGUITreeViewNode* newChild = new CGUITreeViewNode( Owner, this );
|
||||
|
||||
Children.push_front( newChild );
|
||||
auto newChild = new CGUITreeViewNode( Owner, this );
|
||||
newChild->ParentPos = Children.insert(Children.begin(), newChild);
|
||||
newChild->Text = text;
|
||||
newChild->Icon = icon;
|
||||
newChild->ImageIndex = imageIndex;
|
||||
@ -124,7 +121,7 @@ IGUITreeViewNode* CGUITreeViewNode::addChildFront(
|
||||
}
|
||||
|
||||
IGUITreeViewNode* CGUITreeViewNode::insertChildAfter(
|
||||
IGUITreeViewNode* other,
|
||||
IGUITreeViewNode* iother,
|
||||
const wchar_t* text,
|
||||
const wchar_t* icon /*= 0*/,
|
||||
s32 imageIndex /*= -1*/,
|
||||
@ -132,33 +129,27 @@ IGUITreeViewNode* CGUITreeViewNode::insertChildAfter(
|
||||
void* data /*= 0*/,
|
||||
IReferenceCounted* data2/* = 0*/ )
|
||||
{
|
||||
core::list<CGUITreeViewNode*>::Iterator itOther;
|
||||
CGUITreeViewNode* newChild = 0;
|
||||
|
||||
for( itOther = Children.begin(); itOther != Children.end(); itOther++ )
|
||||
{
|
||||
if( other == *itOther )
|
||||
{
|
||||
newChild = new CGUITreeViewNode( Owner, this );
|
||||
newChild->Text = text;
|
||||
newChild->Icon = icon;
|
||||
newChild->ImageIndex = imageIndex;
|
||||
newChild->SelectedImageIndex = selectedImageIndex;
|
||||
newChild->Data = data;
|
||||
newChild->Data2 = data2;
|
||||
if( data2 )
|
||||
{
|
||||
data2->grab();
|
||||
}
|
||||
Children.insert_after( itOther, newChild );
|
||||
break;
|
||||
}
|
||||
}
|
||||
// This cast is needed to access the ParentPos member of `other`.
|
||||
// The abstraction was already broken, because Children is a list of
|
||||
// CGUITreeViewNode, not IGUITreeViewNode. The existing code was
|
||||
// implicitly casting through pointer comparison.
|
||||
auto other = static_cast<CGUITreeViewNode*>(iother);
|
||||
assert(other->Parent == this);
|
||||
auto newChild = new CGUITreeViewNode( Owner, this );
|
||||
newChild->ParentPos = Children.insert(std::next(other->ParentPos), newChild);
|
||||
newChild->Text = text;
|
||||
newChild->Icon = icon;
|
||||
newChild->ImageIndex = imageIndex;
|
||||
newChild->SelectedImageIndex = selectedImageIndex;
|
||||
newChild->Data = data;
|
||||
newChild->Data2 = data2;
|
||||
if( data2 )
|
||||
data2->grab();
|
||||
return newChild;
|
||||
}
|
||||
|
||||
IGUITreeViewNode* CGUITreeViewNode::insertChildBefore(
|
||||
IGUITreeViewNode* other,
|
||||
IGUITreeViewNode* iother,
|
||||
const wchar_t* text,
|
||||
const wchar_t* icon /*= 0*/,
|
||||
s32 imageIndex /*= -1*/,
|
||||
@ -166,28 +157,18 @@ IGUITreeViewNode* CGUITreeViewNode::insertChildBefore(
|
||||
void* data /*= 0*/,
|
||||
IReferenceCounted* data2/* = 0*/ )
|
||||
{
|
||||
core::list<CGUITreeViewNode*>::Iterator itOther;
|
||||
CGUITreeViewNode* newChild = 0;
|
||||
|
||||
for( itOther = Children.begin(); itOther != Children.end(); itOther++ )
|
||||
{
|
||||
if( other == *itOther )
|
||||
{
|
||||
newChild = new CGUITreeViewNode( Owner, this );
|
||||
newChild->Text = text;
|
||||
newChild->Icon = icon;
|
||||
newChild->ImageIndex = imageIndex;
|
||||
newChild->SelectedImageIndex = selectedImageIndex;
|
||||
newChild->Data = data;
|
||||
newChild->Data2 = data2;
|
||||
if( data2 )
|
||||
{
|
||||
data2->grab();
|
||||
}
|
||||
Children.insert_before( itOther, newChild );
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto other = static_cast<CGUITreeViewNode*>(iother);
|
||||
assert(other->Parent == this);
|
||||
auto newChild = new CGUITreeViewNode( Owner, this );
|
||||
newChild->ParentPos = Children.insert(other->ParentPos, newChild);
|
||||
newChild->Text = text;
|
||||
newChild->Icon = icon;
|
||||
newChild->ImageIndex = imageIndex;
|
||||
newChild->SelectedImageIndex = selectedImageIndex;
|
||||
newChild->Data = data;
|
||||
newChild->Data2 = data2;
|
||||
if( data2 )
|
||||
data2->grab();
|
||||
return newChild;
|
||||
}
|
||||
|
||||
@ -199,7 +180,7 @@ IGUITreeViewNode* CGUITreeViewNode::getFirstChild() const
|
||||
}
|
||||
else
|
||||
{
|
||||
return *( Children.begin() );
|
||||
return Children.front();
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,54 +192,25 @@ IGUITreeViewNode* CGUITreeViewNode::getLastChild() const
|
||||
}
|
||||
else
|
||||
{
|
||||
return *( Children.getLast() );
|
||||
return Children.back();
|
||||
}
|
||||
}
|
||||
|
||||
IGUITreeViewNode* CGUITreeViewNode::getPrevSibling() const
|
||||
{
|
||||
core::list<CGUITreeViewNode*>::Iterator itThis;
|
||||
core::list<CGUITreeViewNode*>::Iterator itOther;
|
||||
CGUITreeViewNode* other = 0;
|
||||
|
||||
if( Parent )
|
||||
{
|
||||
for( itThis = Parent->Children.begin(); itThis != Parent->Children.end(); itThis++ )
|
||||
{
|
||||
if( this == *itThis )
|
||||
{
|
||||
if( itThis != Parent->Children.begin() )
|
||||
{
|
||||
other = *itOther;
|
||||
}
|
||||
break;
|
||||
}
|
||||
itOther = itThis;
|
||||
}
|
||||
}
|
||||
return other;
|
||||
if (!Parent || ParentPos == Parent->Children.begin())
|
||||
return nullptr;
|
||||
return *std::prev(ParentPos);
|
||||
}
|
||||
|
||||
IGUITreeViewNode* CGUITreeViewNode::getNextSibling() const
|
||||
{
|
||||
core::list<CGUITreeViewNode*>::Iterator itThis;
|
||||
CGUITreeViewNode* other = 0;
|
||||
|
||||
if( Parent )
|
||||
{
|
||||
for( itThis = Parent->Children.begin(); itThis != Parent->Children.end(); itThis++ )
|
||||
{
|
||||
if( this == *itThis )
|
||||
{
|
||||
if( itThis != Parent->Children.getLast() )
|
||||
{
|
||||
other = *( ++itThis );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return other;
|
||||
if (!Parent)
|
||||
return nullptr;
|
||||
auto nextIt = std::next(ParentPos);
|
||||
if (nextIt == Parent->Children.end())
|
||||
return nullptr;
|
||||
return *nextIt;
|
||||
}
|
||||
|
||||
IGUITreeViewNode* CGUITreeViewNode::getNextVisible() const
|
||||
@ -286,73 +238,40 @@ IGUITreeViewNode* CGUITreeViewNode::getNextVisible() const
|
||||
return next;
|
||||
}
|
||||
|
||||
bool CGUITreeViewNode::deleteChild( IGUITreeViewNode* child )
|
||||
bool CGUITreeViewNode::deleteChild( IGUITreeViewNode* ichild )
|
||||
{
|
||||
core::list<CGUITreeViewNode*>::Iterator itChild;
|
||||
bool deleted = false;
|
||||
|
||||
for( itChild = Children.begin(); itChild != Children.end(); itChild++ )
|
||||
{
|
||||
if( child == *itChild )
|
||||
{
|
||||
child->drop();
|
||||
Children.erase( itChild );
|
||||
deleted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return deleted;
|
||||
auto child = static_cast<CGUITreeViewNode*>(ichild);
|
||||
assert(child->Parent == this);
|
||||
Children.erase(child->ParentPos);
|
||||
child->Parent = nullptr;
|
||||
child->drop();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CGUITreeViewNode::moveChildUp( IGUITreeViewNode* child )
|
||||
bool CGUITreeViewNode::moveChildUp( IGUITreeViewNode* ichild )
|
||||
{
|
||||
core::list<CGUITreeViewNode*>::Iterator itChild;
|
||||
core::list<CGUITreeViewNode*>::Iterator itOther;
|
||||
CGUITreeViewNode* nodeTmp;
|
||||
bool moved = false;
|
||||
|
||||
for( itChild = Children.begin(); itChild != Children.end(); itChild++ )
|
||||
{
|
||||
if( child == *itChild )
|
||||
{
|
||||
if( itChild != Children.begin() )
|
||||
{
|
||||
nodeTmp = *itChild;
|
||||
*itChild = *itOther;
|
||||
*itOther = nodeTmp;
|
||||
moved = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
itOther = itChild;
|
||||
}
|
||||
return moved;
|
||||
auto child = static_cast<CGUITreeViewNode*>(ichild);
|
||||
assert(child->Parent == this);
|
||||
if (child->ParentPos == Children.begin())
|
||||
return false;
|
||||
auto curPos = child->ParentPos;
|
||||
auto prevPos = std::prev(child->ParentPos);
|
||||
std::swap(*curPos, *prevPos);
|
||||
std::swap((*curPos)->ParentPos, (*prevPos)->ParentPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CGUITreeViewNode::moveChildDown( IGUITreeViewNode* child )
|
||||
bool CGUITreeViewNode::moveChildDown( IGUITreeViewNode* ichild )
|
||||
{
|
||||
core::list<CGUITreeViewNode*>::Iterator itChild;
|
||||
core::list<CGUITreeViewNode*>::Iterator itOther;
|
||||
CGUITreeViewNode* nodeTmp;
|
||||
bool moved = false;
|
||||
|
||||
for( itChild = Children.begin(); itChild != Children.end(); itChild++ )
|
||||
{
|
||||
if( child == *itChild )
|
||||
{
|
||||
if( itChild != Children.getLast() )
|
||||
{
|
||||
itOther = itChild;
|
||||
++itOther;
|
||||
nodeTmp = *itChild;
|
||||
*itChild = *itOther;
|
||||
*itOther = nodeTmp;
|
||||
moved = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return moved;
|
||||
auto child = static_cast<CGUITreeViewNode*>(ichild);
|
||||
assert(child->Parent == this);
|
||||
auto nextPos = std::next(child->ParentPos);
|
||||
if (nextPos == Children.end())
|
||||
return false;
|
||||
auto curPos = child->ParentPos;
|
||||
std::swap(*curPos, *nextPos);
|
||||
std::swap((*curPos)->ParentPos, (*nextPos)->ParentPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGUITreeViewNode::setExpanded( bool expanded )
|
||||
|
@ -5,7 +5,6 @@
|
||||
#define __C_GUI_TREE_VIEW_H_INCLUDED__
|
||||
|
||||
#include "IGUITreeView.h"
|
||||
#include "irrList.h"
|
||||
|
||||
|
||||
namespace irr
|
||||
@ -93,7 +92,7 @@ namespace gui
|
||||
|
||||
//! returns the child item count
|
||||
virtual u32 getChildCount() const _IRR_OVERRIDE_
|
||||
{ return Children.getSize(); }
|
||||
{ return Children.size(); }
|
||||
|
||||
//! removes all children (recursive) from this node
|
||||
virtual void clearChildren() _IRR_OVERRIDE_;
|
||||
@ -231,7 +230,10 @@ namespace gui
|
||||
void* Data;
|
||||
IReferenceCounted* Data2;
|
||||
bool Expanded;
|
||||
core::list<CGUITreeViewNode*> Children;
|
||||
std::list<CGUITreeViewNode*> Children;
|
||||
// Position of this node in Parent->Children.
|
||||
// Only valid when Parent != NULL
|
||||
std::list<CGUITreeViewNode*>::iterator ParentPos;
|
||||
};
|
||||
|
||||
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include "CIrrDeviceOSX.h"
|
||||
|
||||
#include "IEventReceiver.h"
|
||||
#include "irrList.h"
|
||||
#include "os.h"
|
||||
#include "CTimer.h"
|
||||
#include "irrString.h"
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
#include "CIrrDeviceSDL.h"
|
||||
#include "IEventReceiver.h"
|
||||
#include "irrList.h"
|
||||
#include "os.h"
|
||||
#include "CTimer.h"
|
||||
#include "irrString.h"
|
||||
|
@ -12,7 +12,6 @@
|
||||
|
||||
#include "CIrrDeviceWin32.h"
|
||||
#include "IEventReceiver.h"
|
||||
#include "irrList.h"
|
||||
#include "os.h"
|
||||
|
||||
#include "CTimer.h"
|
||||
|
@ -855,7 +855,7 @@ ISceneNode* CSceneManager::getSceneNodeFromName(const char* name, ISceneNode* st
|
||||
ISceneNode* node = 0;
|
||||
|
||||
const ISceneNodeList& list = start->getChildren();
|
||||
ISceneNodeList::ConstIterator it = list.begin();
|
||||
ISceneNodeList::const_iterator it = list.begin();
|
||||
for (; it!=list.end(); ++it)
|
||||
{
|
||||
node = getSceneNodeFromName(name, *it);
|
||||
@ -879,7 +879,7 @@ ISceneNode* CSceneManager::getSceneNodeFromId(s32 id, ISceneNode* start)
|
||||
ISceneNode* node = 0;
|
||||
|
||||
const ISceneNodeList& list = start->getChildren();
|
||||
ISceneNodeList::ConstIterator it = list.begin();
|
||||
ISceneNodeList::const_iterator it = list.begin();
|
||||
for (; it!=list.end(); ++it)
|
||||
{
|
||||
node = getSceneNodeFromId(id, *it);
|
||||
@ -903,7 +903,7 @@ ISceneNode* CSceneManager::getSceneNodeFromType(scene::ESCENE_NODE_TYPE type, IS
|
||||
ISceneNode* node = 0;
|
||||
|
||||
const ISceneNodeList& list = start->getChildren();
|
||||
ISceneNodeList::ConstIterator it = list.begin();
|
||||
ISceneNodeList::const_iterator it = list.begin();
|
||||
for (; it!=list.end(); ++it)
|
||||
{
|
||||
node = getSceneNodeFromType(type, *it);
|
||||
@ -925,7 +925,7 @@ void CSceneManager::getSceneNodesFromType(ESCENE_NODE_TYPE type, core::array<sce
|
||||
outNodes.push_back(start);
|
||||
|
||||
const ISceneNodeList& list = start->getChildren();
|
||||
ISceneNodeList::ConstIterator it = list.begin();
|
||||
ISceneNodeList::const_iterator it = list.begin();
|
||||
|
||||
for (; it!=list.end(); ++it)
|
||||
{
|
||||
|
Reference in New Issue
Block a user