Răsfoiți Sursa

Split CIntObjectClasses into multiple smaller files. This should be the last change in files

Ivan Savenko 11 ani în urmă
părinte
comite
731aedf3a1
67 a modificat fișierele cu 4023 adăugiri și 3655 ștergeri
  1. 1 0
      client/CMT.cpp
  2. 12 2
      client/CMakeLists.txt
  3. 5 4
      client/CMessage.cpp
  4. 3 1
      client/CPlayerInterface.cpp
  5. 3 2
      client/CPlayerInterface.h
  6. 6 2
      client/CPreGame.cpp
  7. 9 2
      client/CPreGame.h
  8. 3 0
      client/Client.cpp
  9. 5 1
      client/Client.h
  10. 0 1
      client/Graphics.cpp
  11. 1 0
      client/battle/CBattleAnimations.cpp
  12. 1 1
      client/battle/CBattleAnimations.h
  13. 1 1
      client/battle/CBattleInterface.cpp
  14. 1 3
      client/battle/CBattleInterface.h
  15. 4 1
      client/battle/CBattleInterfaceClasses.cpp
  16. 2 1
      client/battle/CCreatureAnimation.h
  17. 0 316
      client/gui/CAnimation.cpp
  18. 2 160
      client/gui/CAnimation.h
  19. 1 1
      client/gui/CCursorHandler.cpp
  20. 2 0
      client/gui/CGuiHandler.cpp
  21. 1 1
      client/gui/CGuiHandler.h
  22. 3 1
      client/gui/CIntObject.h
  23. 6 1
      client/gui/Geometries.cpp
  24. 3 5
      client/gui/Geometries.h
  25. 3 2
      client/gui/SDL_Extensions.h
  26. 5 37
      client/widgets/AdventureMapClasses.cpp
  27. 3 11
      client/widgets/AdventureMapClasses.h
  28. 732 0
      client/widgets/Buttons.cpp
  29. 175 0
      client/widgets/Buttons.h
  30. 3 1
      client/widgets/CArtifactHolder.cpp
  31. 2 1
      client/widgets/CArtifactHolder.h
  32. 1 1
      client/widgets/CComponent.cpp
  33. 2 14
      client/widgets/CComponent.h
  34. 12 1
      client/widgets/CGarrisonInt.cpp
  35. 1 1
      client/widgets/CGarrisonInt.h
  36. 0 2025
      client/widgets/CIntObjectClasses.cpp
  37. 0 560
      client/widgets/CIntObjectClasses.h
  38. 533 0
      client/widgets/Images.cpp
  39. 226 0
      client/widgets/Images.h
  40. 29 374
      client/widgets/MiscWidgets.cpp
  41. 42 101
      client/widgets/MiscWidgets.h
  42. 234 0
      client/widgets/ObjectLists.cpp
  43. 111 0
      client/widgets/ObjectLists.h
  44. 642 0
      client/widgets/TextControls.cpp
  45. 189 0
      client/widgets/TextControls.h
  46. 36 1
      client/windows/CAdvmapInterface.cpp
  47. 14 0
      client/windows/CAdvmapInterface.h
  48. 3 2
      client/windows/CCastleInterface.cpp
  49. 1 1
      client/windows/CCastleInterface.h
  50. 6 2
      client/windows/CCreatureWindow.cpp
  51. 3 1
      client/windows/CCreatureWindow.h
  52. 1 2
      client/windows/CHeroWindow.cpp
  53. 3 0
      client/windows/CHeroWindow.h
  54. 4 2
      client/windows/CKingdomInterface.cpp
  55. 3 0
      client/windows/CKingdomInterface.h
  56. 0 1
      client/windows/CQuestLog.cpp
  57. 4 2
      client/windows/CQuestLog.h
  58. 5 0
      client/windows/CSpellWindow.cpp
  59. 1 1
      client/windows/CSpellWindow.h
  60. 4 1
      client/windows/CTradeWindow.cpp
  61. 4 0
      client/windows/CTradeWindow.h
  62. 242 0
      client/windows/CWindowObject.cpp
  63. 65 0
      client/windows/CWindowObject.h
  64. 2 1
      client/windows/GUIClasses.cpp
  65. 9 2
      client/windows/GUIClasses.h
  66. 456 0
      client/windows/InfoWindows.cpp
  67. 137 0
      client/windows/InfoWindows.h

+ 1 - 0
client/CMT.cpp

@@ -39,6 +39,7 @@
 #include "../lib/GameConstants.h"
 #include "gui/CGuiHandler.h"
 #include "../lib/logging/CBasicLogConfigurator.h"
+#include "../lib/CondSh.h"
 
 #ifdef _WIN32
 #include "SDL_syswm.h"

+ 12 - 2
client/CMakeLists.txt

@@ -14,6 +14,7 @@ set(client_SRCS
 		battle/CBattleInterfaceClasses.cpp
 		battle/CCreatureAnimation.cpp
 
+		gui/CAnimation.cpp
 		gui/CCursorHandler.cpp
 		gui/CGuiHandler.cpp
 		gui/CIntObject.cpp
@@ -22,12 +23,14 @@ set(client_SRCS
 		gui/SDL_Extensions.cpp
 
 		widgets/AdventureMapClasses.cpp
-		widgets/CAnimation.cpp
+		widgets/Buttons.cpp
 		widgets/CArtifactHolder.cpp
 		widgets/CComponent.cpp
 		widgets/CGarrisonInt.cpp
-		widgets/CIntObjectClasses.cpp
+		widgets/Images.cpp
 		widgets/MiscWidgets.cpp
+		widgets/ObjectLists.cpp
+		widgets/TextControls.cpp
 
 		windows/CAdvmapInterface.cpp
 		windows/CCastleInterface.cpp
@@ -37,6 +40,8 @@ set(client_SRCS
 		windows/CQuestLog.cpp
 		windows/CSpellWindow.cpp
 		windows/CTradeWindow.cpp
+		windows/CWindowObject
+		windows/InfoWindows.cpp
 		windows/GUIClasses.cpp
 
 		CBitmapHandler.cpp
@@ -54,6 +59,11 @@ set(client_SRCS
 		NetPacksClient.cpp
 )
 
+set(client_HEADERS
+		gui/SDL_Pixels.h
+		gui/SDL_Compat.h
+)
+
 if(APPLE)
 	# OS X specific includes
 	include_directories(${SPARKLE_INCLUDE_DIR})

+ 5 - 4
client/CMessage.cpp

@@ -11,9 +11,7 @@
 #include "StdInc.h"
 #include "CMessage.h"
 
-#include "SDL_ttf.h"
 #include "CDefHandler.h"
-#include "widgets/CAnimation.h"
 #include "CGameInfo.h"
 #include "gui/SDL_Extensions.h"
 #include "../lib/CGeneralTextHandler.h"
@@ -21,8 +19,11 @@
 #include "windows/GUIClasses.h"
 #include "../lib/CConfigHandler.h"
 #include "CBitmapHandler.h"
-#include "widgets/CIntObjectClasses.h"
-#include "widgets/MiscWidgets.h"
+
+#include "widgets/CComponent.h"
+#include "windows/InfoWindows.h"
+#include "widgets/Buttons.h"
+#include "widgets/TextControls.h"
 
 const int BETWEEN_COMPS_ROWS = 10;
 const int BEFORE_COMPONENTS = 30;

+ 3 - 1
client/CPlayerInterface.cpp

@@ -13,6 +13,7 @@
 #include "CMessage.h"
 #include "CPlayerInterface.h"
 #include "gui/SDL_Extensions.h"
+#include "widgets/CComponent.h"
 #include "windows/CTradeWindow.h"
 #include "../lib/CConfigHandler.h"
 #include "battle/CCreatureAnimation.h"
@@ -37,8 +38,9 @@
 #include "../lib/CGameState.h"
 #include "../lib/GameConstants.h"
 #include "gui/CGuiHandler.h"
-#include "widgets/MiscWidgets.h"
+#include "windows/InfoWindows.h"
 #include "../lib/UnlockGuard.h"
+#include <SDL.h>
 
 #ifdef min
 #undef min

+ 3 - 2
client/CPlayerInterface.h

@@ -1,11 +1,12 @@
 #pragma once
 
 
-#include "../lib/CondSh.h"
+//#include "../lib/CondSh.h"
 #include "../lib/FunctionList.h"
 #include "../lib/CGameInterface.h"
+#include "../lib/NetPacksBase.h"
 #include "gui/CIntObject.h"
-#include "../lib/CGameState.h"
+//#include "../lib/CGameState.h"
 
 #ifdef __GNUC__
 #define sprintf_s snprintf

+ 6 - 2
client/CPreGame.cpp

@@ -9,7 +9,6 @@
 #include "gui/SDL_Extensions.h"
 #include "CGameInfo.h"
 #include "gui/CCursorHandler.h"
-#include "widgets/CAnimation.h"
 #include "CDefHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CTownHandler.h"
@@ -38,8 +37,13 @@
 #include "../lib/CConfigHandler.h"
 #include "../lib/GameConstants.h"
 #include "gui/CGuiHandler.h"
-#include "widgets/CIntObjectClasses.h"
+#include "gui/CAnimation.h"
+#include "widgets/CComponent.h"
+#include "widgets/Buttons.h"
 #include "widgets/MiscWidgets.h"
+#include "widgets/ObjectLists.h"
+#include "widgets/TextControls.h"
+#include "windows/InfoWindows.h"
 #include "../lib/mapping/CMapService.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/CRandomGenerator.h"

+ 9 - 2
client/CPreGame.h

@@ -1,11 +1,11 @@
 #pragma once
 
-#include "../lib/filesystem/Filesystem.h"
+//#include "../lib/filesystem/Filesystem.h"
 #include "../lib/StartInfo.h"
 #include "../lib/FunctionList.h"
 #include "../lib/mapping/CMapInfo.h"
 #include "../lib/rmg/CMapGenerator.h"
-#include "widgets/CIntObjectClasses.h"
+#include "windows/CWindowObject.h"
 
 /*
  * CPreGame.h, part of VCMI engine
@@ -17,6 +17,7 @@
  *
  */
 
+class CMapInfo;
 class CMusicHandler;
 class CMapHeader;
 class CCampaignHeader;
@@ -31,6 +32,12 @@ class CMapGenOptions;
 class CRandomMapTab;
 struct CPackForSelectionScreen;
 struct PlayerInfo;
+class CMultiLineLabel;
+class CHighlightableButton;
+class CHighlightableButtonsGroup;
+class CTabbedInt;
+class CAdventureMapButton;
+class CSlider;
 
 namespace boost{ class thread; class recursive_mutex;}
 

+ 3 - 0
client/Client.cpp

@@ -1,4 +1,7 @@
 #include "StdInc.h"
+#include "Client.h"
+
+#include <SDL.h>
 
 #include "CMusicHandler.h"
 #include "../lib/mapping/CCampaignHandler.h"

+ 5 - 1
client/Client.h

@@ -2,8 +2,9 @@
 
 
 #include "../lib/IGameCallback.h"
-#include "../lib/CondSh.h"
+#include "../lib/BattleAction.h"
 #include "../lib/CStopWatch.h"
+#include "../lib/int3.h"
 
 /*
  * Client.h, part of VCMI engine
@@ -15,6 +16,9 @@
  *
  */
 
+class CPack;
+class CCampaignState;
+class CBattleCallback;
 class IGameEventsReceiver;
 class IBattleEventsReceiver;
 class CBattleGameInterface;

+ 0 - 1
client/Graphics.cpp

@@ -21,7 +21,6 @@
 #include "../lib/vcmi_endian.h"
 #include "../lib/GameConstants.h"
 #include "../lib/CStopWatch.h"
-#include "widgets/CAnimation.h"
 #include "../lib/mapObjects/CObjectClassesHandler.h"
 
 using namespace boost::assign;

+ 1 - 0
client/battle/CBattleAnimations.cpp

@@ -19,6 +19,7 @@
 #include "../../CCallback.h"
 #include "../../lib/BattleState.h"
 #include "../../lib/CTownHandler.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
 
 /*
  * CBattleAnimations.cpp, part of VCMI engine

+ 1 - 1
client/battle/CBattleAnimations.h

@@ -1,7 +1,7 @@
 #pragma once
 
-#include "../widgets/CAnimation.h"
 #include "../../lib/BattleHex.h"
+#include "../widgets/Images.h"
 
 class CBattleInterface;
 class CStack;

+ 1 - 1
client/battle/CBattleInterface.cpp

@@ -17,7 +17,6 @@
 #include "../gui/CCursorHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/SDL_Extensions.h"
-#include "../widgets/CAnimation.h"
 #include "../windows/CAdvmapInterface.h"
 #include "../windows/CCreatureWindow.h"
 #include "../windows/CSpellWindow.h"
@@ -31,6 +30,7 @@
 #include "../../lib/CRandomGenerator.h"
 #include "../../lib/CSpellHandler.h"
 #include "../../lib/CTownHandler.h"
+#include "../../lib/CGameState.h"
 #include "../../lib/mapping/CMap.h"
 #include "../../lib/NetPacks.h"
 #include "../../lib/UnlockGuard.h"

+ 1 - 3
client/battle/CBattleInterface.h

@@ -1,12 +1,10 @@
 #pragma once
 
 
-#include "../../lib/CCreatureSet.h"
+//#include "../../lib/CCreatureSet.h"
 #include "../../lib/ConstTransitivePtr.h" //may be reundant
 #include "../../lib/GameConstants.h"
 
-#include "../widgets/CAnimation.h"
-
 #include "CBattleAnimations.h"
 
 /*

+ 4 - 1
client/battle/CBattleInterfaceClasses.cpp

@@ -14,7 +14,8 @@
 #include "../gui/CCursorHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/SDL_Extensions.h"
-#include "../widgets/CIntObjectClasses.h"
+#include "../widgets/Buttons.h"
+#include "../widgets/TextControls.h"
 #include "../windows/CCreatureWindow.h"
 #include "../windows/CSpellWindow.h"
 
@@ -22,10 +23,12 @@
 #include "../../lib/BattleState.h"
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CCreatureHandler.h"
+#include "../../lib/CGameState.h"
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/CTownHandler.h"
 #include "../../lib/NetPacks.h"
 #include "../../lib/StartInfo.h"
+#include "../../lib/CondSh.h"
 
 /*
  * CBattleInterfaceClasses.cpp, part of VCMI engine

+ 2 - 1
client/battle/CCreatureAnimation.h

@@ -1,7 +1,8 @@
 #pragma once
 
 #include "../../lib/FunctionList.h"
-#include "../widgets/CAnimation.h"
+#include "../gui/SDL_Extensions.h"
+#include "../widgets/Images.h"
 
 /*
  * CCreatureAnimation.h, part of VCMI engine

+ 0 - 316
client/widgets/CAnimation.cpp → client/gui/CAnimation.cpp

@@ -1222,319 +1222,3 @@ void CAnimation::getAnimInfo()
             logGlobal->errorStream()<<", "<<anim->images.begin()->second.size()<<" image loaded in group "<< anim->images.begin()->first;
 	}
 }
-
-CAnimImage::CAnimImage(std::string name, size_t Frame, size_t Group, int x, int y, ui8 Flags):
-	frame(Frame),
-	group(Group),
-	player(-1),
-	flags(Flags)
-{
-	pos.x += x;
-	pos.y += y;
-	anim = new CAnimation(name);
-	init();
-}
-
-CAnimImage::CAnimImage(CAnimation *Anim, size_t Frame, size_t Group, int x, int y, ui8 Flags):
-	anim(Anim),
-	frame(Frame),
-	group(Group),
-	player(-1),
-	flags(Flags)
-{
-	pos.x += x;
-	pos.y += y;
-	init();
-}
-
-size_t CAnimImage::size()
-{
-	return anim->size(group);
-}
-
-void CAnimImage::init()
-{
-	anim->load(frame, group);
-	if (flags & CShowableAnim::BASE)
-		anim->load(0,group);
-	
-	IImage *img = anim->getImage(frame, group);
-	if (img)
-	{
-		pos.w = img->width();
-		pos.h = img->height();
-	}
-}
-
-CAnimImage::~CAnimImage()
-{
-	anim->unload(frame, group);
-	if (flags & CShowableAnim::BASE)
-		anim->unload(0,group);
-	delete anim;
-}
-
-void CAnimImage::showAll(SDL_Surface * to)
-{
-	IImage *img;
-
-	if ( flags & CShowableAnim::BASE && frame != 0)
-		if ((img = anim->getImage(0, group)))
-			img->draw(to, pos.x, pos.y);
-
-	if ((img = anim->getImage(frame, group)))
-		img->draw(to, pos.x, pos.y);
-}
-
-void CAnimImage::setFrame(size_t Frame, size_t Group)
-{
-	if (frame == Frame && group==Group)
-		return;
-	if (anim->size(Group) > Frame)
-	{
-		anim->load(Frame, Group);
-		anim->unload(frame, group);
-		frame = Frame;
-		group = Group;
-		IImage *img = anim->getImage(frame, group);
-		if (img)
-		{
-			if (flags & CShowableAnim::PLAYER_COLORED)
-				img->playerColored(player);
-			pos.w = img->width();
-			pos.h = img->height();
-		}
-	}
-	else
-        logGlobal->errorStream() << "Error: accessing unavailable frame " << Group << ":" << Frame << " in CAnimation!";
-}
-
-void CAnimImage::playerColored(PlayerColor currPlayer)
-{
-	player = currPlayer;
-	flags |= CShowableAnim::PLAYER_COLORED;
-	anim->getImage(frame, group)->playerColored(player);
-	if (flags & CShowableAnim::BASE)
-			anim->getImage(0, group)->playerColored(player);
-}
-
-CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 Delay, size_t Group):
-	anim(name, Flags & USE_RLE),
-	group(Group),
-	frame(0),
-	first(0),
-	frameDelay(Delay),
-	value(0),
-	flags(Flags),
-	xOffset(0),
-	yOffset(0),
-	alpha(255)
-{
-	anim.loadGroup(group);
-	last = anim.size(group);
-
-	pos.w = anim.getImage(0, group)->width();
-	pos.h = anim.getImage(0, group)->height();
-	pos.x+= x;
-	pos.y+= y;
-}
-
-CShowableAnim::~CShowableAnim()
-{
-	anim.unloadGroup(group);
-}
-
-void CShowableAnim::setAlpha(ui32 alphaValue)
-{
-	alpha = std::min<ui32>(alphaValue, 255);
-}
-
-bool CShowableAnim::set(size_t Group, size_t from, size_t to)
-{
-	size_t max = anim.size(Group);
-
-	if (to < max)
-		max = to;
-
-	if (max < from || max == 0)
-		return false;
-
-	anim.load(Group);
-	anim.unload(group);
-	group = Group;
-	frame = first = from;
-	last = max;
-	value = 0;
-	return true;
-}
-
-bool CShowableAnim::set(size_t Group)
-{
-	if (anim.size(Group)== 0)
-		return false;
-	if (group != Group)
-	{
-		anim.loadGroup(Group);
-		anim.unloadGroup(group);
-		first = 0;
-		group = Group;
-		last = anim.size(Group);
-	}
-	frame = value = 0;
-	return true;
-}
-
-void CShowableAnim::reset()
-{
-	value = 0;
-	frame = first;
-
-	if (callback)
-		callback();
-}
-
-void CShowableAnim::clipRect(int posX, int posY, int width, int height)
-{
-	xOffset = posX;
-	yOffset = posY;
-	pos.w = width;
-	pos.h = height;
-}
-
-void CShowableAnim::show(SDL_Surface * to)
-{
-	if ( flags & BASE )// && frame != first) // FIXME: results in graphical glytch in Fortress, upgraded hydra's dwelling
-		blitImage(first, group, to);
-	blitImage(frame, group, to);
-
-	if ((flags & PLAY_ONCE) && frame + 1 == last)
-		return;
-
-	if ( ++value == frameDelay )
-	{
-		value = 0;
-		if ( ++frame >= last)
-			reset();
-	}
-}
-
-void CShowableAnim::showAll(SDL_Surface * to)
-{
-	if ( flags & BASE )// && frame != first)
-		blitImage(first, group, to);
-	blitImage(frame, group, to);
-}
-
-void CShowableAnim::blitImage(size_t frame, size_t group, SDL_Surface *to)
-{
-	assert(to);
-	Rect src( xOffset, yOffset, pos.w, pos.h);
-	IImage * img = anim.getImage(frame, group);
-	if (img)
-		img->draw(to, pos.x-xOffset, pos.y-yOffset, &src, alpha);
-}
-
-void CShowableAnim::rotate(bool on, bool vertical)
-{
-	ui8 flag = vertical? VERTICAL_FLIP:HORIZONTAL_FLIP;
-	if (on)
-		flags |= flag;
-	else
-		flags &= ~flag;
-}
-
-CCreatureAnim::CCreatureAnim(int x, int y, std::string name, Rect picPos, ui8 flags, EAnimType type):
-	CShowableAnim(x,y,name,flags,4,type)
-{
-	xOffset = picPos.x;
-	yOffset = picPos.y;
-	if (picPos.w)
-		pos.w = picPos.w;
-	if (picPos.h)
-		pos.h = picPos.h;
-};
-
-void CCreatureAnim::loopPreview(bool warMachine)
-{
-	std::vector<EAnimType> available;
-
-	static const EAnimType creaPreviewList[] = {HOLDING, HITTED, DEFENCE, ATTACK_FRONT, CAST_FRONT};
-	static const EAnimType machPreviewList[] = {HOLDING, MOVING, SHOOT_UP, SHOOT_FRONT, SHOOT_DOWN};
-	auto & previewList = warMachine ? machPreviewList : creaPreviewList;
-	
-	for (auto & elem : previewList)
-		if (anim.size(elem))
-			available.push_back(elem);
-
-	size_t rnd = CRandomGenerator::getDefault().nextInt(available.size() * 2 - 1);
-
-	if (rnd >= available.size())
-	{
-		EAnimType type;
-		if ( anim.size(MOVING) == 0 )//no moving animation present
-			type = HOLDING;
-		else
-			type = MOVING;
-
-		//display this anim for ~1 second (time is random, but it looks good)
-		for (size_t i=0; i< 12/anim.size(type) + 1; i++)
-			addLast(type);
-	}
-	else
-		addLast(available[rnd]);
-}
-
-void CCreatureAnim::addLast(EAnimType newType)
-{
-	if (type != MOVING && newType == MOVING)//starting moving - play init sequence
-	{
-		queue.push( MOVE_START );
-	}
-	else if (type == MOVING && newType != MOVING )//previous anim was moving - finish it
-	{
-		queue.push( MOVE_END );
-	}
-	if (newType == TURN_L || newType == TURN_R)
-		queue.push(newType);
-
-	queue.push(newType);
-}
-
-void CCreatureAnim::reset()
-{
-	//if we are in the middle of rotation - set flag
-	if (type == TURN_L && !queue.empty() && queue.front() == TURN_L)
-		rotate(true);
-	if (type == TURN_R && !queue.empty() && queue.front() == TURN_R)
-		rotate(false);
-
-	while (!queue.empty())
-	{
-		EAnimType at = queue.front();
-		queue.pop();
-		if (set(at))
-			return;
-	}
-	if  (callback)
-		callback();
-	while (!queue.empty())
-	{
-		EAnimType at = queue.front();
-		queue.pop();
-		if (set(at))
-			return;
-	}
-	set(HOLDING);
-}
-
-void CCreatureAnim::startPreview(bool warMachine)
-{
-	callback = boost::bind(&CCreatureAnim::loopPreview, this, warMachine);
-}
-
-void CCreatureAnim::clearAndSet(EAnimType type)
-{
-	while (!queue.empty())
-		queue.pop();
-	set(type);
-}

+ 2 - 160
client/widgets/CAnimation.h → client/gui/CAnimation.h

@@ -1,7 +1,8 @@
 #pragma once
 
 #include "../../lib/vcmi_endian.h"
-#include "../gui/CIntObject.h"
+#include "gui/Geometries.h"
+#include "../../lib/GameConstants.h"
 
 /*
  * CAnimation.h, part of VCMI engine
@@ -219,162 +220,3 @@ public:
 	//total count of frames in group (including not loaded)
 	size_t size(size_t group=0) const;
 };
-
-
-/// Class for displaying one image from animation
-class CAnimImage: public CIntObject
-{
-private:
-	CAnimation* anim;
-	//displayed frame/group
-	size_t frame;
-	size_t group;
-	PlayerColor player;
-	ui8 flags;
-
-	void init();
-
-public:
-	CAnimImage(std::string name, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0);
-	CAnimImage(CAnimation* anim, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0);
-	~CAnimImage();//d-tor
-
-	//size of animation
-	size_t size();
-
-	//change displayed frame on this one
-	void setFrame(size_t Frame, size_t Group=0);
-
-	//makes image player-colored
-	void playerColored(PlayerColor player);
-
-	void showAll(SDL_Surface * to);
-};
-
-/// Base class for displaying animation, used as superclass for different animations
-class CShowableAnim: public CIntObject
-{
-public:
-	enum EFlags
-	{
-		BASE=1,            //base frame will be blitted before current one
-		HORIZONTAL_FLIP=2, //TODO: will be displayed rotated
-		VERTICAL_FLIP=4,   //TODO: will be displayed rotated
-		USE_RLE=8,         //RLE-d version, support full alpha-channel for 8-bit images
-		PLAYER_COLORED=16, //TODO: all loaded images will be player-colored
-		PLAY_ONCE=32       //play animation only once and stop at last frame
-	};
-protected:
-	CAnimation anim;
-
-	size_t group, frame;//current frame
-
-	size_t first, last; //animation range
-
-	//TODO: replace with time delay(needed for battles)
-	ui32 frameDelay;//delay in frames of each image
-	ui32 value;//how many times current frame was showed
-
-	ui8 flags;//Flags from EFlags enum
-
-	//blit image with optional rotation, fitting into rect, etc
-	void blitImage(size_t frame, size_t group, SDL_Surface *to);
-
-	//For clipping in rect, offsets of picture coordinates
-	int xOffset, yOffset;
-
-	ui8 alpha;
-
-public:
-	//called when next animation sequence is required
-	std::function<void()> callback;
-
-	//Set per-surface alpha, 0 = transparent, 255 = opaque
-	void setAlpha(ui32 alphaValue);
-
-	CShowableAnim(int x, int y, std::string name, ui8 flags=0, ui32 Delay=4, size_t Group=0);
-	~CShowableAnim();
-
-	//set animation to group or part of group
-	bool set(size_t Group);
-	bool set(size_t Group, size_t from, size_t to=-1);
-
-	//set rotation flags
-	void rotate(bool on, bool vertical=false);
-
-	//move displayed part of picture (if picture is clipped to rect)
-	void clipRect(int posX, int posY, int width, int height);
-
-	//set frame to first, call callback
-	virtual void reset();
-
-	//show current frame and increase counter
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
-};
-
-/// Creature-dependend animations like attacking, moving,...
-class CCreatureAnim: public CShowableAnim
-{
-public:
-
-	enum EHeroAnimType
-	{
-		HERO_HOLDING = 0,
-		HERO_IDLE = 1, // idling movement that happens from time to time
-		HERO_DEFEAT = 2, // played when army loses stack or on friendly fire
-		HERO_VICTORY = 3, // when enemy stack killed or huge damage is dealt
-		HERO_CAST_SPELL = 4 // spellcasting
-	};
-
-	enum EAnimType // list of creature animations, numbers were taken from def files
-	{
-		MOVING=0,
-		MOUSEON=1,
-		HOLDING=2,
-		HITTED=3,
-		DEFENCE=4,
-		DEATH=5,
-		//DEATH2=6, //unused?
-		TURN_L=7,
-		TURN_R=8, //same
-		//TURN_L2=9, //identical to previous?
-		//TURN_R2=10,
-		ATTACK_UP=11,
-		ATTACK_FRONT=12,
-		ATTACK_DOWN=13,
-		SHOOT_UP=14,
-		SHOOT_FRONT=15,
-		SHOOT_DOWN=16,
-		CAST_UP=17,
-		CAST_FRONT=18,
-		CAST_DOWN=19,
-		MOVE_START=20,
-		MOVE_END=21,
-		DEAD = 22 // new group, used to show dead stacks. If empty - last frame from "DEATH" will be copied here
-
-	};
-
-private:
-	//queue of animations waiting to be displayed
-	std::queue<EAnimType> queue;
-
-	//this function is used as callback if preview flag was set during construction
-	void loopPreview(bool warMachine);
-
-public:
-	//change anim to next if queue is not empty, call callback othervice
-	void reset();
-
-	//add sequence to the end of queue
-	void addLast(EAnimType newType);
-
-	void startPreview(bool warMachine);
-
-	//clear queue and set animation to this sequence
-	void clearAndSet(EAnimType type);
-
-	CCreatureAnim(int x, int y, std::string name, Rect picPos,
-	              ui8 flags= USE_RLE, EAnimType = HOLDING );
-
-};

+ 1 - 1
client/gui/CCursorHandler.cpp

@@ -5,9 +5,9 @@
 
 #include "SDL_Extensions.h"
 #include "CGuiHandler.h"
+#include "widgets/Images.h"
 
 #include "../CMT.h"
-#include "../widgets/CAnimation.h"
 
 /*
  * CCursorHandler.cpp, part of VCMI engine

+ 2 - 0
client/gui/CGuiHandler.cpp

@@ -1,6 +1,8 @@
 #include "StdInc.h"
 #include "CGuiHandler.h"
 
+#include <SDL.h>
+
 #include "CIntObject.h"
 #include "CCursorHandler.h"
 

+ 1 - 1
client/gui/CGuiHandler.h

@@ -1,6 +1,6 @@
 #pragma once
 
-#include "../../lib/CStopWatch.h"
+//#include "../../lib/CStopWatch.h"
 #include "Geometries.h"
 #include "SDL_Extensions.h"
 

+ 3 - 1
client/gui/CIntObject.h

@@ -10,7 +10,7 @@
  
 #pragma once
 
-#include <SDL_events.h>
+//#include <SDL_events.h>
 #include "Geometries.h"
 #include "../Graphics.h"
 
@@ -18,6 +18,8 @@ struct SDL_Surface;
 class CPicture;
 class CGuiHandler;
 
+struct SDL_KeyboardEvent;
+
 using boost::logic::tribool;
 
 // Defines a activate/deactive method

+ 6 - 1
client/gui/Geometries.cpp

@@ -1,6 +1,11 @@
 #include "StdInc.h"
 #include "Geometries.h"
 #include "../CMT.h"
+#include <SDL_events.h>
+
+Point::Point(const SDL_MouseMotionEvent &a)
+	:x(a.x),y(a.y)
+{}
 
 Rect Rect::createCentered( int w, int h )
 {
@@ -15,4 +20,4 @@ Rect Rect::around(const Rect &r, int width /*= 1*/) /*creates rect around anothe
 Rect Rect::centerIn(const Rect &r)
 {
 	return Rect(r.x + (r.w - w) / 2, r.y + (r.h - h) / 2, w, h);
-}
+}

+ 3 - 5
client/gui/Geometries.h

@@ -1,7 +1,6 @@
 #pragma once
 
 #include <SDL_video.h>
-#include <SDL_events.h>
 #include "../../lib/int3.h"
 
 /*
@@ -21,6 +20,7 @@
 #undef min
 #endif
 
+struct SDL_MouseMotionEvent;
 
 // A point with x/y coordinate, used mostly for graphic rendering
 struct Point
@@ -38,9 +38,7 @@ struct Point
 	Point(const int3 &a)
 		:x(a.x),y(a.y)
 	{}
-	Point(const SDL_MouseMotionEvent &a)
-		:x(a.x),y(a.y)
-	{}
+	Point(const SDL_MouseMotionEvent &a);
 
 	template<typename T>
 	Point operator+(const T &b) const
@@ -265,4 +263,4 @@ struct Rect : public SDL_Rect
 		ret.h = y2 -ret.y;
 		return ret;
 	}
-};
+};

+ 3 - 2
client/gui/SDL_Extensions.h

@@ -16,10 +16,11 @@
 #endif
 
 #include <SDL_video.h>
-#include <SDL_ttf.h>
+#include <SDL_events.h>
 #include "../../lib/int3.h"
-#include "../Graphics.h"
+//#include "../Graphics.h"
 #include "Geometries.h"
+#include "../../lib/GameConstants.h"
 
 
 //A macro to force inlining some of our functions. Compiler (at least MSVC) is not so smart here-> without that displaying is MUCH slower

+ 5 - 37
client/widgets/AdventureMapClasses.cpp

@@ -1,8 +1,10 @@
 #include "StdInc.h"
 #include "AdventureMapClasses.h"
 
-#include "CAnimation.h"
+#include <SDL.h>
+
 #include "MiscWidgets.h"
+#include "CComponent.h"
 
 #include "../CGameInfo.h"
 #include "../CMusicHandler.h"
@@ -13,6 +15,7 @@
 #include "../gui/CGuiHandler.h"
 #include "../gui/SDL_Pixels.h"
 
+#include "../windows/InfoWindows.h"
 #include "../windows/CAdvmapInterface.h"
 #include "../windows/GUIClasses.h"
 
@@ -34,7 +37,7 @@
 #include "../../lib/StringConstants.h"
 
 /*
- * CAdventureMapClasses.h, part of VCMI engine
+ * CAdventureMapClasses.cpp, part of VCMI engine
  *
  * Authors: listed in file AUTHORS in main folder
  *
@@ -1205,38 +1208,3 @@ CInGameConsole::CInGameConsole() : prevEntDisp(-1), defaultTimeout(10000), maxDi
 	addUsedEvents(KEYBOARD | TEXTINPUT);
 	#endif
 }
-
-CAdventureOptions::CAdventureOptions():
-	CWindowObject(PLAYER_COLORED, "ADVOPTS")
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	exit = new CAdventureMapButton("","",boost::bind(&CAdventureOptions::close, this), 204, 313, "IOK6432.DEF",SDLK_RETURN);
-	exit->assignedKeys.insert(SDLK_ESCAPE);
-
-	scenInfo = new CAdventureMapButton("","", boost::bind(&CAdventureOptions::close, this), 24, 198, "ADVINFO.DEF",SDLK_i);
-	scenInfo->callback += CAdventureOptions::showScenarioInfo;
-	//viewWorld = new CAdventureMapButton("","",boost::bind(&CGuiHandler::popIntTotally, &GH, this), 204, 313, "IOK6432.DEF",SDLK_RETURN);
-
-	puzzle = new CAdventureMapButton("","", boost::bind(&CAdventureOptions::close, this), 24, 81, "ADVPUZ.DEF");
-	puzzle->callback += boost::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT);
-
-	dig = new CAdventureMapButton("","", boost::bind(&CAdventureOptions::close, this), 24, 139, "ADVDIG.DEF");
-	if(const CGHeroInstance *h = adventureInt->curHero())
-		dig->callback += boost::bind(&CPlayerInterface::tryDiggging, LOCPLINT, h);
-	else
-		dig->block(true);
-}
-
-void CAdventureOptions::showScenarioInfo()
-{
-	auto campState = LOCPLINT->cb->getStartInfo()->campState;
-	if(campState)
-	{
-		GH.pushInt(new CBonusSelection(campState));
-	}
-	else
-	{
-		GH.pushInt(new CScenarioInfo(LOCPLINT->cb->getMapHeader(), LOCPLINT->cb->getStartInfo()));
-	}
-}

+ 3 - 11
client/widgets/AdventureMapClasses.h

@@ -1,6 +1,7 @@
 #pragma once
 
-#include "CIntObjectClasses.h"
+#include "ObjectLists.h"
+#include "../../lib/FunctionList.h"
 
 class CArmedInstance;
 class CShowableAnim;
@@ -8,6 +9,7 @@ class CGGarrison;
 class CGObjectInstance;
 class CGHeroInstance;
 class CGTownInstance;
+class CAdventureMapButton;
 struct Component;
 struct InfoAboutArmy;
 struct InfoAboutHero;
@@ -339,13 +341,3 @@ public:
 
 	CInGameConsole(); //c-tor
 };
-
-/// Adventure options dialogue where you can view the world, dig, play the replay of the last turn,...
-class CAdventureOptions : public CWindowObject
-{
-public:
-	CAdventureMapButton *exit, *viewWorld, *puzzle, *dig, *scenInfo, *replay;
-
-	CAdventureOptions();
-	static void showScenarioInfo();
-};

+ 732 - 0
client/widgets/Buttons.cpp

@@ -0,0 +1,732 @@
+#include "StdInc.h"
+#include "Buttons.h"
+
+#include "Images.h"
+#include "TextControls.h"
+
+#include "../CMusicHandler.h"
+#include "../CGameInfo.h"
+#include "../CPlayerInterface.h"
+#include "../battle/CBattleInterface.h"
+#include "../battle/CBattleInterfaceClasses.h"
+#include "../gui/CAnimation.h"
+#include "../gui/CGuiHandler.h"
+#include "../windows/InfoWindows.h"
+#include "../../lib/CConfigHandler.h"
+
+/*
+ * Buttons.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+CButtonBase::CButtonBase()
+{
+	swappedImages = keepFrame = false;
+	bitmapOffset = 0;
+	state=NORMAL;
+	image = nullptr;
+	overlay = nullptr;
+}
+
+CButtonBase::~CButtonBase()
+{
+
+}
+
+void CButtonBase::update()
+{
+	if (overlay)
+	{
+		if (state == PRESSED)
+			overlay->moveTo(overlay->pos.centerIn(pos).topLeft() + Point(1,1));
+		else
+			overlay->moveTo(overlay->pos.centerIn(pos).topLeft());
+	}
+
+	int newPos = (int)state + bitmapOffset;
+	if (newPos < 0)
+		newPos = 0;
+
+	if (state == HIGHLIGHTED && image->size() < 4)
+		newPos = image->size()-1;
+
+	if (swappedImages)
+	{
+		if (newPos == 0) newPos = 1;
+		else if (newPos == 1) newPos = 0;
+	}
+
+	if (!keepFrame)
+		image->setFrame(newPos);
+
+	if (active)
+		redraw();
+}
+
+void CButtonBase::addTextOverlay( const std::string &Text, EFonts font, SDL_Color color)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	addOverlay(new CLabel(pos.w/2, pos.h/2, font, CENTER, color, Text));
+	update();
+}
+
+void CButtonBase::addOverlay(CIntObject *newOverlay)
+{
+	delete overlay;
+	overlay = newOverlay;
+	addChild(newOverlay);
+	overlay->moveTo(overlay->pos.centerIn(pos).topLeft());
+	update();
+}
+
+void CButtonBase::setOffset(int newOffset)
+{
+	if (bitmapOffset == newOffset)
+		return;
+	bitmapOffset = newOffset;
+	update();
+}
+
+void CButtonBase::setState(ButtonState newState)
+{
+	if (state == newState)
+		return;
+	state = newState;
+	update();
+}
+
+CButtonBase::ButtonState CButtonBase::getState()
+{
+	return state;
+}
+
+bool CButtonBase::isBlocked()
+{
+	return state == BLOCKED;
+}
+
+bool CButtonBase::isHighlighted()
+{
+	return state == HIGHLIGHTED;
+}
+
+void CButtonBase::block(bool on)
+{
+	setState(on?BLOCKED:NORMAL);
+}
+
+CAdventureMapButton::CAdventureMapButton ()
+{
+	hoverable = actOnDown = borderEnabled = soundDisabled = false;
+	CSDL_Ext::colorSetAlpha(borderColor,1);// represents a transparent color, used for HighlightableButton
+	addUsedEvents(LCLICK | RCLICK | HOVER | KEYBOARD);
+}
+
+CAdventureMapButton::CAdventureMapButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &Callback, int x, int y,  const std::string &defName,int key, std::vector<std::string> * add, bool playerColoredButton )
+{
+	std::map<int,std::string> pom;
+	pom[0] = Name;
+	init(Callback, pom, HelpBox, playerColoredButton, defName, add, x, y, key);
+}
+
+CAdventureMapButton::CAdventureMapButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &Callback, config::ButtonInfo *info, int key/*=0*/ )
+{
+	std::map<int,std::string> pom;
+	pom[0] = Name;
+	init(Callback, pom, HelpBox, info->playerColoured, info->defName, &info->additionalDefs, info->x, info->y, key);
+}
+
+CAdventureMapButton::CAdventureMapButton( const std::pair<std::string, std::string> &help, const CFunctionList<void()> &Callback, int x, int y, const std::string &defName, int key/*=0*/, std::vector<std::string> * add /*= nullptr*/, bool playerColoredButton /*= false */ )
+{
+	std::map<int,std::string> pom;
+	pom[0] = help.first;
+	init(Callback, pom, help.second, playerColoredButton, defName, add, x, y, key);
+}
+
+void CAdventureMapButton::onButtonClicked()
+{
+	// debug logging to figure out pressed button (and as result - player actions) in case of crash
+	logAnim->traceStream() << "Button clicked at " << pos.x << "x" << pos.y;
+	CIntObject * parent = this->parent;
+	std::string prefix = "Parent is";
+	while (parent)
+	{
+		logAnim->traceStream() << prefix << typeid(*parent).name() << " at " << parent->pos.x << "x" << parent->pos.y;
+		parent = parent->parent;
+		prefix = '\t' + prefix;
+	}
+	callback();
+}
+
+void CAdventureMapButton::clickLeft(tribool down, bool previousState)
+{
+	if(isBlocked())
+		return;
+
+	if (down)
+	{
+		if (!soundDisabled)
+			CCS->soundh->playSound(soundBase::button);
+		setState(PRESSED);
+	}
+	else if(hoverable && hovered)
+		setState(HIGHLIGHTED);
+	else
+		setState(NORMAL);
+
+	if (actOnDown && down)
+	{
+		onButtonClicked();
+	}
+	else if (!actOnDown && previousState && (down==false))
+	{
+		onButtonClicked();
+	}
+}
+
+void CAdventureMapButton::clickRight(tribool down, bool previousState)
+{
+	if(down && helpBox.size()) //there is no point to show window with nothing inside...
+		CRClickPopup::createAndPush(helpBox);
+}
+
+void CAdventureMapButton::hover (bool on)
+{
+	if(hoverable)
+	{
+		if(on)
+			setState(HIGHLIGHTED);
+		else
+			setState(NORMAL);
+	}
+
+	if(pressedL && on)
+		setState(PRESSED);
+
+	std::string *name = (vstd::contains(hoverTexts,getState()))
+		? (&hoverTexts[getState()])
+		: (vstd::contains(hoverTexts,0) ? (&hoverTexts[0]) : nullptr);
+	if(name && name->size() && !isBlocked()) //if there is no name, there is nohing to display also
+	{
+		if (LOCPLINT && LOCPLINT->battleInt) //for battle buttons
+		{
+			if(on && LOCPLINT->battleInt->console->alterTxt == "")
+			{
+				LOCPLINT->battleInt->console->alterTxt = *name;
+				LOCPLINT->battleInt->console->whoSetAlter = 1;
+			}
+			else if (LOCPLINT->battleInt->console->alterTxt == *name)
+			{
+				LOCPLINT->battleInt->console->alterTxt = "";
+				LOCPLINT->battleInt->console->whoSetAlter = 0;
+			}
+		}
+		else if(GH.statusbar) //for other buttons
+		{
+			if (on)
+				GH.statusbar->setText(*name);
+			else if ( GH.statusbar->getText()==(*name) )
+				GH.statusbar->clear();
+		}
+	}
+}
+
+void CAdventureMapButton::init(const CFunctionList<void()> &Callback, const std::map<int,std::string> &Name, const std::string &HelpBox, bool playerColoredButton, const std::string &defName, std::vector<std::string> * add, int x, int y, int key)
+{
+	currentImage = -1;
+	addUsedEvents(LCLICK | RCLICK | HOVER | KEYBOARD);
+	callback = Callback;
+	hoverable = actOnDown = borderEnabled = soundDisabled = false;
+	CSDL_Ext::colorSetAlpha(borderColor,1);// represents a transparent color, used for HighlightableButton
+	hoverTexts = Name;
+	helpBox=HelpBox;
+
+	if (key != SDLK_UNKNOWN)
+		assignedKeys.insert(key);
+
+	pos.x += x;
+	pos.y += y;
+
+	if (!defName.empty())
+		imageNames.push_back(defName);
+	if (add)
+		for (auto & elem : *add)
+			imageNames.push_back(elem);
+	setIndex(0, playerColoredButton);
+}
+
+void CAdventureMapButton::setIndex(size_t index, bool playerColoredButton)
+{
+	if (index == currentImage || index>=imageNames.size())
+		return;
+	currentImage = index;
+	setImage(new CAnimation(imageNames[index]), playerColoredButton);
+}
+
+void CAdventureMapButton::setImage(CAnimation* anim, bool playerColoredButton, int animFlags)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	delete image;
+	image = new CAnimImage(anim, getState(), 0, 0, 0, animFlags);
+	if (playerColoredButton)
+		image->playerColored(LOCPLINT->playerID);
+
+	pos.w = image->pos.w;
+	pos.h = image->pos.h;
+}
+
+void CAdventureMapButton::setPlayerColor(PlayerColor player)
+{
+	if (image)
+		image->playerColored(player);
+}
+
+void CAdventureMapButton::showAll(SDL_Surface * to)
+{
+	CIntObject::showAll(to);
+	
+	#ifdef VCMI_SDL1
+	if (borderEnabled && borderColor.unused == 0)
+		CSDL_Ext::drawBorder(to, pos.x-1, pos.y-1, pos.w+2, pos.h+2, int3(borderColor.r, borderColor.g, borderColor.b));	
+	#else
+	if (borderEnabled && borderColor.a == 0)
+		CSDL_Ext::drawBorder(to, pos.x-1, pos.y-1, pos.w+2, pos.h+2, int3(borderColor.r, borderColor.g, borderColor.b));	
+	#endif // 0
+}
+
+void CHighlightableButton::select(bool on)
+{
+	selected = on;
+	if (on)
+	{
+		borderEnabled = true;
+		setState(HIGHLIGHTED);
+		callback();
+	}
+	else
+	{
+		borderEnabled = false;
+		setState(NORMAL);
+		callback2();
+	}
+
+	if(hoverTexts.size()>1)
+	{
+		hover(false);
+		hover(true);
+	}
+}
+
+void CHighlightableButton::clickLeft(tribool down, bool previousState)
+{
+	if(isBlocked())
+		return;
+
+	if (down && !(onlyOn && isHighlighted()))
+	{
+		CCS->soundh->playSound(soundBase::button);
+		setState(PRESSED);
+	}
+
+	if(previousState)//mouse up
+	{
+		if(down == false && getState() == PRESSED)
+			select(!selected);
+		else
+			setState(selected?HIGHLIGHTED:NORMAL);
+	}
+}
+
+CHighlightableButton::CHighlightableButton( const CFunctionList<void()> &onSelect, const CFunctionList<void()> &onDeselect, const std::map<int,std::string> &Name, const std::string &HelpBox, bool playerColoredButton, const std::string &defName, std::vector<std::string> * add, int x, int y, int key)
+: onlyOn(false), selected(false), callback2(onDeselect)
+{
+	init(onSelect,Name,HelpBox,playerColoredButton,defName,add,x,y,key);
+}
+
+CHighlightableButton::CHighlightableButton( const std::pair<std::string, std::string> &help, const CFunctionList<void()> &onSelect, int x, int y, const std::string &defName, int myid, int key/*=0*/, std::vector<std::string> * add /*= nullptr*/, bool playerColoredButton /*= false */ )
+: onlyOn(false), selected(false) // TODO: callback2(???)
+{
+	ID = myid;
+	std::map<int,std::string> pom;
+	pom[0] = help.first;
+	init(onSelect, pom, help.second, playerColoredButton, defName, add, x, y, key);
+}
+
+CHighlightableButton::CHighlightableButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &onSelect, int x, int y, const std::string &defName, int myid, int key/*=0*/, std::vector<std::string> * add /*= nullptr*/, bool playerColoredButton /*= false */ )
+: onlyOn(false), selected(false) // TODO: callback2(???)
+{
+	ID = myid;
+	std::map<int,std::string> pom;
+	pom[0] = Name;
+	init(onSelect, pom,HelpBox, playerColoredButton, defName, add, x, y, key);
+}
+
+void CHighlightableButtonsGroup::addButton(CHighlightableButton* bt)
+{
+	if (bt->parent)
+		bt->parent->removeChild(bt);
+	addChild(bt);
+	bt->recActions = defActions;//FIXME: not needed?
+
+	bt->callback += boost::bind(&CHighlightableButtonsGroup::selectionChanged,this,bt->ID);
+	bt->onlyOn = true;
+	buttons.push_back(bt);
+}
+
+void CHighlightableButtonsGroup::addButton(const std::map<int,std::string> &tooltip, const std::string &HelpBox, const std::string &defName, int x, int y, int uid, const CFunctionList<void()> &OnSelect, int key)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	CHighlightableButton *bt = new CHighlightableButton(OnSelect, 0, tooltip, HelpBox, false, defName, nullptr, x, y, key);
+	if(musicLike)
+	{
+		bt->setOffset(buttons.size()-3);
+	}
+	bt->ID = uid;
+	bt->callback += boost::bind(&CHighlightableButtonsGroup::selectionChanged,this,bt->ID);
+	bt->onlyOn = true;
+	buttons.push_back(bt);
+}
+
+CHighlightableButtonsGroup::CHighlightableButtonsGroup(const CFunctionList<void(int)> &OnChange, bool musicLikeButtons)
+: onChange(OnChange), musicLike(musicLikeButtons)
+{}
+
+CHighlightableButtonsGroup::~CHighlightableButtonsGroup()
+{
+
+}
+
+void CHighlightableButtonsGroup::select(int id, bool mode)
+{
+	assert(!buttons.empty());
+
+	CHighlightableButton *bt = buttons.front();
+	if(mode)
+	{
+		for(auto btn : buttons)
+			if (btn->ID == id)
+				bt = btn;
+	}
+	else
+	{
+		bt = buttons[id];
+	}
+	bt->select(true);
+	selectionChanged(bt->ID);
+}
+
+void CHighlightableButtonsGroup::selectionChanged(int to)
+{
+	for(auto & elem : buttons)
+		if(elem->ID!=to && elem->isHighlighted())
+			elem->select(false);
+	onChange(to);
+	if (parent)
+		parent->redraw();
+}
+
+void CHighlightableButtonsGroup::show(SDL_Surface * to)
+{
+	if (musicLike)
+	{
+		for(auto & elem : buttons)
+			if(elem->isHighlighted())
+				elem->show(to);
+	}
+	else
+		CIntObject::show(to);
+}
+
+void CHighlightableButtonsGroup::showAll(SDL_Surface * to)
+{
+	if (musicLike)
+	{
+		for(auto & elem : buttons)
+			if(elem->isHighlighted())
+				elem->showAll(to);
+	}
+	else
+		CIntObject::showAll(to);
+}
+
+void CHighlightableButtonsGroup::block( ui8 on )
+{
+	for(auto & elem : buttons)
+	{
+		elem->block(on);
+	}
+}
+
+void CSlider::sliderClicked()
+{
+	if(!(active & MOVE))
+		addUsedEvents(MOVE);
+}
+
+void CSlider::mouseMoved (const SDL_MouseMotionEvent & sEvent)
+{
+	double v = 0;
+	if(horizontal)
+	{
+		if(	std::abs(sEvent.y-(pos.y+pos.h/2)) > pos.h/2+40  ||  std::abs(sEvent.x-(pos.x+pos.w/2)) > pos.w/2  )
+			return;
+		v = sEvent.x - pos.x - 24;
+		v *= positions;
+		v /= (pos.w - 48);
+	}
+	else
+	{
+		if(std::abs(sEvent.x-(pos.x+pos.w/2)) > pos.w/2+40  ||  std::abs(sEvent.y-(pos.y+pos.h/2)) > pos.h/2  )
+			return;
+		v = sEvent.y - pos.y - 24;
+		v *= positions;
+		v /= (pos.h - 48);
+	}
+	v += 0.5;
+	if(v!=value)
+	{
+		moveTo(v);
+		redrawSlider();
+	}
+}
+
+void CSlider::redrawSlider()
+{
+	//slider->show(screenBuf);
+}
+
+void CSlider::moveLeft()
+{
+	moveTo(value-1);
+}
+
+void CSlider::moveRight()
+{
+	moveTo(value+1);
+}
+
+void CSlider::moveTo(int to)
+{
+	vstd::amax(to, 0);
+	vstd::amin(to, positions);
+
+	//same, old position?
+	if(value == to)
+		return;
+
+	value = to;
+	if(horizontal)
+	{
+		if(positions)
+		{
+			double part = static_cast<double>(to) / positions;
+			part*=(pos.w-48);
+			int newPos = part + pos.x + 16 - slider->pos.x;
+			slider->moveBy(Point(newPos, 0));
+		}
+		else
+			slider->moveTo(Point(pos.x+16, pos.y));
+	}
+	else
+	{
+		if(positions)
+		{
+			double part = static_cast<double>(to) / positions;
+			part*=(pos.h-48);
+			int newPos = part + pos.y + 16 - slider->pos.y;
+			slider->moveBy(Point(0, newPos));
+		}
+		else
+			slider->moveTo(Point(pos.x, pos.y+16));
+	}
+
+	if(moved)
+		moved(to);
+}
+
+void CSlider::clickLeft(tribool down, bool previousState)
+{
+	if(down && !slider->isBlocked())
+	{
+		double pw = 0;
+		double rw = 0;
+		if(horizontal)
+		{
+			pw = GH.current->motion.x-pos.x-25;
+			rw = pw / static_cast<double>(pos.w - 48);
+		}
+		else
+		{
+			pw = GH.current->motion.y-pos.y-24;
+			rw = pw / (pos.h-48);
+		}
+		if(pw < -8  ||  pw > (horizontal ? pos.w : pos.h) - 40)
+			return;
+		// 		if (rw>1) return;
+		// 		if (rw<0) return;
+		slider->clickLeft(true, slider->pressedL);
+		moveTo(rw * positions  +  0.5);
+		return;
+	}
+	if(active & MOVE)
+		removeUsedEvents(MOVE);
+}
+
+CSlider::~CSlider()
+{
+
+}
+
+CSlider::CSlider(int x, int y, int totalw, std::function<void(int)> Moved, int Capacity, int Amount, int Value, bool Horizontal, int style):
+    capacity(Capacity),
+    amount(Amount),
+    scrollStep(1),
+    horizontal(Horizontal),
+    moved(Moved)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	setAmount(amount);
+
+	addUsedEvents(LCLICK | KEYBOARD | WHEEL);
+	strongInterest = true;
+
+
+	left = new CAdventureMapButton();
+	right = new CAdventureMapButton();
+	slider = new CAdventureMapButton();
+
+	pos.x += x;
+	pos.y += y;
+
+	if(horizontal)
+	{
+		left->pos.y = slider->pos.y = right->pos.y = pos.y;
+		left->pos.x = pos.x;
+		right->pos.x = pos.x + totalw - 16;
+	}
+	else
+	{
+		left->pos.x = slider->pos.x = right->pos.x = pos.x;
+		left->pos.y = pos.y;
+		right->pos.y = pos.y + totalw - 16;
+	}
+
+	left->callback = boost::bind(&CSlider::moveLeft,this);
+	right->callback = boost::bind(&CSlider::moveRight,this);
+	slider->callback = boost::bind(&CSlider::sliderClicked,this);
+	left->pos.w = left->pos.h = right->pos.w = right->pos.h = slider->pos.w = slider->pos.h = 16;
+	if(horizontal)
+	{
+		pos.h = 16;
+		pos.w = totalw;
+	}
+	else
+	{
+		pos.w = 16;
+		pos.h = totalw;
+	}
+
+	if(style == 0)
+	{
+		std::string name = horizontal?"IGPCRDIV.DEF":"OVBUTN2.DEF";
+		//NOTE: this images do not have "blocked" frames. They should be implemented somehow (e.g. palette transform or something...)
+
+		//use source def to create custom animations. Format "name.def:123" will load this frame from def file
+		auto animLeft = new CAnimation();
+		animLeft->setCustom(name + ":0", 0);
+		animLeft->setCustom(name + ":1", 1);
+		left->setImage(animLeft);
+
+		auto animRight = new CAnimation();
+		animRight->setCustom(name + ":2", 0);
+		animRight->setCustom(name + ":3", 1);
+		right->setImage(animRight);
+
+		auto animSlider = new CAnimation();
+		animSlider->setCustom(name + ":4", 0);
+		slider->setImage(animSlider);
+	}
+	else
+	{
+		left->setImage(new CAnimation(horizontal ? "SCNRBLF.DEF" : "SCNRBUP.DEF"));
+		right->setImage(new CAnimation(horizontal ? "SCNRBRT.DEF" : "SCNRBDN.DEF"));
+		slider->setImage(new CAnimation("SCNRBSL.DEF"));
+	}
+	slider->actOnDown = true;
+	slider->soundDisabled = true;
+	left->soundDisabled = true;
+	right->soundDisabled = true;
+
+	value = -1;
+	moveTo(Value);
+}
+
+void CSlider::block( bool on )
+{
+	left->block(on);
+	right->block(on);
+	slider->block(on);
+}
+
+void CSlider::setAmount( int to )
+{
+	amount = to;
+	positions = to - capacity;
+	vstd::amax(positions, 0);
+}
+
+void CSlider::showAll(SDL_Surface * to)
+{
+	CSDL_Ext::fillRectBlack(to, &pos);
+	CIntObject::showAll(to);
+}
+
+void CSlider::wheelScrolled(bool down, bool in)
+{
+	moveTo(value + 3 * (down ? +scrollStep : -scrollStep));
+}
+
+void CSlider::keyPressed(const SDL_KeyboardEvent & key)
+{
+	if(key.state != SDL_PRESSED) return;
+
+	int moveDest = 0;
+	switch(key.keysym.sym)
+	{
+	case SDLK_UP:
+	case SDLK_LEFT:
+		moveDest = value - scrollStep;
+		break;
+	case SDLK_DOWN:
+	case SDLK_RIGHT:
+		moveDest = value + scrollStep;
+		break;
+	case SDLK_PAGEUP:
+		moveDest = value - capacity + scrollStep;
+		break;
+	case SDLK_PAGEDOWN:
+		moveDest = value + capacity - scrollStep;
+		break;
+	case SDLK_HOME:
+		moveDest = 0;
+		break;
+	case SDLK_END:
+		moveDest = amount - capacity;
+		break;
+	default:
+		return;
+	}
+
+	moveTo(moveDest);
+}
+
+void CSlider::moveToMax()
+{
+	moveTo(amount);
+}

+ 175 - 0
client/widgets/Buttons.h

@@ -0,0 +1,175 @@
+#pragma once
+
+#include "../gui/CIntObject.h"
+#include "../gui/SDL_Extensions.h"
+
+#include "../../lib/FunctionList.h"
+
+struct SDL_Surface;
+struct Rect;
+class CAnimImage;
+class CLabel;
+class CAnimation;
+class CDefHandler;
+
+namespace config
+{
+	struct ButtonInfo;
+}
+
+/*
+ * Buttons.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+/// Base class for buttons.
+class CButtonBase : public CKeyShortcut
+{
+public:
+	enum ButtonState
+	{
+		NORMAL=0,
+		PRESSED=1,
+		BLOCKED=2,
+		HIGHLIGHTED=3
+	};
+private:
+	int bitmapOffset; // base offset of visible bitmap from animation
+	ButtonState state;//current state of button from enum
+
+public:
+	bool swappedImages,//fix for some buttons: normal and pressed image are swapped
+		keepFrame; // don't change visual representation
+
+	void addOverlay(CIntObject * newOverlay);
+	void addTextOverlay(const std::string &Text, EFonts font, SDL_Color color = Colors::WHITE);
+
+	void update();//to refresh button after image or text change
+
+	void setOffset(int newOffset);
+	void setState(ButtonState newState);
+	ButtonState getState();
+
+	//just to make code clearer
+	void block(bool on);
+	bool isBlocked();
+	bool isHighlighted();
+
+	CAnimImage * image; //image for this button
+	CIntObject * overlay;//object-overlay
+
+	CButtonBase(); //c-tor
+	virtual ~CButtonBase(); //d-tor
+};
+
+/// Typical Heroes 3 button which can be inactive or active and can 
+/// hold further information if you right-click it
+class CAdventureMapButton : public CButtonBase
+{
+	std::vector<std::string> imageNames;//store list of images that can be used by this button
+	size_t currentImage;
+
+	void onButtonClicked(); // calls callback
+public:
+	std::map<int, std::string> hoverTexts; //text for statusbar
+	std::string helpBox; //for right-click help
+	CFunctionList<void()> callback;
+	bool actOnDown,//runs when mouse is pressed down over it, not when up
+		hoverable,//if true, button will be highlighted when hovered
+		borderEnabled,
+		soundDisabled;
+	SDL_Color borderColor;
+
+	void clickRight(tribool down, bool previousState);
+	virtual void clickLeft(tribool down, bool previousState);
+	void hover (bool on);
+
+	CAdventureMapButton(); //c-tor
+	CAdventureMapButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &Callback, int x, int y, const std::string &defName, int key=0, std::vector<std::string> * add = nullptr, bool playerColoredButton = false );//c-tor
+	CAdventureMapButton( const std::pair<std::string, std::string> &help, const CFunctionList<void()> &Callback, int x, int y, const std::string &defName, int key=0, std::vector<std::string> * add = nullptr, bool playerColoredButton = false );//c-tor
+	CAdventureMapButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &Callback, config::ButtonInfo *info, int key=0);//c-tor
+
+	void init(const CFunctionList<void()> &Callback, const std::map<int,std::string> &Name, const std::string &HelpBox, bool playerColoredButton, const std::string &defName, std::vector<std::string> * add, int x, int y, int key );
+
+	void setIndex(size_t index, bool playerColoredButton=false);
+	void setImage(CAnimation* anim, bool playerColoredButton=false, int animFlags=0);
+	void setPlayerColor(PlayerColor player);
+	void showAll(SDL_Surface * to);
+};
+
+/// A button which can be selected/deselected
+class CHighlightableButton 
+	: public CAdventureMapButton
+{
+public:
+	CHighlightableButton(const CFunctionList<void()> &onSelect, const CFunctionList<void()> &onDeselect, const std::map<int,std::string> &Name, const std::string &HelpBox, bool playerColoredButton, const std::string &defName, std::vector<std::string> * add, int x, int y, int key=0);
+	CHighlightableButton(const std::pair<std::string, std::string> &help, const CFunctionList<void()> &onSelect, int x, int y, const std::string &defName, int myid, int key=0, std::vector<std::string> * add = nullptr, bool playerColoredButton = false );//c-tor
+	CHighlightableButton(const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &onSelect, int x, int y, const std::string &defName, int myid, int key=0, std::vector<std::string> * add = nullptr, bool playerColoredButton = false );//c-tor
+	bool onlyOn;//button can not be de-selected
+	bool selected;//state of highlightable button
+	int ID; //for identification
+	CFunctionList<void()> callback2; //when de-selecting
+	void select(bool on);
+	void clickLeft(tribool down, bool previousState);
+};
+
+/// A group of buttons where one button can be selected
+class CHighlightableButtonsGroup : public CIntObject
+{
+public:
+	CFunctionList<void(int)> onChange; //called when changing selected button with new button's id
+	std::vector<CHighlightableButton*> buttons;
+	bool musicLike; //determines the behaviour of this group
+
+	//void addButton(const std::map<int,std::string> &tooltip, const std::string &HelpBox, const std::string &defName, int x, int y, int uid);
+	void addButton(CHighlightableButton* bt);//add existing button, it'll be deleted by CHighlightableButtonsGroup destructor
+	void addButton(const std::map<int,std::string> &tooltip, const std::string &HelpBox, const std::string &defName, int x, int y, int uid, const CFunctionList<void()> &OnSelect=0, int key=0); //creates new button
+	CHighlightableButtonsGroup(const CFunctionList<void(int)> & OnChange, bool musicLikeButtons = false);
+	~CHighlightableButtonsGroup();
+	void select(int id, bool mode); //mode==0: id is serial; mode==1: id is unique button id
+	void selectionChanged(int to);
+	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
+	void block(ui8 on);
+};
+
+/// A typical slider which can be orientated horizontally/vertically.
+class CSlider : public CIntObject
+{
+public:
+	CAdventureMapButton *left, *right, *slider; //if vertical then left=up
+	int capacity;//how many elements can be active at same time (e.g. hero list = 5)
+	int amount; //total amount of elements (e.g. hero list = 0-8)
+	int positions; //number of highest position (0 if there is only one)
+	int value; //first active element
+	int scrollStep; // how many elements will be scrolled via one click, default = 1
+	bool horizontal;
+	bool wheelScrolling;
+	bool keyScrolling;
+
+	std::function<void(int)> moved;
+
+	void redrawSlider(); 
+	void sliderClicked();
+	void moveLeft();
+	void moveRight();
+	void moveTo(int to);
+	void block(bool on);
+	void setAmount(int to);
+
+	void keyPressed(const SDL_KeyboardEvent & key);
+	void wheelScrolled(bool down, bool in);
+	void clickLeft(tribool down, bool previousState);
+	void mouseMoved (const SDL_MouseMotionEvent & sEvent);
+	void showAll(SDL_Surface * to);	
+
+	CSlider(int x, int y, int totalw, std::function<void(int)> Moved, int Capacity, int Amount, 
+		int Value=0, bool Horizontal=true, int style = 0); //style 0 - brown, 1 - blue
+	~CSlider();
+	void moveToMax();
+};

+ 3 - 1
client/widgets/CArtifactHolder.cpp

@@ -4,7 +4,9 @@
 #include "../gui/CGuiHandler.h"
 #include "../gui/CCursorHandler.h"
 
-#include "CAnimation.h"
+#include "Buttons.h"
+#include "CComponent.h"
+
 #include "../windows/CHeroWindow.h"
 #include "../windows/CSpellWindow.h"
 #include "../windows/GUIClasses.h"

+ 2 - 1
client/widgets/CArtifactHolder.h

@@ -1,6 +1,7 @@
 #pragma once
 
-#include "CComponent.h"
+//#include "CComponent.h"
+#include "MiscWidgets.h"
 
 /*
  * CArtifactHolder.h, part of VCMI engine

+ 1 - 1
client/widgets/CComponent.cpp

@@ -4,9 +4,9 @@
 #include "../gui/CGuiHandler.h"
 #include "../gui/CCursorHandler.h"
 
-#include "CAnimation.h"
 #include "../CMessage.h"
 #include "../CGameInfo.h"
+#include "../widgets/Images.h"
 #include "../windows/CAdvmapInterface.h"
 
 #include "../../lib/CArtHandler.h"

+ 2 - 14
client/widgets/CComponent.h

@@ -1,6 +1,6 @@
 #pragma once
 
-#include "CIntObjectClasses.h"
+#include "../gui/CIntObject.h"
 
 /*
  * CComponent.h, part of VCMI engine
@@ -13,6 +13,7 @@
  */
 
 struct Component;
+class CAnimImage;
 
 /// common popup window component
 class CComponent : public virtual CIntObject
@@ -109,16 +110,3 @@ public:
 	/// onSelect - optional function that will be called every time on selection change
 	CComponentBox(std::vector<CSelectableComponent *> components, Rect position, std::function<void(int newID)> onSelect = nullptr);
 };
-
-/// Can interact on left and right mouse clicks
-class LRClickableAreaWTextComp: public LRClickableAreaWText
-{
-public:
-	int baseType;
-	int bonusValue, type;
-	virtual void clickLeft(tribool down, bool previousState);
-	virtual void clickRight(tribool down, bool previousState);
-
-	LRClickableAreaWTextComp(const Rect &Pos = Rect(0,0,0,0), int BaseType = -1);
-	CComponent * createComponent() const;
-};

+ 12 - 1
client/widgets/CGarrisonInt.cpp

@@ -3,9 +3,10 @@
 
 #include "../gui/CGuiHandler.h"
 
-#include "CAnimation.h"
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
+#include "../widgets/Buttons.h"
+#include "../widgets/TextControls.h"
 #include "../windows/CCreatureWindow.h"
 #include "../windows/GUIClasses.h"
 
@@ -17,6 +18,16 @@
 
 #include "../../lib/CGameState.h"
 
+/*
+ * CGarrisonInt.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
 void CGarrisonSlot::setHighlight(bool on)
 {
 	if (on)

+ 1 - 1
client/widgets/CGarrisonInt.h

@@ -1,6 +1,6 @@
 #pragma once
 
-#include "CIntObjectClasses.h"
+#include "../windows/CWindowObject.h"
 
 /*
  * CGarrisonInt.h, part of VCMI engine

+ 0 - 2025
client/widgets/CIntObjectClasses.cpp

@@ -1,2025 +0,0 @@
-#include "StdInc.h"
-#include "CIntObjectClasses.h"
-
-#include "CAnimation.h"
-#include "MiscWidgets.h"
-
-#include "../gui/SDL_Pixels.h"
-#include "../gui/SDL_Extensions.h"
-#include "../gui/CGuiHandler.h"
-#include "../gui/CCursorHandler.h"
-
-#include "../battle/CBattleInterface.h"
-#include "../battle/CBattleInterfaceClasses.h"
-
-#include "../CBitmapHandler.h"
-#include "../Graphics.h"
-#include "../CGameInfo.h"
-#include "../CPlayerInterface.h"
-#include "../CMessage.h"
-#include "../CMusicHandler.h"
-#include "../windows/CAdvmapInterface.h"
-
-#include "../../CCallback.h"
-
-#include "../../lib/CConfigHandler.h"
-#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
-
-CPicture::CPicture( SDL_Surface *BG, int x, int y, bool Free )
-{
-	init();
-	bg = BG;
-	freeSurf = Free;
-	pos.x += x;
-	pos.y += y;
-	pos.w = BG->w;
-	pos.h = BG->h;
-}
-
-CPicture::CPicture( const std::string &bmpname, int x, int y )
-{
-	init();
-	bg = BitmapHandler::loadBitmap(bmpname);
-	freeSurf = true;;
-	pos.x += x;
-	pos.y += y;
-	if(bg)
-	{
-		pos.w = bg->w;
-		pos.h = bg->h;
-	}
-	else
-	{
-		pos.w = pos.h = 0;
-	}
-}
-
-CPicture::CPicture(const Rect &r, const SDL_Color &color, bool screenFormat /*= false*/)
-{
-	init();
-	createSimpleRect(r, screenFormat, SDL_MapRGB(bg->format, color.r, color.g,color.b));
-}
-
-CPicture::CPicture(const Rect &r, ui32 color, bool screenFormat /*= false*/)
-{
-	init();
-	createSimpleRect(r, screenFormat, color);
-}
-
-CPicture::CPicture(SDL_Surface *BG, const Rect &SrcRect, int x /*= 0*/, int y /*= 0*/, bool free /*= false*/)
-{
-	needRefresh = false;
-	srcRect = new Rect(SrcRect);
-	pos.x += x;
-	pos.y += y;
-	pos.w = srcRect->w;
-	pos.h = srcRect->h;
-	bg = BG;
-	freeSurf = free;
-}
-
-void CPicture::setSurface(SDL_Surface *to)
-{
-	bg = to;
-	if (srcRect)
-	{
-		pos.w = srcRect->w;
-		pos.h = srcRect->h;
-	}
-	else
-	{
-		pos.w = bg->w;
-		pos.h = bg->h;
-	}
-}
-
-CPicture::~CPicture()
-{
-	if(freeSurf)
-		SDL_FreeSurface(bg);
-	delete srcRect;
-}
-
-void CPicture::init()
-{
-	needRefresh = false;
-	srcRect = nullptr;
-}
-
-void CPicture::show(SDL_Surface * to)
-{
-	if (needRefresh)
-		showAll(to);
-}
-
-void CPicture::showAll(SDL_Surface * to)
-{
-	if(bg)
-	{
-		if(srcRect)
-		{
-			SDL_Rect srcRectCpy = *srcRect;
-			SDL_Rect dstRect = srcRectCpy;
-			dstRect.x = pos.x;
-			dstRect.y = pos.y;
-
-			CSDL_Ext::blitSurface(bg, &srcRectCpy, to, &dstRect);
-		}
-		else
-			blitAt(bg, pos, to);
-	}
-}
-
-void CPicture::convertToScreenBPP()
-{
-	SDL_Surface *hlp = bg;
-	bg = SDL_ConvertSurface(hlp,screen->format,0);
-	CSDL_Ext::setDefaultColorKey(bg);	
-	SDL_FreeSurface(hlp);
-}
-
-void CPicture::setAlpha(int value)
-{	
-	#ifdef VCMI_SDL1
-	SDL_SetAlpha(bg, SDL_SRCALPHA, value);	
-	#else
-	SDL_SetSurfaceAlphaMod(bg,value);
-	#endif // 0
-}
-
-void CPicture::scaleTo(Point size)
-{
-	SDL_Surface * scaled = CSDL_Ext::scaleSurface(bg, size.x, size.y);
-
-	if(freeSurf)
-		SDL_FreeSurface(bg);
-
-	setSurface(scaled);
-	freeSurf = false;
-}
-
-void CPicture::createSimpleRect(const Rect &r, bool screenFormat, ui32 color)
-{
-	pos += r;
-	pos.w = r.w;
-	pos.h = r.h;
-	if(screenFormat)
-		bg = CSDL_Ext::newSurface(r.w, r.h);
-	else
-		bg = SDL_CreateRGBSurface(SDL_SWSURFACE, r.w, r.h, 8, 0, 0, 0, 0);
-
-	SDL_FillRect(bg, nullptr, color);
-	freeSurf = true;
-}
-
-void CPicture::colorizeAndConvert(PlayerColor player)
-{
-	assert(bg);
-	colorize(player);
-	convertToScreenBPP();
-}
-
-void CPicture::colorize(PlayerColor player)
-{
-	assert(bg);
-	graphics->blueToPlayersAdv(bg, player);
-}
-
-CFilledTexture::CFilledTexture(std::string imageName, Rect position):
-    CIntObject(0, position.topLeft()),
-    texture(BitmapHandler::loadBitmap(imageName))
-{
-	pos.w = position.w;
-	pos.h = position.h;
-}
-
-CFilledTexture::~CFilledTexture()
-{
-	SDL_FreeSurface(texture);
-}
-
-void CFilledTexture::showAll(SDL_Surface *to)
-{
-	CSDL_Ext::CClipRectGuard guard(to, pos);
-	CSDL_Ext::fillTexture(to, texture);
-}
-
-CButtonBase::CButtonBase()
-{
-	swappedImages = keepFrame = false;
-	bitmapOffset = 0;
-	state=NORMAL;
-	image = nullptr;
-	overlay = nullptr;
-}
-
-CButtonBase::~CButtonBase()
-{
-
-}
-
-void CButtonBase::update()
-{
-	if (overlay)
-	{
-		if (state == PRESSED)
-			overlay->moveTo(overlay->pos.centerIn(pos).topLeft() + Point(1,1));
-		else
-			overlay->moveTo(overlay->pos.centerIn(pos).topLeft());
-	}
-
-	int newPos = (int)state + bitmapOffset;
-	if (newPos < 0)
-		newPos = 0;
-
-	if (state == HIGHLIGHTED && image->size() < 4)
-		newPos = image->size()-1;
-
-	if (swappedImages)
-	{
-		if (newPos == 0) newPos = 1;
-		else if (newPos == 1) newPos = 0;
-	}
-
-	if (!keepFrame)
-		image->setFrame(newPos);
-
-	if (active)
-		redraw();
-}
-
-void CButtonBase::addTextOverlay( const std::string &Text, EFonts font, SDL_Color color)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	addOverlay(new CLabel(pos.w/2, pos.h/2, font, CENTER, color, Text));
-	update();
-}
-
-void CButtonBase::addOverlay(CIntObject *newOverlay)
-{
-	delete overlay;
-	overlay = newOverlay;
-	addChild(newOverlay);
-	overlay->moveTo(overlay->pos.centerIn(pos).topLeft());
-	update();
-}
-
-void CButtonBase::setOffset(int newOffset)
-{
-	if (bitmapOffset == newOffset)
-		return;
-	bitmapOffset = newOffset;
-	update();
-}
-
-void CButtonBase::setState(ButtonState newState)
-{
-	if (state == newState)
-		return;
-	state = newState;
-	update();
-}
-
-CButtonBase::ButtonState CButtonBase::getState()
-{
-	return state;
-}
-
-bool CButtonBase::isBlocked()
-{
-	return state == BLOCKED;
-}
-
-bool CButtonBase::isHighlighted()
-{
-	return state == HIGHLIGHTED;
-}
-
-void CButtonBase::block(bool on)
-{
-	setState(on?BLOCKED:NORMAL);
-}
-
-CAdventureMapButton::CAdventureMapButton ()
-{
-	hoverable = actOnDown = borderEnabled = soundDisabled = false;
-	CSDL_Ext::colorSetAlpha(borderColor,1);// represents a transparent color, used for HighlightableButton
-	addUsedEvents(LCLICK | RCLICK | HOVER | KEYBOARD);
-}
-
-CAdventureMapButton::CAdventureMapButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &Callback, int x, int y,  const std::string &defName,int key, std::vector<std::string> * add, bool playerColoredButton )
-{
-	std::map<int,std::string> pom;
-	pom[0] = Name;
-	init(Callback, pom, HelpBox, playerColoredButton, defName, add, x, y, key);
-}
-
-CAdventureMapButton::CAdventureMapButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &Callback, config::ButtonInfo *info, int key/*=0*/ )
-{
-	std::map<int,std::string> pom;
-	pom[0] = Name;
-	init(Callback, pom, HelpBox, info->playerColoured, info->defName, &info->additionalDefs, info->x, info->y, key);
-}
-
-CAdventureMapButton::CAdventureMapButton( const std::pair<std::string, std::string> &help, const CFunctionList<void()> &Callback, int x, int y, const std::string &defName, int key/*=0*/, std::vector<std::string> * add /*= nullptr*/, bool playerColoredButton /*= false */ )
-{
-	std::map<int,std::string> pom;
-	pom[0] = help.first;
-	init(Callback, pom, help.second, playerColoredButton, defName, add, x, y, key);
-}
-
-void CAdventureMapButton::onButtonClicked()
-{
-	// debug logging to figure out pressed button (and as result - player actions) in case of crash
-	logAnim->traceStream() << "Button clicked at " << pos.x << "x" << pos.y;
-	CIntObject * parent = this->parent;
-	std::string prefix = "Parent is";
-	while (parent)
-	{
-		logAnim->traceStream() << prefix << typeid(*parent).name() << " at " << parent->pos.x << "x" << parent->pos.y;
-		parent = parent->parent;
-		prefix = '\t' + prefix;
-	}
-	callback();
-}
-
-void CAdventureMapButton::clickLeft(tribool down, bool previousState)
-{
-	if(isBlocked())
-		return;
-
-	if (down)
-	{
-		if (!soundDisabled)
-			CCS->soundh->playSound(soundBase::button);
-		setState(PRESSED);
-	}
-	else if(hoverable && hovered)
-		setState(HIGHLIGHTED);
-	else
-		setState(NORMAL);
-
-	if (actOnDown && down)
-	{
-		onButtonClicked();
-	}
-	else if (!actOnDown && previousState && (down==false))
-	{
-		onButtonClicked();
-	}
-}
-
-void CAdventureMapButton::clickRight(tribool down, bool previousState)
-{
-	if(down && helpBox.size()) //there is no point to show window with nothing inside...
-		CRClickPopup::createAndPush(helpBox);
-}
-
-void CAdventureMapButton::hover (bool on)
-{
-	if(hoverable)
-	{
-		if(on)
-			setState(HIGHLIGHTED);
-		else
-			setState(NORMAL);
-	}
-
-	if(pressedL && on)
-		setState(PRESSED);
-
-	std::string *name = (vstd::contains(hoverTexts,getState()))
-		? (&hoverTexts[getState()])
-		: (vstd::contains(hoverTexts,0) ? (&hoverTexts[0]) : nullptr);
-	if(name && name->size() && !isBlocked()) //if there is no name, there is nohing to display also
-	{
-		if (LOCPLINT && LOCPLINT->battleInt) //for battle buttons
-		{
-			if(on && LOCPLINT->battleInt->console->alterTxt == "")
-			{
-				LOCPLINT->battleInt->console->alterTxt = *name;
-				LOCPLINT->battleInt->console->whoSetAlter = 1;
-			}
-			else if (LOCPLINT->battleInt->console->alterTxt == *name)
-			{
-				LOCPLINT->battleInt->console->alterTxt = "";
-				LOCPLINT->battleInt->console->whoSetAlter = 0;
-			}
-		}
-		else if(GH.statusbar) //for other buttons
-		{
-			if (on)
-				GH.statusbar->setText(*name);
-			else if ( GH.statusbar->getText()==(*name) )
-				GH.statusbar->clear();
-		}
-	}
-}
-
-void CAdventureMapButton::init(const CFunctionList<void()> &Callback, const std::map<int,std::string> &Name, const std::string &HelpBox, bool playerColoredButton, const std::string &defName, std::vector<std::string> * add, int x, int y, int key)
-{
-	currentImage = -1;
-	addUsedEvents(LCLICK | RCLICK | HOVER | KEYBOARD);
-	callback = Callback;
-	hoverable = actOnDown = borderEnabled = soundDisabled = false;
-	CSDL_Ext::colorSetAlpha(borderColor,1);// represents a transparent color, used for HighlightableButton
-	hoverTexts = Name;
-	helpBox=HelpBox;
-
-	if (key != SDLK_UNKNOWN)
-		assignedKeys.insert(key);
-
-	pos.x += x;
-	pos.y += y;
-
-	if (!defName.empty())
-		imageNames.push_back(defName);
-	if (add)
-		for (auto & elem : *add)
-			imageNames.push_back(elem);
-	setIndex(0, playerColoredButton);
-}
-
-void CAdventureMapButton::setIndex(size_t index, bool playerColoredButton)
-{
-	if (index == currentImage || index>=imageNames.size())
-		return;
-	currentImage = index;
-	setImage(new CAnimation(imageNames[index]), playerColoredButton);
-}
-
-void CAdventureMapButton::setImage(CAnimation* anim, bool playerColoredButton, int animFlags)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	delete image;
-	image = new CAnimImage(anim, getState(), 0, 0, 0, animFlags);
-	if (playerColoredButton)
-		image->playerColored(LOCPLINT->playerID);
-
-	pos.w = image->pos.w;
-	pos.h = image->pos.h;
-}
-
-void CAdventureMapButton::setPlayerColor(PlayerColor player)
-{
-	if (image)
-		image->playerColored(player);
-}
-
-void CAdventureMapButton::showAll(SDL_Surface * to)
-{
-	CIntObject::showAll(to);
-	
-	#ifdef VCMI_SDL1
-	if (borderEnabled && borderColor.unused == 0)
-		CSDL_Ext::drawBorder(to, pos.x-1, pos.y-1, pos.w+2, pos.h+2, int3(borderColor.r, borderColor.g, borderColor.b));	
-	#else
-	if (borderEnabled && borderColor.a == 0)
-		CSDL_Ext::drawBorder(to, pos.x-1, pos.y-1, pos.w+2, pos.h+2, int3(borderColor.r, borderColor.g, borderColor.b));	
-	#endif // 0
-}
-
-void CHighlightableButton::select(bool on)
-{
-	selected = on;
-	if (on)
-	{
-		borderEnabled = true;
-		setState(HIGHLIGHTED);
-		callback();
-	}
-	else
-	{
-		borderEnabled = false;
-		setState(NORMAL);
-		callback2();
-	}
-
-	if(hoverTexts.size()>1)
-	{
-		hover(false);
-		hover(true);
-	}
-}
-
-void CHighlightableButton::clickLeft(tribool down, bool previousState)
-{
-	if(isBlocked())
-		return;
-
-	if (down && !(onlyOn && isHighlighted()))
-	{
-		CCS->soundh->playSound(soundBase::button);
-		setState(PRESSED);
-	}
-
-	if(previousState)//mouse up
-	{
-		if(down == false && getState() == PRESSED)
-			select(!selected);
-		else
-			setState(selected?HIGHLIGHTED:NORMAL);
-	}
-}
-
-CHighlightableButton::CHighlightableButton( const CFunctionList<void()> &onSelect, const CFunctionList<void()> &onDeselect, const std::map<int,std::string> &Name, const std::string &HelpBox, bool playerColoredButton, const std::string &defName, std::vector<std::string> * add, int x, int y, int key)
-: onlyOn(false), selected(false), callback2(onDeselect)
-{
-	init(onSelect,Name,HelpBox,playerColoredButton,defName,add,x,y,key);
-}
-
-CHighlightableButton::CHighlightableButton( const std::pair<std::string, std::string> &help, const CFunctionList<void()> &onSelect, int x, int y, const std::string &defName, int myid, int key/*=0*/, std::vector<std::string> * add /*= nullptr*/, bool playerColoredButton /*= false */ )
-: onlyOn(false), selected(false) // TODO: callback2(???)
-{
-	ID = myid;
-	std::map<int,std::string> pom;
-	pom[0] = help.first;
-	init(onSelect, pom, help.second, playerColoredButton, defName, add, x, y, key);
-}
-
-CHighlightableButton::CHighlightableButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &onSelect, int x, int y, const std::string &defName, int myid, int key/*=0*/, std::vector<std::string> * add /*= nullptr*/, bool playerColoredButton /*= false */ )
-: onlyOn(false), selected(false) // TODO: callback2(???)
-{
-	ID = myid;
-	std::map<int,std::string> pom;
-	pom[0] = Name;
-	init(onSelect, pom,HelpBox, playerColoredButton, defName, add, x, y, key);
-}
-
-void CHighlightableButtonsGroup::addButton(CHighlightableButton* bt)
-{
-	if (bt->parent)
-		bt->parent->removeChild(bt);
-	addChild(bt);
-	bt->recActions = defActions;//FIXME: not needed?
-
-	bt->callback += boost::bind(&CHighlightableButtonsGroup::selectionChanged,this,bt->ID);
-	bt->onlyOn = true;
-	buttons.push_back(bt);
-}
-
-void CHighlightableButtonsGroup::addButton(const std::map<int,std::string> &tooltip, const std::string &HelpBox, const std::string &defName, int x, int y, int uid, const CFunctionList<void()> &OnSelect, int key)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	CHighlightableButton *bt = new CHighlightableButton(OnSelect, 0, tooltip, HelpBox, false, defName, nullptr, x, y, key);
-	if(musicLike)
-	{
-		bt->setOffset(buttons.size()-3);
-	}
-	bt->ID = uid;
-	bt->callback += boost::bind(&CHighlightableButtonsGroup::selectionChanged,this,bt->ID);
-	bt->onlyOn = true;
-	buttons.push_back(bt);
-}
-
-CHighlightableButtonsGroup::CHighlightableButtonsGroup(const CFunctionList<void(int)> &OnChange, bool musicLikeButtons)
-: onChange(OnChange), musicLike(musicLikeButtons)
-{}
-
-CHighlightableButtonsGroup::~CHighlightableButtonsGroup()
-{
-
-}
-
-void CHighlightableButtonsGroup::select(int id, bool mode)
-{
-	assert(!buttons.empty());
-
-	CHighlightableButton *bt = buttons.front();
-	if(mode)
-	{
-		for(auto btn : buttons)
-			if (btn->ID == id)
-				bt = btn;
-	}
-	else
-	{
-		bt = buttons[id];
-	}
-	bt->select(true);
-	selectionChanged(bt->ID);
-}
-
-void CHighlightableButtonsGroup::selectionChanged(int to)
-{
-	for(auto & elem : buttons)
-		if(elem->ID!=to && elem->isHighlighted())
-			elem->select(false);
-	onChange(to);
-	if (parent)
-		parent->redraw();
-}
-
-void CHighlightableButtonsGroup::show(SDL_Surface * to)
-{
-	if (musicLike)
-	{
-		for(auto & elem : buttons)
-			if(elem->isHighlighted())
-				elem->show(to);
-	}
-	else
-		CIntObject::show(to);
-}
-
-void CHighlightableButtonsGroup::showAll(SDL_Surface * to)
-{
-	if (musicLike)
-	{
-		for(auto & elem : buttons)
-			if(elem->isHighlighted())
-				elem->showAll(to);
-	}
-	else
-		CIntObject::showAll(to);
-}
-
-void CHighlightableButtonsGroup::block( ui8 on )
-{
-	for(auto & elem : buttons)
-	{
-		elem->block(on);
-	}
-}
-
-void CSlider::sliderClicked()
-{
-	if(!(active & MOVE))
-		addUsedEvents(MOVE);
-}
-
-void CSlider::mouseMoved (const SDL_MouseMotionEvent & sEvent)
-{
-	double v = 0;
-	if(horizontal)
-	{
-		if(	std::abs(sEvent.y-(pos.y+pos.h/2)) > pos.h/2+40  ||  std::abs(sEvent.x-(pos.x+pos.w/2)) > pos.w/2  )
-			return;
-		v = sEvent.x - pos.x - 24;
-		v *= positions;
-		v /= (pos.w - 48);
-	}
-	else
-	{
-		if(std::abs(sEvent.x-(pos.x+pos.w/2)) > pos.w/2+40  ||  std::abs(sEvent.y-(pos.y+pos.h/2)) > pos.h/2  )
-			return;
-		v = sEvent.y - pos.y - 24;
-		v *= positions;
-		v /= (pos.h - 48);
-	}
-	v += 0.5;
-	if(v!=value)
-	{
-		moveTo(v);
-		redrawSlider();
-	}
-}
-
-void CSlider::redrawSlider()
-{
-	//slider->show(screenBuf);
-}
-
-void CSlider::moveLeft()
-{
-	moveTo(value-1);
-}
-
-void CSlider::moveRight()
-{
-	moveTo(value+1);
-}
-
-void CSlider::moveTo(int to)
-{
-	vstd::amax(to, 0);
-	vstd::amin(to, positions);
-
-	//same, old position?
-	if(value == to)
-		return;
-
-	value = to;
-	if(horizontal)
-	{
-		if(positions)
-		{
-			double part = static_cast<double>(to) / positions;
-			part*=(pos.w-48);
-			int newPos = part + pos.x + 16 - slider->pos.x;
-			slider->moveBy(Point(newPos, 0));
-		}
-		else
-			slider->moveTo(Point(pos.x+16, pos.y));
-	}
-	else
-	{
-		if(positions)
-		{
-			double part = static_cast<double>(to) / positions;
-			part*=(pos.h-48);
-			int newPos = part + pos.y + 16 - slider->pos.y;
-			slider->moveBy(Point(0, newPos));
-		}
-		else
-			slider->moveTo(Point(pos.x, pos.y+16));
-	}
-
-	if(moved)
-		moved(to);
-}
-
-void CSlider::clickLeft(tribool down, bool previousState)
-{
-	if(down && !slider->isBlocked())
-	{
-		double pw = 0;
-		double rw = 0;
-		if(horizontal)
-		{
-			pw = GH.current->motion.x-pos.x-25;
-			rw = pw / static_cast<double>(pos.w - 48);
-		}
-		else
-		{
-			pw = GH.current->motion.y-pos.y-24;
-			rw = pw / (pos.h-48);
-		}
-		if(pw < -8  ||  pw > (horizontal ? pos.w : pos.h) - 40)
-			return;
-		// 		if (rw>1) return;
-		// 		if (rw<0) return;
-		slider->clickLeft(true, slider->pressedL);
-		moveTo(rw * positions  +  0.5);
-		return;
-	}
-	if(active & MOVE)
-		removeUsedEvents(MOVE);
-}
-
-CSlider::~CSlider()
-{
-
-}
-
-CSlider::CSlider(int x, int y, int totalw, std::function<void(int)> Moved, int Capacity, int Amount, int Value, bool Horizontal, int style):
-    capacity(Capacity),
-    amount(Amount),
-    scrollStep(1),
-    horizontal(Horizontal),
-    moved(Moved)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	setAmount(amount);
-
-	addUsedEvents(LCLICK | KEYBOARD | WHEEL);
-	strongInterest = true;
-
-
-	left = new CAdventureMapButton();
-	right = new CAdventureMapButton();
-	slider = new CAdventureMapButton();
-
-	pos.x += x;
-	pos.y += y;
-
-	if(horizontal)
-	{
-		left->pos.y = slider->pos.y = right->pos.y = pos.y;
-		left->pos.x = pos.x;
-		right->pos.x = pos.x + totalw - 16;
-	}
-	else
-	{
-		left->pos.x = slider->pos.x = right->pos.x = pos.x;
-		left->pos.y = pos.y;
-		right->pos.y = pos.y + totalw - 16;
-	}
-
-	left->callback = boost::bind(&CSlider::moveLeft,this);
-	right->callback = boost::bind(&CSlider::moveRight,this);
-	slider->callback = boost::bind(&CSlider::sliderClicked,this);
-	left->pos.w = left->pos.h = right->pos.w = right->pos.h = slider->pos.w = slider->pos.h = 16;
-	if(horizontal)
-	{
-		pos.h = 16;
-		pos.w = totalw;
-	}
-	else
-	{
-		pos.w = 16;
-		pos.h = totalw;
-	}
-
-	if(style == 0)
-	{
-		std::string name = horizontal?"IGPCRDIV.DEF":"OVBUTN2.DEF";
-		//NOTE: this images do not have "blocked" frames. They should be implemented somehow (e.g. palette transform or something...)
-
-		//use source def to create custom animations. Format "name.def:123" will load this frame from def file
-		auto animLeft = new CAnimation();
-		animLeft->setCustom(name + ":0", 0);
-		animLeft->setCustom(name + ":1", 1);
-		left->setImage(animLeft);
-
-		auto animRight = new CAnimation();
-		animRight->setCustom(name + ":2", 0);
-		animRight->setCustom(name + ":3", 1);
-		right->setImage(animRight);
-
-		auto animSlider = new CAnimation();
-		animSlider->setCustom(name + ":4", 0);
-		slider->setImage(animSlider);
-	}
-	else
-	{
-		left->setImage(new CAnimation(horizontal ? "SCNRBLF.DEF" : "SCNRBUP.DEF"));
-		right->setImage(new CAnimation(horizontal ? "SCNRBRT.DEF" : "SCNRBDN.DEF"));
-		slider->setImage(new CAnimation("SCNRBSL.DEF"));
-	}
-	slider->actOnDown = true;
-	slider->soundDisabled = true;
-	left->soundDisabled = true;
-	right->soundDisabled = true;
-
-	value = -1;
-	moveTo(Value);
-}
-
-void CSlider::block( bool on )
-{
-	left->block(on);
-	right->block(on);
-	slider->block(on);
-}
-
-void CSlider::setAmount( int to )
-{
-	amount = to;
-	positions = to - capacity;
-	vstd::amax(positions, 0);
-}
-
-void CSlider::showAll(SDL_Surface * to)
-{
-	CSDL_Ext::fillRectBlack(to, &pos);
-	CIntObject::showAll(to);
-}
-
-void CSlider::wheelScrolled(bool down, bool in)
-{
-	moveTo(value + 3 * (down ? +scrollStep : -scrollStep));
-}
-
-void CSlider::keyPressed(const SDL_KeyboardEvent & key)
-{
-	if(key.state != SDL_PRESSED) return;
-
-	int moveDest = 0;
-	switch(key.keysym.sym)
-	{
-	case SDLK_UP:
-	case SDLK_LEFT:
-		moveDest = value - scrollStep;
-		break;
-	case SDLK_DOWN:
-	case SDLK_RIGHT:
-		moveDest = value + scrollStep;
-		break;
-	case SDLK_PAGEUP:
-		moveDest = value - capacity + scrollStep;
-		break;
-	case SDLK_PAGEDOWN:
-		moveDest = value + capacity - scrollStep;
-		break;
-	case SDLK_HOME:
-		moveDest = 0;
-		break;
-	case SDLK_END:
-		moveDest = amount - capacity;
-		break;
-	default:
-		return;
-	}
-
-	moveTo(moveDest);
-}
-
-void CSlider::moveToMax()
-{
-	moveTo(amount);
-}
-
-static void intDeleter(CIntObject* object)
-{
-	delete object;
-}
-
-CObjectList::CObjectList(CreateFunc create, DestroyFunc destroy):
-createObject(create),
-destroyObject(destroy)
-{
-	if (!destroyObject)
-		destroyObject = intDeleter;
-}
-
-void CObjectList::deleteItem(CIntObject* item)
-{
-	if (!item)
-		return;
-	removeChild(item);
-	destroyObject(item);
-}
-
-CIntObject* CObjectList::createItem(size_t index)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	CIntObject * item = createObject(index);
-	if (item == nullptr)
-		item = new CIntObject();
-
-	item->recActions = defActions;
-
-	addChild(item);
-	return item;
-}
-
-CTabbedInt::CTabbedInt(CreateFunc create, DestroyFunc destroy, Point position, size_t ActiveID):
-CObjectList(create, destroy),
-activeTab(nullptr),
-activeID(ActiveID)
-{
-	pos += position;
-	reset();
-}
-
-void CTabbedInt::setActive(size_t which)
-{
-	if (which != activeID)
-	{
-		activeID = which;
-		reset();
-	}
-}
-
-void CTabbedInt::reset()
-{
-	deleteItem(activeTab);
-	activeTab = createItem(activeID);
-	activeTab->moveTo(pos.topLeft());
-
-	if (active)
-		redraw();
-}
-
-CIntObject * CTabbedInt::getItem()
-{
-	return activeTab;
-}
-
-CListBox::CListBox(CreateFunc create, DestroyFunc destroy, Point Pos, Point ItemOffset, size_t VisibleSize,
-				   size_t TotalSize, size_t InitialPos, int Slider, Rect SliderPos):
-	CObjectList(create, destroy),
-	first(InitialPos),
-	totalSize(TotalSize),
-	itemOffset(ItemOffset),
-    slider(nullptr)
-{
-	pos += Pos;
-	items.resize(VisibleSize, nullptr);
-
-	if (Slider & 1)
-	{
-		OBJ_CONSTRUCTION_CAPTURING_ALL;
-		slider = new CSlider(SliderPos.x, SliderPos.y, SliderPos.w, boost::bind(&CListBox::moveToPos, this, _1),
-			VisibleSize, TotalSize, InitialPos, Slider & 2, Slider & 4);
-	}
-	reset();
-}
-
-// Used to move active items after changing list position
-void CListBox::updatePositions()
-{
-	Point itemPos = pos.topLeft();
-	for (auto & elem : items)
-	{
-		(elem)->moveTo(itemPos);
-		itemPos += itemOffset;
-	}
-	if (active)
-	{
-		redraw();
-		if (slider)
-			slider->moveTo(first);
-	}
-}
-
-void CListBox::reset()
-{
-	size_t current = first;
-	for (auto & elem : items)
-	{
-		deleteItem(elem);
-		elem = createItem(current++);
-	}
-	updatePositions();
-}
-
-void CListBox::resize(size_t newSize)
-{
-	totalSize = newSize;
-	if (slider)
-		slider->setAmount(totalSize);
-	reset();
-}
-
-size_t CListBox::size()
-{
-	return totalSize;
-}
-
-CIntObject * CListBox::getItem(size_t which)
-{
-	if (which < first || which > first + items.size() || which > totalSize)
-		return nullptr;
-
-	size_t i=first;
-	for (auto iter = items.begin(); iter != items.end(); iter++, i++)
-		if( i == which)
-			return *iter;
-	return nullptr;
-}
-
-size_t CListBox::getIndexOf(CIntObject *item)
-{
-	size_t i=first;
-	for (auto iter = items.begin(); iter != items.end(); iter++, i++)
-		if(*iter == item)
-			return i;
-	return size_t(-1);
-}
-
-void CListBox::scrollTo(size_t which)
-{
-	//scroll up
-	if (first > which)
-		moveToPos(which);
-	//scroll down
-	else if (first + items.size() <= which && which < totalSize)
-		moveToPos(which - items.size() + 1);
-}
-
-void CListBox::moveToPos(size_t which)
-{
-	//Calculate new position
-	size_t maxPossible;
-	if (totalSize > items.size())
-		maxPossible = totalSize - items.size();
-	else
-		maxPossible = 0;
-
-	size_t newPos = std::min(which, maxPossible);
-
-	//If move distance is 1 (most of calls from Slider) - use faster shifts instead of resetting all items
-	if (first - newPos == 1)
-		moveToPrev();
-	else if (newPos - first == 1)
-		moveToNext();
-	else if (newPos != first)
-	{
-		first = newPos;
-		reset();
-	}
-}
-
-void CListBox::moveToNext()
-{
-	//Remove front item and insert new one to end
-	if (first + items.size() < totalSize)
-	{
-		first++;
-		deleteItem(items.front());
-		items.pop_front();
-		items.push_back(createItem(first+items.size()));
-		updatePositions();
-	}
-}
-
-void CListBox::moveToPrev()
-{
-	//Remove last item and insert new one at start
-	if (first)
-	{
-		first--;
-		deleteItem(items.back());
-		items.pop_back();
-		items.push_front(createItem(first));
-		updatePositions();
-	}
-}
-
-size_t CListBox::getPos()
-{
-	return first;
-}
-
-const std::list<CIntObject *> &CListBox::getItems()
-{
-	return items;
-}
-
-void CSimpleWindow::show(SDL_Surface * to)
-{
-	if(bitmap)
-		blitAt(bitmap,pos.x,pos.y,to);
-}
-CSimpleWindow::~CSimpleWindow()
-{
-	if (bitmap)
-	{
-		SDL_FreeSurface(bitmap);
-		bitmap=nullptr;
-	}
-}
-
-void CHoverableArea::hover (bool on)
-{
-	if (on)
-		GH.statusbar->setText(hoverText);
-	else if (GH.statusbar->getText()==hoverText)
-		GH.statusbar->clear();
-}
-
-CHoverableArea::CHoverableArea()
-{
-	addUsedEvents(HOVER);
-}
-
-CHoverableArea::~CHoverableArea()
-{
-}
-
-void LRClickableAreaWText::clickLeft(tribool down, bool previousState)
-{
-	if(!down && previousState && !text.empty())
-	{
-		LOCPLINT->showInfoDialog(text);
-	}
-}
-void LRClickableAreaWText::clickRight(tribool down, bool previousState)
-{
-	if (!text.empty())
-		adventureInt->handleRightClick(text, down);
-}
-
-LRClickableAreaWText::LRClickableAreaWText()
-{
-	init();
-}
-
-LRClickableAreaWText::LRClickableAreaWText(const Rect &Pos, const std::string &HoverText /*= ""*/, const std::string &ClickText /*= ""*/)
-{
-	init();
-	pos = Pos + pos;
-	hoverText = HoverText;
-	text = ClickText;
-}
-
-LRClickableAreaWText::~LRClickableAreaWText()
-{
-}
-
-void LRClickableAreaWText::init()
-{
-	addUsedEvents(LCLICK | RCLICK | HOVER);
-}
-
-std::string CLabel::visibleText()
-{
-	return text;
-}
-
-void CLabel::showAll(SDL_Surface * to)
-{
-	CIntObject::showAll(to);
-
-	if(!visibleText().empty())
-		blitLine(to, pos, visibleText());
-
-}
-
-CLabel::CLabel(int x, int y, EFonts Font /*= FONT_SMALL*/, EAlignment Align, const SDL_Color &Color /*= Colors::WHITE*/, const std::string &Text /*= ""*/)
-:CTextContainer(Align, Font, Color), text(Text)
-{
-	type |= REDRAW_PARENT;
-	autoRedraw = true;
-	pos.x += x;
-	pos.y += y;
-	pos.w = pos.h = 0;
-	bg = nullptr;
-
-	if (alignment == TOPLEFT) // causes issues for MIDDLE
-	{
-		pos.w = graphics->fonts[font]->getStringWidth(visibleText().c_str());
-		pos.h = graphics->fonts[font]->getLineHeight();
-	}
-}
-
-Point CLabel::getBorderSize()
-{
-	return Point(0, 0);
-}
-
-std::string CLabel::getText()
-{
-	return text;
-}
-
-void CLabel::setText(const std::string &Txt)
-{
-	text = Txt;
-	if(autoRedraw)
-	{
-		if(bg || !parent)
-			redraw();
-		else
-			parent->redraw();
-	}
-}
-
-CMultiLineLabel::CMultiLineLabel(Rect position, EFonts Font, EAlignment Align, const SDL_Color &Color, const std::string &Text):
-    CLabel(position.x, position.y, Font, Align, Color, Text),
-    visibleSize(0, 0, position.w, position.h)
-{
-	pos.w = position.w;
-	pos.h = position.h;
-	splitText(Text);
-}
-
-void CMultiLineLabel::setVisibleSize(Rect visibleSize)
-{
-	this->visibleSize = visibleSize;
-	redraw();
-}
-
-void CMultiLineLabel::scrollTextBy(int distance)
-{
-	scrollTextTo(visibleSize.y + distance);
-}
-
-void CMultiLineLabel::scrollTextTo(int distance)
-{
-	Rect size = visibleSize;
-	size.y = distance;
-	setVisibleSize(size);
-}
-
-void CMultiLineLabel::setText(const std::string &Txt)
-{
-	splitText(Txt);
-	CLabel::setText(Txt);
-}
-
-void CTextContainer::blitLine(SDL_Surface *to, Rect destRect, std::string what)
-{
-	const IFont * f = graphics->fonts[font];
-	Point where = destRect.topLeft();
-
-	// input is rect in which given text should be placed
-	// calculate proper position for top-left corner of the text
-	if (alignment == TOPLEFT)
-	{
-		where.x += getBorderSize().x;
-		where.y += getBorderSize().y;
-	}
-
-	if (alignment == CENTER)
-	{
-		where.x += (int(destRect.w) - int(f->getStringWidth(what))) / 2;
-		where.y += (int(destRect.h) - int(f->getLineHeight())) / 2;
-	}
-
-	if (alignment == BOTTOMRIGHT)
-	{
-		where.x += getBorderSize().x + destRect.w - f->getStringWidth(what);
-		where.y += getBorderSize().y + destRect.h - f->getLineHeight();
-	}
-
-	size_t begin = 0;
-	std::string delimeters = "{}";
-	size_t currDelimeter = 0;
-
-	do
-	{
-		size_t end = what.find_first_of(delimeters[currDelimeter % 2], begin);
-		if (begin != end)
-		{
-			std::string toPrint = what.substr(begin, end - begin);
-
-			if (currDelimeter % 2) // Enclosed in {} text - set to yellow
-				f->renderTextLeft(to, toPrint, Colors::YELLOW, where);
-			else // Non-enclosed text, use default color
-				f->renderTextLeft(to, toPrint, color, where);
-			begin = end;
-
-			where.x += f->getStringWidth(toPrint);
-		}
-		currDelimeter++;
-	}
-	while (begin++ != std::string::npos);
-}
-
-CTextContainer::CTextContainer(EAlignment alignment, EFonts font, SDL_Color color):
-	alignment(alignment),
-	font(font),
-	color(color)
-{}
-
-void CMultiLineLabel::showAll(SDL_Surface * to)
-{
-	CIntObject::showAll(to);
-
-	const IFont * f = graphics->fonts[font];
-
-	// calculate which lines should be visible
-	int totalLines = lines.size();
-	int beginLine  = visibleSize.y;
-	int endLine    = getTextLocation().h + visibleSize.y;
-
-	if (beginLine < 0)
-		beginLine = 0;
-	else
-		beginLine /= f->getLineHeight();
-
-	if (endLine < 0)
-		endLine = 0;
-	else
-		endLine /= f->getLineHeight();
-	endLine++;
-
-	// and where they should be displayed
-	Point lineStart = getTextLocation().topLeft() - visibleSize + Point(0, beginLine * f->getLineHeight());
-	Point lineSize  = Point(getTextLocation().w, f->getLineHeight());
-
-	CSDL_Ext::CClipRectGuard guard(to, getTextLocation()); // to properly trim text that is too big to fit
-
-	for (int i = beginLine; i < std::min(totalLines, endLine); i++)
-	{
-		if (!lines[i].empty()) //non-empty line
-			blitLine(to, Rect(lineStart, lineSize), lines[i]);
-
-		lineStart.y += f->getLineHeight();
-	}
-}
-
-void CMultiLineLabel::splitText(const std::string &Txt)
-{
-	lines.clear();
-
-	const IFont * f = graphics->fonts[font];
-	int lineHeight =  f->getLineHeight();
-
-	lines = CMessage::breakText(Txt, pos.w, font);
-
-	 textSize.y = lineHeight * lines.size();
-	 textSize.x = 0;
-	for(const std::string &line : lines)
-		vstd::amax( textSize.x, f->getStringWidth(line.c_str()));
-	redraw();
-}
-
-Rect CMultiLineLabel::getTextLocation()
-{
-	// this method is needed for vertical alignment alignment of text
-	// when height of available text is smaller than height of widget
-	// in this case - we should add proper offset to display text at required position
-	if (pos.h <= textSize.y)
-		return pos;
-
-	Point textSize(pos.w, graphics->fonts[font]->getLineHeight() * lines.size());
-	Point textOffset(pos.w - textSize.x, pos.h - textSize.y);
-
-	switch(alignment)
-	{
-	case TOPLEFT:     return Rect(pos.topLeft(), textSize);
-	case CENTER:      return Rect(pos.topLeft() + textOffset / 2, textSize);
-	case BOTTOMRIGHT: return Rect(pos.topLeft() + textOffset, textSize);
-	}
-	assert(0);
-	return Rect();
-}
-
-CLabelGroup::CLabelGroup(EFonts Font, EAlignment Align, const SDL_Color &Color):
-	font(Font), align(Align), color(Color)
-{}
-
-void CLabelGroup::add(int x, int y, const std::string &text)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	new CLabel(x, y, font, align, color, text);
-}
-
-CTextBox::CTextBox(std::string Text, const Rect &rect, int SliderStyle, EFonts Font /*= FONT_SMALL*/, EAlignment Align /*= TOPLEFT*/, const SDL_Color &Color /*= Colors::WHITE*/):
-    sliderStyle(SliderStyle),
-    slider(nullptr)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	label = new CMultiLineLabel(rect, Font, Align, Color);
-
-	type |= REDRAW_PARENT;
-	pos.x += rect.x;
-	pos.y += rect.y;
-	pos.h = rect.h;
-	pos.w = rect.w;
-
-	assert(pos.w >= 40); //we need some space
-	setText(Text);
-}
-
-void CTextBox::sliderMoved(int to)
-{
-	label->scrollTextTo(to);
-}
-
-void CTextBox::resize(Point newSize)
-{
-	pos.w = newSize.x;
-	pos.h = newSize.y;
-	label->pos.w = pos.w;
-	label->pos.h = pos.h;
-	if (slider)
-		vstd::clear_pointer(slider); // will be recreated if needed later
-
-	setText(label->getText()); // force refresh
-}
-
-void CTextBox::setText(const std::string &text)
-{
-	label->setText(text);
-	if (label->textSize.y <= label->pos.h && slider)
-	{
-		// slider is no longer needed
-		vstd::clear_pointer(slider);
-		label->pos.w = pos.w;
-		label->setText(text);
-	}
-	else if (label->textSize.y > label->pos.h && !slider)
-	{
-		// create slider and update widget
-		label->pos.w = pos.w - 32;
-		label->setText(text);
-
-		OBJ_CONSTRUCTION_CAPTURING_ALL;
-		slider = new CSlider(pos.w - 32, 0, pos.h, boost::bind(&CTextBox::sliderMoved, this, _1),
-		                     label->pos.h, label->textSize.y, 0, false, sliderStyle);
-		slider->scrollStep = graphics->fonts[label->font]->getLineHeight();
-	}
-}
-
-void CGStatusBar::setText(const std::string & Text)
-{
-	if(!textLock)
-		CLabel::setText(Text);
-}
-
-void CGStatusBar::clear()
-{
-	setText("");
-}
-
-CGStatusBar::CGStatusBar(CPicture *BG, EFonts Font /*= FONT_SMALL*/, EAlignment Align /*= CENTER*/, const SDL_Color &Color /*= Colors::WHITE*/)
-: CLabel(BG->pos.x, BG->pos.y, Font, Align, Color, "")
-{
-	init();
-	bg = BG;
-	addChild(bg);
-	pos = bg->pos;
-	getBorderSize();
-    textLock = false;
-}
-
-CGStatusBar::CGStatusBar(int x, int y, std::string name/*="ADROLLVR.bmp"*/, int maxw/*=-1*/)
-: CLabel(x, y, FONT_SMALL, CENTER)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	init();
-	bg = new CPicture(name);
-	pos = bg->pos;
-	if((unsigned int)maxw < pos.w)
-	{
-		vstd::amin(pos.w, maxw);
-		bg->srcRect = new Rect(0, 0, maxw, pos.h);
-	}
-    textLock = false;
-}
-
-CGStatusBar::~CGStatusBar()
-{
-	GH.statusbar = oldStatusBar;
-}
-
-void CGStatusBar::show(SDL_Surface * to)
-{
-    showAll(to);
-}
-
-void CGStatusBar::init()
-{
-	oldStatusBar = GH.statusbar;
-	GH.statusbar = this;
-}
-
-Point CGStatusBar::getBorderSize()
-{
-	//Width of borders where text should not be printed
-	static const Point borderSize(5,1);
-
-	switch(alignment)
-	{
-	case TOPLEFT:     return Point(borderSize.x, borderSize.y);
-	case CENTER:      return Point(pos.w/2, pos.h/2);
-	case BOTTOMRIGHT: return Point(pos.w - borderSize.x, pos.h - borderSize.y);
-	}
-	assert(0);
-	return Point();
-}
-
-void CGStatusBar::lock(bool shouldLock)
-{
-    textLock = shouldLock;
-}
-
-CTextInput::CTextInput(const Rect &Pos, EFonts font, const CFunctionList<void(const std::string &)> &CB):
-    CLabel(Pos.x, Pos.y, font, CENTER),
-    cb(CB)
-{
-	type |= REDRAW_PARENT;
-	focus = false;
-	pos.h = Pos.h;
-	pos.w = Pos.w;
-	captureAllKeys = true;
-	bg = nullptr;
-	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
-	giveFocus();
-}
-
-CTextInput::CTextInput( const Rect &Pos, const Point &bgOffset, const std::string &bgName, const CFunctionList<void(const std::string &)> &CB )
-:cb(CB)
-{
-	focus = false;
-	pos += Pos;
-	captureAllKeys = true;
-	OBJ_CONSTRUCTION;
-	bg = new CPicture(bgName, bgOffset.x, bgOffset.y);
-	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
-	giveFocus();
-}
-
-CTextInput::CTextInput(const Rect &Pos, SDL_Surface *srf)
-{
-	focus = false;
-	pos += Pos;
-	captureAllKeys = true;
-	OBJ_CONSTRUCTION;
-	bg = new CPicture(Pos, 0, true);
-	Rect hlp = Pos;
-	if(srf)
-		CSDL_Ext::blitSurface(srf, &hlp, *bg, nullptr);
-	else
-		SDL_FillRect(*bg, nullptr, 0);
-	pos.w = bg->pos.w;
-	pos.h = bg->pos.h;
-	bg->pos = pos;
-	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
-	giveFocus();
-}
-
-void CTextInput::focusGot()
-{
-	CSDL_Ext::startTextInput(&pos);	
-}
-
-void CTextInput::focusLost()
-{
-	CSDL_Ext::stopTextInput();
-}
-
-
-std::string CTextInput::visibleText()
-{
-	return focus ? text + newText + "_" : text;
-}
-
-void CTextInput::clickLeft( tribool down, bool previousState )
-{
-	if(down && !focus)
-		giveFocus();
-}
-
-void CTextInput::keyPressed( const SDL_KeyboardEvent & key )
-{
-
-	if(!focus || key.state != SDL_PRESSED)
-		return;
-
-	if(key.keysym.sym == SDLK_TAB)
-	{
-		moveFocus();
-		GH.breakEventHandling();
-		return;
-	}
-
-	bool redrawNeeded = false;
-	#ifdef VCMI_SDL1
-	std::string oldText = text;
-	#endif // 0	
-	switch(key.keysym.sym)
-	{
-	case SDLK_DELETE: // have index > ' ' so it won't be filtered out by default section
-		return;
-	case SDLK_BACKSPACE:
-		if(!newText.empty())
-		{
-			Unicode::trimRight(newText);
-			redrawNeeded = true;
-		}
-		else if(!text.empty())
-		{
-			Unicode::trimRight(text);
-			redrawNeeded = true;
-		}			
-		break;
-	default:
-		#ifdef VCMI_SDL1
-		if (key.keysym.unicode < ' ')
-			return;
-		else
-		{
-			text += key.keysym.unicode; //TODO 16-/>8
-			redrawNeeded = true;
-		}			
-		#endif // 0
-		break;
-	}
-	#ifdef VCMI_SDL1
-	filters(text, oldText);
-	#endif // 0
-	if (redrawNeeded)
-	{
-		redraw();
-		cb(text);
-	}	
-}
-
-void CTextInput::setText( const std::string &nText, bool callCb )
-{
-	CLabel::setText(nText);
-	if(callCb)
-		cb(text);
-}
-
-bool CTextInput::captureThisEvent(const SDL_KeyboardEvent & key)
-{
-	if(key.keysym.sym == SDLK_RETURN || key.keysym.sym == SDLK_KP_ENTER)
-		return false;
-	
-	#ifdef VCMI_SDL1
-	//this should allow all non-printable keys to go through (for example arrows)
-	if (key.keysym.unicode < ' ')
-		return false;
-
-	return true;
-	#else
-	return false;
-	#endif
-}
-
-#ifndef VCMI_SDL1
-void CTextInput::textInputed(const SDL_TextInputEvent & event)
-{
-	if(!focus)
-		return;
-	std::string oldText = text;
-	
-	text += event.text;	
-	
-	filters(text,oldText);
-	if (text != oldText)
-	{
-		redraw();
-		cb(text);
-	}	
-	newText = "";
-}
-
-void CTextInput::textEdited(const SDL_TextEditingEvent & event)
-{
-	if(!focus)
-		return;
-		
-	newText = event.text;
-	redraw();
-	cb(text+newText);	
-}
-
-#endif
-
-
-void CTextInput::filenameFilter(std::string & text, const std::string &)
-{
-	static const std::string forbiddenChars = "<>:\"/\\|?*\r\n"; //if we are entering a filename, some special characters won't be allowed
-	size_t pos;
-	while ((pos = text.find_first_of(forbiddenChars)) != std::string::npos)
-		text.erase(pos, 1);
-}
-
-void CTextInput::numberFilter(std::string & text, const std::string & oldText, int minValue, int maxValue)
-{
-	assert(minValue < maxValue);
-
-	if (text.empty())
-		text = "0";
-
-	size_t pos = 0;
-	if (text[0] == '-') //allow '-' sign as first symbol only
-		pos++;
-
-	while (pos < text.size())
-	{
-		if (text[pos] < '0' || text[pos] > '9')
-		{
-			text = oldText;
-			return; //new text is not number.
-		}
-		pos++;
-	}
-	try
-	{
-		int value = boost::lexical_cast<int>(text);
-		if (value < minValue)
-			text = boost::lexical_cast<std::string>(minValue);
-		else if (value > maxValue)
-			text = boost::lexical_cast<std::string>(maxValue);
-	}
-	catch(boost::bad_lexical_cast &)
-	{
-		//Should never happen. Unless I missed some cases
-        logGlobal->warnStream() << "Warning: failed to convert "<< text << " to number!";
-		text = oldText;
-	}
-}
-
-CFocusable::CFocusable()
-{
-	focusables.push_back(this);
-}
-
-CFocusable::~CFocusable()
-{
-	if(inputWithFocus == this)
-	{
-		focusLost();
-		inputWithFocus = nullptr;
-	}	
-
-	focusables -= this;
-}
-void CFocusable::giveFocus()
-{
-	if(inputWithFocus)
-	{
-		inputWithFocus->focus = false;
-		inputWithFocus->focusLost();
-		inputWithFocus->redraw();
-	}
-
-	focus = true;
-	inputWithFocus = this;
-	focusGot();
-	redraw();	
-}
-
-void CFocusable::moveFocus()
-{
-	auto i = vstd::find(focusables, this),
-		ourIt = i;
-	for(i++; i != ourIt; i++)
-	{
-		if(i == focusables.end())
-			i = focusables.begin();
-
-		if((*i)->active)
-		{
-			(*i)->giveFocus();
-			break;;
-		}
-	}
-}
-
-CWindowObject::CWindowObject(int options_, std::string imageName, Point centerAt):
-    CIntObject(getUsedEvents(options_), Point()),
-    shadow(nullptr),
-    options(options_),
-    background(createBg(imageName, options & PLAYER_COLORED))
-{
-	assert(parent == nullptr); //Safe to remove, but windows should not have parent
-
-	if (options & RCLICK_POPUP)
-		CCS->curh->hide();
-
-	if (background)
-		pos = background->center(centerAt);
-	else
-		center(centerAt);
-
-	if (!(options & SHADOW_DISABLED))
-		setShadow(true);
-}
-
-CWindowObject::CWindowObject(int options_, std::string imageName):
-    CIntObject(getUsedEvents(options_), Point()),
-    shadow(nullptr),
-    options(options_),
-    background(createBg(imageName, options & PLAYER_COLORED))
-{
-	assert(parent == nullptr); //Safe to remove, but windows should not have parent
-
-	if (options & RCLICK_POPUP)
-		CCS->curh->hide();
-
-	if (background)
-		pos = background->center();
-	else
-		center(Point(screen->w/2, screen->h/2));
-
-	if (!(options & SHADOW_DISABLED))
-		setShadow(true);
-}
-
-CWindowObject::~CWindowObject()
-{
-	setShadow(false);
-}
-
-CPicture * CWindowObject::createBg(std::string imageName, bool playerColored)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	if (imageName.empty())
-		return nullptr;
-
-	auto  image = new CPicture(imageName);
-	if (playerColored)
-		image->colorize(LOCPLINT->playerID);
-	return image;
-}
-
-void CWindowObject::setBackground(std::string filename)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	delete background;
-	background = createBg(filename, options & PLAYER_COLORED);
-
-	if (background)
-		pos = background->center(Point(pos.w/2 + pos.x, pos.h/2 + pos.y));
-
-	updateShadow();
-}
-
-int CWindowObject::getUsedEvents(int options)
-{
-	if (options & RCLICK_POPUP)
-		return RCLICK;
-	return 0;
-}
-
-void CWindowObject::updateShadow()
-{
-	setShadow(false);
-	if (!(options & SHADOW_DISABLED))
-		setShadow(true);
-}
-
-void CWindowObject::setShadow(bool on)
-{
-	//size of shadow
-	static const int size = 8;
-
-	if (on == bool(shadow))
-		return;
-
-	vstd::clear_pointer(shadow);
-
-	//object too small to cast shadow
-	if (pos.h <= size || pos.w <= size)
-		return;
-
-	if (on)
-	{
-
-		//helper to set last row
-		auto blitAlphaRow = [](SDL_Surface *surf, size_t row)
-		{
-			Uint8 * ptr = (Uint8*)surf->pixels + surf->pitch * (row);
-
-			for (size_t i=0; i< surf->w; i++)
-			{
-				Channels::px<4>::a.set(ptr, 128);
-				ptr+=4;
-			}
-		};
-
-		// helper to set last column
-		auto blitAlphaCol = [](SDL_Surface *surf, size_t col)
-		{
-			Uint8 * ptr = (Uint8*)surf->pixels + 4 * (col);
-
-			for (size_t i=0; i< surf->h; i++)
-			{
-				Channels::px<4>::a.set(ptr, 128);
-				ptr+= surf->pitch;
-			}
-		};
-
-		static SDL_Surface * shadowCornerTempl = nullptr;
-		static SDL_Surface * shadowBottomTempl = nullptr;
-		static SDL_Surface * shadowRightTempl = nullptr;
-
-		//one-time initialization
-		if (!shadowCornerTempl)
-		{
-			//create "template" surfaces
-			shadowCornerTempl = CSDL_Ext::createSurfaceWithBpp<4>(size, size);
-			shadowBottomTempl = CSDL_Ext::createSurfaceWithBpp<4>(1, size);
-			shadowRightTempl  = CSDL_Ext::createSurfaceWithBpp<4>(size, 1);
-
-			Uint32 shadowColor = SDL_MapRGBA(shadowCornerTempl->format, 0, 0, 0, 192);
-
-			//fill with shadow body color
-			SDL_FillRect(shadowCornerTempl, nullptr, shadowColor);
-			SDL_FillRect(shadowBottomTempl, nullptr, shadowColor);
-			SDL_FillRect(shadowRightTempl,  nullptr, shadowColor);
-
-			//fill last row and column with more transparent color
-			blitAlphaCol(shadowRightTempl , size-1);
-			blitAlphaCol(shadowCornerTempl, size-1);
-			blitAlphaRow(shadowBottomTempl, size-1);
-			blitAlphaRow(shadowCornerTempl, size-1);
-		}
-
-		OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-		//FIXME: do something with this points
-		Point shadowStart;
-		if (options & BORDERED)
-			shadowStart = Point(size - 14, size - 14);
-		else
-			shadowStart = Point(size, size);
-
-		Point shadowPos;
-		if (options & BORDERED)
-			shadowPos = Point(pos.w + 14, pos.h + 14);
-		else
-			shadowPos = Point(pos.w, pos.h);
-
-		Point fullsize;
-		if (options & BORDERED)
-			fullsize = Point(pos.w + 28, pos.h + 29);
-		else
-			fullsize = Point(pos.w, pos.h);
-
-		//create base 8x8 piece of shadow
-		SDL_Surface * shadowCorner = CSDL_Ext::copySurface(shadowCornerTempl);
-		SDL_Surface * shadowBottom = CSDL_Ext::scaleSurfaceFast(shadowBottomTempl, fullsize.x - size, size);
-		SDL_Surface * shadowRight  = CSDL_Ext::scaleSurfaceFast(shadowRightTempl,  size, fullsize.y - size);
-
-		blitAlphaCol(shadowBottom, 0);
-		blitAlphaRow(shadowRight, 0);
-
-		//generate "shadow" object with these 3 pieces in it
-		shadow = new CIntObject;
-		shadow->addChild(new CPicture(shadowCorner, shadowPos.x, shadowPos.y));
-		shadow->addChild(new CPicture(shadowRight,  shadowPos.x, shadowStart.y));
-		shadow->addChild(new CPicture(shadowBottom, shadowStart.x, shadowPos.y));
-	}
-}
-
-void CWindowObject::showAll(SDL_Surface *to)
-{
-	CIntObject::showAll(to);
-	if ((options & BORDERED) && (pos.h != to->h || pos.w != to->w))
-		CMessage::drawBorder(LOCPLINT ? LOCPLINT->playerID : PlayerColor(1), to, pos.w+28, pos.h+29, pos.x-14, pos.y-15);
-}
-
-void CWindowObject::close()
-{
-	GH.popIntTotally(this);
-}
-
-void CWindowObject::clickRight(tribool down, bool previousState)
-{
-	close();
-	CCS->curh->show();
-}

+ 0 - 560
client/widgets/CIntObjectClasses.h

@@ -1,560 +0,0 @@
-#pragma once
-
-#include "../gui/CIntObject.h"
-#include "../gui/SDL_Extensions.h"
-
-#include "../../lib/FunctionList.h"
-
-struct SDL_Surface;
-struct Rect;
-class CAnimImage;
-class CLabel;
-class CAnimation;
-class CDefHandler;
-
-/*
- * CPicture.h, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
-
-// Window GUI class
-class CSimpleWindow : public CIntObject
-{
-public:
-	SDL_Surface * bitmap; //background
-	virtual void show(SDL_Surface * to);
-	CSimpleWindow():bitmap(nullptr){}; //c-tor
-	virtual ~CSimpleWindow(); //d-tor
-};
-
-// Image class
-class CPicture : public CIntObject
-{
-	void setSurface(SDL_Surface *to);
-public: 
-	SDL_Surface * bg;
-	Rect * srcRect; //if nullptr then whole surface will be used
-	bool freeSurf; //whether surface will be freed upon CPicture destruction
-	bool needRefresh;//Surface needs to be displayed each frame
-
-	operator SDL_Surface*()
-	{
-		return bg;
-	}
-
-	CPicture(const Rect & r, const SDL_Color & color, bool screenFormat = false); //rect filled with given color
-	CPicture(const Rect & r, ui32 color, bool screenFormat = false); //rect filled with given color
-	CPicture(SDL_Surface * BG, int x = 0, int y=0, bool Free = true); //wrap existing SDL_Surface
-	CPicture(const std::string &bmpname, int x=0, int y=0);
-	CPicture(SDL_Surface *BG, const Rect &SrcRext, int x = 0, int y = 0, bool free = false); //wrap subrect of given surface
-	~CPicture();
-	void init();
-
-	//set alpha value for whole surface. Note: may be messed up if surface is shared
-	// 0=transparent, 255=opaque
-	void setAlpha(int value);
-
-	void scaleTo(Point size);
-	void createSimpleRect(const Rect &r, bool screenFormat, ui32 color);
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
-	void convertToScreenBPP();
-	void colorizeAndConvert(PlayerColor player);
-	void colorize(PlayerColor player);
-};
-
-/// area filled with specific texture
-class CFilledTexture : CIntObject
-{
-	SDL_Surface * texture;
-
-public:
-	CFilledTexture(std::string imageName, Rect position);
-	~CFilledTexture();
-	void showAll(SDL_Surface *to);
-};
-
-namespace config{struct ButtonInfo;}
-
-/// Base class for buttons.
-class CButtonBase : public CKeyShortcut
-{
-public:
-	enum ButtonState
-	{
-		NORMAL=0,
-		PRESSED=1,
-		BLOCKED=2,
-		HIGHLIGHTED=3
-	};
-private:
-	int bitmapOffset; // base offset of visible bitmap from animation
-	ButtonState state;//current state of button from enum
-
-public:
-	bool swappedImages,//fix for some buttons: normal and pressed image are swapped
-		keepFrame; // don't change visual representation
-
-	void addOverlay(CIntObject * newOverlay);
-	void addTextOverlay(const std::string &Text, EFonts font, SDL_Color color = Colors::WHITE);
-
-	void update();//to refresh button after image or text change
-
-	void setOffset(int newOffset);
-	void setState(ButtonState newState);
-	ButtonState getState();
-
-	//just to make code clearer
-	void block(bool on);
-	bool isBlocked();
-	bool isHighlighted();
-
-	CAnimImage * image; //image for this button
-	CIntObject * overlay;//object-overlay
-
-	CButtonBase(); //c-tor
-	virtual ~CButtonBase(); //d-tor
-};
-
-/// Typical Heroes 3 button which can be inactive or active and can 
-/// hold further information if you right-click it
-class CAdventureMapButton : public CButtonBase
-{
-	std::vector<std::string> imageNames;//store list of images that can be used by this button
-	size_t currentImage;
-
-	void onButtonClicked(); // calls callback
-public:
-	std::map<int, std::string> hoverTexts; //text for statusbar
-	std::string helpBox; //for right-click help
-	CFunctionList<void()> callback;
-	bool actOnDown,//runs when mouse is pressed down over it, not when up
-		hoverable,//if true, button will be highlighted when hovered
-		borderEnabled,
-		soundDisabled;
-	SDL_Color borderColor;
-
-	void clickRight(tribool down, bool previousState);
-	virtual void clickLeft(tribool down, bool previousState);
-	void hover (bool on);
-
-	CAdventureMapButton(); //c-tor
-	CAdventureMapButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &Callback, int x, int y, const std::string &defName, int key=0, std::vector<std::string> * add = nullptr, bool playerColoredButton = false );//c-tor
-	CAdventureMapButton( const std::pair<std::string, std::string> &help, const CFunctionList<void()> &Callback, int x, int y, const std::string &defName, int key=0, std::vector<std::string> * add = nullptr, bool playerColoredButton = false );//c-tor
-	CAdventureMapButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &Callback, config::ButtonInfo *info, int key=0);//c-tor
-
-	void init(const CFunctionList<void()> &Callback, const std::map<int,std::string> &Name, const std::string &HelpBox, bool playerColoredButton, const std::string &defName, std::vector<std::string> * add, int x, int y, int key );
-
-	void setIndex(size_t index, bool playerColoredButton=false);
-	void setImage(CAnimation* anim, bool playerColoredButton=false, int animFlags=0);
-	void setPlayerColor(PlayerColor player);
-	void showAll(SDL_Surface * to);
-};
-
-/// A button which can be selected/deselected
-class CHighlightableButton 
-	: public CAdventureMapButton
-{
-public:
-	CHighlightableButton(const CFunctionList<void()> &onSelect, const CFunctionList<void()> &onDeselect, const std::map<int,std::string> &Name, const std::string &HelpBox, bool playerColoredButton, const std::string &defName, std::vector<std::string> * add, int x, int y, int key=0);
-	CHighlightableButton(const std::pair<std::string, std::string> &help, const CFunctionList<void()> &onSelect, int x, int y, const std::string &defName, int myid, int key=0, std::vector<std::string> * add = nullptr, bool playerColoredButton = false );//c-tor
-	CHighlightableButton(const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &onSelect, int x, int y, const std::string &defName, int myid, int key=0, std::vector<std::string> * add = nullptr, bool playerColoredButton = false );//c-tor
-	bool onlyOn;//button can not be de-selected
-	bool selected;//state of highlightable button
-	int ID; //for identification
-	CFunctionList<void()> callback2; //when de-selecting
-	void select(bool on);
-	void clickLeft(tribool down, bool previousState);
-};
-
-/// A group of buttons where one button can be selected
-class CHighlightableButtonsGroup : public CIntObject
-{
-public:
-	CFunctionList<void(int)> onChange; //called when changing selected button with new button's id
-	std::vector<CHighlightableButton*> buttons;
-	bool musicLike; //determines the behaviour of this group
-
-	//void addButton(const std::map<int,std::string> &tooltip, const std::string &HelpBox, const std::string &defName, int x, int y, int uid);
-	void addButton(CHighlightableButton* bt);//add existing button, it'll be deleted by CHighlightableButtonsGroup destructor
-	void addButton(const std::map<int,std::string> &tooltip, const std::string &HelpBox, const std::string &defName, int x, int y, int uid, const CFunctionList<void()> &OnSelect=0, int key=0); //creates new button
-	CHighlightableButtonsGroup(const CFunctionList<void(int)> & OnChange, bool musicLikeButtons = false);
-	~CHighlightableButtonsGroup();
-	void select(int id, bool mode); //mode==0: id is serial; mode==1: id is unique button id
-	void selectionChanged(int to);
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
-	void block(ui8 on);
-};
-
-/// A typical slider which can be orientated horizontally/vertically.
-class CSlider : public CIntObject
-{
-public:
-	CAdventureMapButton *left, *right, *slider; //if vertical then left=up
-	int capacity;//how many elements can be active at same time (e.g. hero list = 5)
-	int amount; //total amount of elements (e.g. hero list = 0-8)
-	int positions; //number of highest position (0 if there is only one)
-	int value; //first active element
-	int scrollStep; // how many elements will be scrolled via one click, default = 1
-	bool horizontal;
-	bool wheelScrolling;
-	bool keyScrolling;
-
-	std::function<void(int)> moved;
-
-	void redrawSlider(); 
-	void sliderClicked();
-	void moveLeft();
-	void moveRight();
-	void moveTo(int to);
-	void block(bool on);
-	void setAmount(int to);
-
-	void keyPressed(const SDL_KeyboardEvent & key);
-	void wheelScrolled(bool down, bool in);
-	void clickLeft(tribool down, bool previousState);
-	void mouseMoved (const SDL_MouseMotionEvent & sEvent);
-	void showAll(SDL_Surface * to);	
-
-	CSlider(int x, int y, int totalw, std::function<void(int)> Moved, int Capacity, int Amount, 
-		int Value=0, bool Horizontal=true, int style = 0); //style 0 - brown, 1 - blue
-	~CSlider();
-	void moveToMax();
-};
-
-/// Used as base for Tabs and List classes
-class CObjectList : public CIntObject
-{
-public:
-	typedef std::function<CIntObject* (size_t)> CreateFunc;
-	typedef std::function<void(CIntObject *)> DestroyFunc;
-
-private:
-	CreateFunc createObject;
-	DestroyFunc destroyObject;
-
-protected:
-	//Internal methods for safe creation of items (Children capturing and activation/deactivation if needed)
-	void deleteItem(CIntObject* item);
-	CIntObject* createItem(size_t index);
-
-	CObjectList(CreateFunc create, DestroyFunc destroy = DestroyFunc());//Protected constructor
-};
-
-/// Window element with multiple tabs
-class CTabbedInt : public CObjectList
-{
-private:
-	CIntObject * activeTab;
-	size_t activeID;
-
-public:
-	//CreateFunc, DestroyFunc - see CObjectList
-	//Pos - position of object, all tabs will be moved to this position
-	//ActiveID - ID of initially active tab
-	CTabbedInt(CreateFunc create, DestroyFunc destroy = DestroyFunc(), Point position=Point(), size_t ActiveID=0);
-
-	void setActive(size_t which);
-	//recreate active tab
-	void reset();
-
-	//return currently active item
-	CIntObject * getItem();
-};
-
-/// List of IntObjects with optional slider
-class CListBox : public CObjectList
-{
-private:
-	std::list< CIntObject* > items;
-	size_t first;
-	size_t totalSize;
-
-	Point itemOffset;
-	CSlider * slider;
-
-	void updatePositions();
-public:
-	//CreateFunc, DestroyFunc - see CObjectList
-	//Pos - position of first item
-	//ItemOffset - distance between items in the list
-	//VisibleSize - maximal number of displayable at once items
-	//TotalSize
-	//Slider - slider style, bit field: 1 = present(disabled), 2=horisontal(vertical), 4=blue(brown)
-	//SliderPos - position of slider, if present
-	CListBox(CreateFunc create, DestroyFunc destroy, Point Pos, Point ItemOffset, size_t VisibleSize,
-		size_t TotalSize, size_t InitialPos=0, int Slider=0, Rect SliderPos=Rect() );
-
-	//recreate all visible items
-	void reset();
-
-	//change or get total amount of items in the list
-	void resize(size_t newSize);
-	size_t size();
-
-	//return item with index which or null if not present
-	CIntObject * getItem(size_t which);
-
-	//return currently active items
-	const std::list< CIntObject * > & getItems();
-
-	//get index of this item. -1 if not found
-	size_t getIndexOf(CIntObject * item);
-
-	//scroll list to make item which visible
-	void scrollTo(size_t which);
-
-	//scroll list to specified position
-	void moveToPos(size_t which);
-	void moveToNext();
-	void moveToPrev();
-
-	size_t getPos();
-};
-
-/// Small helper class to manage group of similar labels
-class CLabelGroup : public CIntObject
-{
-	std::list<CLabel*> labels;
-	EFonts font;
-	EAlignment align;
-	SDL_Color color;
-public:
-	CLabelGroup(EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE);
-	void add(int x=0, int y=0, const std::string &text =  "");
-};
-
-/// Base class for all text-related widgets.
-/// Controls text blitting-related options
-class CTextContainer : public virtual CIntObject
-{
-protected:
-	/// returns size of border, for left- or right-aligned text
-	virtual Point getBorderSize() = 0;
-	/// do actual blitting of line. Text "what" will be placed at "where" and aligned according to alignment
-	void blitLine(SDL_Surface * to, Rect where, std::string what);
-
-	CTextContainer(EAlignment alignment, EFonts font, SDL_Color color);
-
-public:
-	EAlignment alignment;
-	EFonts font;
-	SDL_Color color; // default font color. Can be overridden by placing "{}" into the string
-};
-
-/// Label which shows text
-class CLabel : public CTextContainer
-{
-protected:
-	Point getBorderSize() override;
-	virtual std::string visibleText();
-
-	CPicture *bg;
-public:
-
-	std::string text;
-	bool autoRedraw;  //whether control will redraw itself on setTxt
-
-	std::string getText();
-	virtual void setText(const std::string &Txt);
-
-	CLabel(int x=0, int y=0, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT,
-	       const SDL_Color &Color = Colors::WHITE, const std::string &Text =  "");
-	void showAll(SDL_Surface * to); //shows statusbar (with current text)
-};
-
-/// Multi-line label that can display multiple lines of text
-/// If text is too big to fit into requested area remaining part will not be visible
-class CMultiLineLabel : public CLabel
-{
-	// text to blit, split into lines that are no longer than widget width
-	std::vector<std::string> lines;
-
-	// area of text that actually will be printed, default is widget size
-	Rect visibleSize;
-
-	void splitText(const std::string &Txt);
-	Rect getTextLocation();
-public:
-	// total size of text, x = longest line of text, y = total height of lines
-	Point textSize;
-
-	CMultiLineLabel(Rect position, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE, const std::string &Text =  "");
-
-	void setText(const std::string &Txt);
-	void showAll(SDL_Surface * to);
-
-	void setVisibleSize(Rect visibleSize);
-	// scrolls text visible in widget. Positive value will move text up
-	void scrollTextTo(int distance);
-	void scrollTextBy(int distance);
-};
-
-/// a multi-line label that tries to fit text with given available width and height;
-/// if not possible, it creates a slider for scrolling text
-class CTextBox : public CIntObject
-{
-	int sliderStyle;
-public:
-	CMultiLineLabel * label;
-	CSlider *slider;
-
-	CTextBox(std::string Text, const Rect &rect, int SliderStyle, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE);
-
-	void resize(Point newSize);
-	void setText(const std::string &Txt);
-	void sliderMoved(int to);
-};
-
-/// Status bar which is shown at the bottom of the in-game screens
-class CGStatusBar : public CLabel
-{
-	bool textLock; //Used for blocking changes to the text
-	void init();
-
-	CGStatusBar *oldStatusBar;
-protected:
-	Point getBorderSize() override;
-
-public:
-
-	void clear();//clears statusbar and refreshes
-	void setText(const std::string & Text) override; //prints text and refreshes statusbar
-
-	void show(SDL_Surface * to); //shows statusbar (with current text)
-
-	CGStatusBar(CPicture *BG, EFonts Font = FONT_SMALL, EAlignment Align = CENTER, const SDL_Color &Color = Colors::WHITE); //given CPicture will be captured by created sbar and it's pos will be used as pos for sbar
-	CGStatusBar(int x, int y, std::string name, int maxw=-1);
-	~CGStatusBar();
-
-	void lock(bool shouldLock); //If true, current text cannot be changed until lock(false) is called
-};
-
-/// UIElement which can get input focus
-class CFocusable : public virtual CIntObject
-{
-protected:
-	virtual void focusGot(){};
-	virtual void focusLost(){};
-public:
-	bool focus; //only one focusable control can have focus at one moment
-
-	void giveFocus(); //captures focus
-	void moveFocus(); //moves focus to next active control (may be used for tab switching)
-
-	static std::list<CFocusable*> focusables; //all existing objs
-	static CFocusable *inputWithFocus; //who has focus now
-	CFocusable();
-	~CFocusable();
-};
-
-/// Text input box where players can enter text
-class CTextInput : public CLabel, public CFocusable
-{
-	std::string newText;
-protected:
-	std::string visibleText() override;
-
-	void focusGot() override;
-	void focusLost() override;
-public:
-	CFunctionList<void(const std::string &)> cb;
-	CFunctionList<void(std::string &, const std::string &)> filters;
-	void setText(const std::string &nText, bool callCb = false);
-
-	CTextInput(const Rect &Pos, EFonts font, const CFunctionList<void(const std::string &)> &CB);
-	CTextInput(const Rect &Pos, const Point &bgOffset, const std::string &bgName, const CFunctionList<void(const std::string &)> &CB);
-	CTextInput(const Rect &Pos, SDL_Surface *srf = nullptr);
-
-	void clickLeft(tribool down, bool previousState) override;
-	void keyPressed(const SDL_KeyboardEvent & key) override;
-	bool captureThisEvent(const SDL_KeyboardEvent & key) override;
-	
-#ifndef VCMI_SDL1
-	void textInputed(const SDL_TextInputEvent & event) override;
-	void textEdited(const SDL_TextEditingEvent & event) override;
-	
-	
-#endif // VCMI_SDL1	
-
-	//Filter that will block all characters not allowed in filenames
-	static void filenameFilter(std::string &text, const std::string & oldText);
-	//Filter that will allow only input of numbers in range min-max (min-max are allowed)
-	//min-max should be set via something like boost::bind
-	static void numberFilter(std::string &text, const std::string & oldText, int minValue, int maxValue);
-};
-
-/// Shows a text by moving the mouse cursor over the object
-class CHoverableArea: public virtual CIntObject
-{
-public:
-	std::string hoverText;
-
-	virtual void hover (bool on);
-
-	CHoverableArea();
-	virtual ~CHoverableArea();
-};
-
-/// Can interact on left and right mouse clicks, plus it shows a text when by hovering over it
-class LRClickableAreaWText: public CHoverableArea
-{
-public:
-	std::string text;
-
-	LRClickableAreaWText();
-	LRClickableAreaWText(const Rect &Pos, const std::string &HoverText = "", const std::string &ClickText = "");
-	virtual ~LRClickableAreaWText();
-	void init();
-
-	virtual void clickLeft(tribool down, bool previousState);
-	virtual void clickRight(tribool down, bool previousState);
-};
-
-/// Basic class for windows
-class CWindowObject : public CIntObject
-{
-	CPicture * createBg(std::string imageName, bool playerColored);
-	int getUsedEvents(int options);
-
-	CIntObject *shadow;
-	void setShadow(bool on);
-
-	int options;
-
-protected:
-	CPicture * background;
-
-	//Simple function with call to GH.popInt
-	void close();
-	//Used only if RCLICK_POPUP was set
-	void clickRight(tribool down, bool previousState);
-	//To display border
-	void showAll(SDL_Surface *to);
-	//change or set background image
-	void setBackground(std::string filename);
-	void updateShadow();
-public:
-	enum EOptions
-	{
-		PLAYER_COLORED=1, //background will be player-colored
-		RCLICK_POPUP=2, // window will behave as right-click popup
-		BORDERED=4, // window will have border if current resolution is bigger than size of window
-		SHADOW_DISABLED=8 //this window won't display any shadow
-	};
-
-	/*
-	 * options - EOpions enum
-	 * imageName - name for background image, can be empty
-	 * centerAt - position of window center. Default - center of the screen
-	*/
-	CWindowObject(int options, std::string imageName, Point centerAt);
-	CWindowObject(int options, std::string imageName = "");
-	~CWindowObject();
-};

+ 533 - 0
client/widgets/Images.cpp

@@ -0,0 +1,533 @@
+#include "StdInc.h"
+#include "Images.h"
+
+#include "MiscWidgets.h"
+
+#include "../gui/CAnimation.h"
+#include "../gui/SDL_Pixels.h"
+#include "../gui/SDL_Extensions.h"
+#include "../gui/CGuiHandler.h"
+#include "../gui/CCursorHandler.h"
+
+#include "../battle/CBattleInterface.h"
+#include "../battle/CBattleInterfaceClasses.h"
+
+#include "../CBitmapHandler.h"
+#include "../Graphics.h"
+#include "../CGameInfo.h"
+#include "../CPlayerInterface.h"
+#include "../CMessage.h"
+#include "../CMusicHandler.h"
+#include "../windows/CAdvmapInterface.h"
+
+#include "../../CCallback.h"
+
+#include "../../lib/CConfigHandler.h"
+#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
+#include "../../lib/CRandomGenerator.h"
+
+/*
+ * Images.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+CPicture::CPicture( SDL_Surface *BG, int x, int y, bool Free )
+{
+	init();
+	bg = BG;
+	freeSurf = Free;
+	pos.x += x;
+	pos.y += y;
+	pos.w = BG->w;
+	pos.h = BG->h;
+}
+
+CPicture::CPicture( const std::string &bmpname, int x, int y )
+{
+	init();
+	bg = BitmapHandler::loadBitmap(bmpname);
+	freeSurf = true;;
+	pos.x += x;
+	pos.y += y;
+	if(bg)
+	{
+		pos.w = bg->w;
+		pos.h = bg->h;
+	}
+	else
+	{
+		pos.w = pos.h = 0;
+	}
+}
+
+CPicture::CPicture(const Rect &r, const SDL_Color &color, bool screenFormat /*= false*/)
+{
+	init();
+	createSimpleRect(r, screenFormat, SDL_MapRGB(bg->format, color.r, color.g,color.b));
+}
+
+CPicture::CPicture(const Rect &r, ui32 color, bool screenFormat /*= false*/)
+{
+	init();
+	createSimpleRect(r, screenFormat, color);
+}
+
+CPicture::CPicture(SDL_Surface *BG, const Rect &SrcRect, int x /*= 0*/, int y /*= 0*/, bool free /*= false*/)
+{
+	needRefresh = false;
+	srcRect = new Rect(SrcRect);
+	pos.x += x;
+	pos.y += y;
+	pos.w = srcRect->w;
+	pos.h = srcRect->h;
+	bg = BG;
+	freeSurf = free;
+}
+
+void CPicture::setSurface(SDL_Surface *to)
+{
+	bg = to;
+	if (srcRect)
+	{
+		pos.w = srcRect->w;
+		pos.h = srcRect->h;
+	}
+	else
+	{
+		pos.w = bg->w;
+		pos.h = bg->h;
+	}
+}
+
+CPicture::~CPicture()
+{
+	if(freeSurf)
+		SDL_FreeSurface(bg);
+	delete srcRect;
+}
+
+void CPicture::init()
+{
+	needRefresh = false;
+	srcRect = nullptr;
+}
+
+void CPicture::show(SDL_Surface * to)
+{
+	if (needRefresh)
+		showAll(to);
+}
+
+void CPicture::showAll(SDL_Surface * to)
+{
+	if(bg)
+	{
+		if(srcRect)
+		{
+			SDL_Rect srcRectCpy = *srcRect;
+			SDL_Rect dstRect = srcRectCpy;
+			dstRect.x = pos.x;
+			dstRect.y = pos.y;
+
+			CSDL_Ext::blitSurface(bg, &srcRectCpy, to, &dstRect);
+		}
+		else
+			blitAt(bg, pos, to);
+	}
+}
+
+void CPicture::convertToScreenBPP()
+{
+	SDL_Surface *hlp = bg;
+	bg = SDL_ConvertSurface(hlp,screen->format,0);
+	CSDL_Ext::setDefaultColorKey(bg);	
+	SDL_FreeSurface(hlp);
+}
+
+void CPicture::setAlpha(int value)
+{	
+	#ifdef VCMI_SDL1
+	SDL_SetAlpha(bg, SDL_SRCALPHA, value);	
+	#else
+	SDL_SetSurfaceAlphaMod(bg,value);
+	#endif // 0
+}
+
+void CPicture::scaleTo(Point size)
+{
+	SDL_Surface * scaled = CSDL_Ext::scaleSurface(bg, size.x, size.y);
+
+	if(freeSurf)
+		SDL_FreeSurface(bg);
+
+	setSurface(scaled);
+	freeSurf = false;
+}
+
+void CPicture::createSimpleRect(const Rect &r, bool screenFormat, ui32 color)
+{
+	pos += r;
+	pos.w = r.w;
+	pos.h = r.h;
+	if(screenFormat)
+		bg = CSDL_Ext::newSurface(r.w, r.h);
+	else
+		bg = SDL_CreateRGBSurface(SDL_SWSURFACE, r.w, r.h, 8, 0, 0, 0, 0);
+
+	SDL_FillRect(bg, nullptr, color);
+	freeSurf = true;
+}
+
+void CPicture::colorizeAndConvert(PlayerColor player)
+{
+	assert(bg);
+	colorize(player);
+	convertToScreenBPP();
+}
+
+void CPicture::colorize(PlayerColor player)
+{
+	assert(bg);
+	graphics->blueToPlayersAdv(bg, player);
+}
+
+CFilledTexture::CFilledTexture(std::string imageName, Rect position):
+    CIntObject(0, position.topLeft()),
+    texture(BitmapHandler::loadBitmap(imageName))
+{
+	pos.w = position.w;
+	pos.h = position.h;
+}
+
+CFilledTexture::~CFilledTexture()
+{
+	SDL_FreeSurface(texture);
+}
+
+void CFilledTexture::showAll(SDL_Surface *to)
+{
+	CSDL_Ext::CClipRectGuard guard(to, pos);
+	CSDL_Ext::fillTexture(to, texture);
+}
+
+CAnimImage::CAnimImage(std::string name, size_t Frame, size_t Group, int x, int y, ui8 Flags):
+	frame(Frame),
+	group(Group),
+	player(-1),
+	flags(Flags)
+{
+	pos.x += x;
+	pos.y += y;
+	anim = new CAnimation(name);
+	init();
+}
+
+CAnimImage::CAnimImage(CAnimation *Anim, size_t Frame, size_t Group, int x, int y, ui8 Flags):
+	anim(Anim),
+	frame(Frame),
+	group(Group),
+	player(-1),
+	flags(Flags)
+{
+	pos.x += x;
+	pos.y += y;
+	init();
+}
+
+size_t CAnimImage::size()
+{
+	return anim->size(group);
+}
+
+void CAnimImage::init()
+{
+	anim->load(frame, group);
+	if (flags & CShowableAnim::BASE)
+		anim->load(0,group);
+
+	IImage *img = anim->getImage(frame, group);
+	if (img)
+	{
+		pos.w = img->width();
+		pos.h = img->height();
+	}
+}
+
+CAnimImage::~CAnimImage()
+{
+	anim->unload(frame, group);
+	if (flags & CShowableAnim::BASE)
+		anim->unload(0,group);
+	delete anim;
+}
+
+void CAnimImage::showAll(SDL_Surface * to)
+{
+	IImage *img;
+
+	if ( flags & CShowableAnim::BASE && frame != 0)
+		if ((img = anim->getImage(0, group)))
+			img->draw(to, pos.x, pos.y);
+
+	if ((img = anim->getImage(frame, group)))
+		img->draw(to, pos.x, pos.y);
+}
+
+void CAnimImage::setFrame(size_t Frame, size_t Group)
+{
+	if (frame == Frame && group==Group)
+		return;
+	if (anim->size(Group) > Frame)
+	{
+		anim->load(Frame, Group);
+		anim->unload(frame, group);
+		frame = Frame;
+		group = Group;
+		IImage *img = anim->getImage(frame, group);
+		if (img)
+		{
+			if (flags & CShowableAnim::PLAYER_COLORED)
+				img->playerColored(player);
+			pos.w = img->width();
+			pos.h = img->height();
+		}
+	}
+	else
+		logGlobal->errorStream() << "Error: accessing unavailable frame " << Group << ":" << Frame << " in CAnimation!";
+}
+
+void CAnimImage::playerColored(PlayerColor currPlayer)
+{
+	player = currPlayer;
+	flags |= CShowableAnim::PLAYER_COLORED;
+	anim->getImage(frame, group)->playerColored(player);
+	if (flags & CShowableAnim::BASE)
+			anim->getImage(0, group)->playerColored(player);
+}
+
+CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 Delay, size_t Group):
+	anim(new CAnimation(name, Flags & USE_RLE)),
+	group(Group),
+	frame(0),
+	first(0),
+	frameDelay(Delay),
+	value(0),
+	flags(Flags),
+	xOffset(0),
+	yOffset(0),
+	alpha(255)
+{
+	anim->loadGroup(group);
+	last = anim->size(group);
+
+	pos.w = anim->getImage(0, group)->width();
+	pos.h = anim->getImage(0, group)->height();
+	pos.x+= x;
+	pos.y+= y;
+}
+
+CShowableAnim::~CShowableAnim()
+{
+	anim->unloadGroup(group);
+	delete anim;
+}
+
+void CShowableAnim::setAlpha(ui32 alphaValue)
+{
+	alpha = std::min<ui32>(alphaValue, 255);
+}
+
+bool CShowableAnim::set(size_t Group, size_t from, size_t to)
+{
+	size_t max = anim->size(Group);
+
+	if (to < max)
+		max = to;
+
+	if (max < from || max == 0)
+		return false;
+
+	anim->load(Group);
+	anim->unload(group);
+	group = Group;
+	frame = first = from;
+	last = max;
+	value = 0;
+	return true;
+}
+
+bool CShowableAnim::set(size_t Group)
+{
+	if (anim->size(Group)== 0)
+		return false;
+	if (group != Group)
+	{
+		anim->loadGroup(Group);
+		anim->unloadGroup(group);
+		first = 0;
+		group = Group;
+		last = anim->size(Group);
+	}
+	frame = value = 0;
+	return true;
+}
+
+void CShowableAnim::reset()
+{
+	value = 0;
+	frame = first;
+
+	if (callback)
+		callback();
+}
+
+void CShowableAnim::clipRect(int posX, int posY, int width, int height)
+{
+	xOffset = posX;
+	yOffset = posY;
+	pos.w = width;
+	pos.h = height;
+}
+
+void CShowableAnim::show(SDL_Surface * to)
+{
+	if ( flags & BASE )// && frame != first) // FIXME: results in graphical glytch in Fortress, upgraded hydra's dwelling
+		blitImage(first, group, to);
+	blitImage(frame, group, to);
+
+	if ((flags & PLAY_ONCE) && frame + 1 == last)
+		return;
+
+	if ( ++value == frameDelay )
+	{
+		value = 0;
+		if ( ++frame >= last)
+			reset();
+	}
+}
+
+void CShowableAnim::showAll(SDL_Surface * to)
+{
+	if ( flags & BASE )// && frame != first)
+		blitImage(first, group, to);
+	blitImage(frame, group, to);
+}
+
+void CShowableAnim::blitImage(size_t frame, size_t group, SDL_Surface *to)
+{
+	assert(to);
+	Rect src( xOffset, yOffset, pos.w, pos.h);
+	IImage * img = anim->getImage(frame, group);
+	if (img)
+		img->draw(to, pos.x-xOffset, pos.y-yOffset, &src, alpha);
+}
+
+void CShowableAnim::rotate(bool on, bool vertical)
+{
+	ui8 flag = vertical? VERTICAL_FLIP:HORIZONTAL_FLIP;
+	if (on)
+		flags |= flag;
+	else
+		flags &= ~flag;
+}
+
+CCreatureAnim::CCreatureAnim(int x, int y, std::string name, Rect picPos, ui8 flags, EAnimType type):
+	CShowableAnim(x,y,name,flags,4,type)
+{
+	xOffset = picPos.x;
+	yOffset = picPos.y;
+	if (picPos.w)
+		pos.w = picPos.w;
+	if (picPos.h)
+		pos.h = picPos.h;
+};
+
+void CCreatureAnim::loopPreview(bool warMachine)
+{
+	std::vector<EAnimType> available;
+
+	static const EAnimType creaPreviewList[] = {HOLDING, HITTED, DEFENCE, ATTACK_FRONT, CAST_FRONT};
+	static const EAnimType machPreviewList[] = {HOLDING, MOVING, SHOOT_UP, SHOOT_FRONT, SHOOT_DOWN};
+	auto & previewList = warMachine ? machPreviewList : creaPreviewList;
+
+	for (auto & elem : previewList)
+		if (anim->size(elem))
+			available.push_back(elem);
+
+	size_t rnd = CRandomGenerator::getDefault().nextInt(available.size() * 2 - 1);
+
+	if (rnd >= available.size())
+	{
+		EAnimType type;
+		if ( anim->size(MOVING) == 0 )//no moving animation present
+			type = HOLDING;
+		else
+			type = MOVING;
+
+		//display this anim for ~1 second (time is random, but it looks good)
+		for (size_t i=0; i< 12/anim->size(type) + 1; i++)
+			addLast(type);
+	}
+	else
+		addLast(available[rnd]);
+}
+
+void CCreatureAnim::addLast(EAnimType newType)
+{
+	if (type != MOVING && newType == MOVING)//starting moving - play init sequence
+	{
+		queue.push( MOVE_START );
+	}
+	else if (type == MOVING && newType != MOVING )//previous anim was moving - finish it
+	{
+		queue.push( MOVE_END );
+	}
+	if (newType == TURN_L || newType == TURN_R)
+		queue.push(newType);
+
+	queue.push(newType);
+}
+
+void CCreatureAnim::reset()
+{
+	//if we are in the middle of rotation - set flag
+	if (type == TURN_L && !queue.empty() && queue.front() == TURN_L)
+		rotate(true);
+	if (type == TURN_R && !queue.empty() && queue.front() == TURN_R)
+		rotate(false);
+
+	while (!queue.empty())
+	{
+		EAnimType at = queue.front();
+		queue.pop();
+		if (set(at))
+			return;
+	}
+	if  (callback)
+		callback();
+	while (!queue.empty())
+	{
+		EAnimType at = queue.front();
+		queue.pop();
+		if (set(at))
+			return;
+	}
+	set(HOLDING);
+}
+
+void CCreatureAnim::startPreview(bool warMachine)
+{
+	callback = boost::bind(&CCreatureAnim::loopPreview, this, warMachine);
+}
+
+void CCreatureAnim::clearAndSet(EAnimType type)
+{
+	while (!queue.empty())
+		queue.pop();
+	set(type);
+}

+ 226 - 0
client/widgets/Images.h

@@ -0,0 +1,226 @@
+#pragma once
+
+#include "../gui/CIntObject.h"
+#include "../gui/SDL_Extensions.h"
+
+struct SDL_Surface;
+struct Rect;
+class CAnimImage;
+class CLabel;
+class CAnimation;
+class CDefHandler;
+
+/*
+ * Images.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+// Image class
+class CPicture : public CIntObject
+{
+	void setSurface(SDL_Surface *to);
+public: 
+	SDL_Surface * bg;
+	Rect * srcRect; //if nullptr then whole surface will be used
+	bool freeSurf; //whether surface will be freed upon CPicture destruction
+	bool needRefresh;//Surface needs to be displayed each frame
+
+	operator SDL_Surface*()
+	{
+		return bg;
+	}
+
+	CPicture(const Rect & r, const SDL_Color & color, bool screenFormat = false); //rect filled with given color
+	CPicture(const Rect & r, ui32 color, bool screenFormat = false); //rect filled with given color
+	CPicture(SDL_Surface * BG, int x = 0, int y=0, bool Free = true); //wrap existing SDL_Surface
+	CPicture(const std::string &bmpname, int x=0, int y=0);
+	CPicture(SDL_Surface *BG, const Rect &SrcRext, int x = 0, int y = 0, bool free = false); //wrap subrect of given surface
+	~CPicture();
+	void init();
+
+	//set alpha value for whole surface. Note: may be messed up if surface is shared
+	// 0=transparent, 255=opaque
+	void setAlpha(int value);
+
+	void scaleTo(Point size);
+	void createSimpleRect(const Rect &r, bool screenFormat, ui32 color);
+	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
+	void convertToScreenBPP();
+	void colorizeAndConvert(PlayerColor player);
+	void colorize(PlayerColor player);
+};
+
+/// area filled with specific texture
+class CFilledTexture : CIntObject
+{
+	SDL_Surface * texture;
+
+public:
+	CFilledTexture(std::string imageName, Rect position);
+	~CFilledTexture();
+	void showAll(SDL_Surface *to);
+};
+
+/// Class for displaying one image from animation
+class CAnimImage: public CIntObject
+{
+private:
+	CAnimation* anim;
+	//displayed frame/group
+	size_t frame;
+	size_t group;
+	PlayerColor player;
+	ui8 flags;
+
+	void init();
+
+public:
+	CAnimImage(std::string name, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0);
+	CAnimImage(CAnimation* anim, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0);
+	~CAnimImage();//d-tor
+
+	//size of animation
+	size_t size();
+
+	//change displayed frame on this one
+	void setFrame(size_t Frame, size_t Group=0);
+
+	//makes image player-colored
+	void playerColored(PlayerColor player);
+
+	void showAll(SDL_Surface * to);
+};
+
+/// Base class for displaying animation, used as superclass for different animations
+class CShowableAnim: public CIntObject
+{
+public:
+	enum EFlags
+	{
+		BASE=1,            //base frame will be blitted before current one
+		HORIZONTAL_FLIP=2, //TODO: will be displayed rotated
+		VERTICAL_FLIP=4,   //TODO: will be displayed rotated
+		USE_RLE=8,         //RLE-d version, support full alpha-channel for 8-bit images
+		PLAYER_COLORED=16, //TODO: all loaded images will be player-colored
+		PLAY_ONCE=32       //play animation only once and stop at last frame
+	};
+protected:
+	CAnimation * anim;
+
+	size_t group, frame;//current frame
+
+	size_t first, last; //animation range
+
+	//TODO: replace with time delay(needed for battles)
+	ui32 frameDelay;//delay in frames of each image
+	ui32 value;//how many times current frame was showed
+
+	ui8 flags;//Flags from EFlags enum
+
+	//blit image with optional rotation, fitting into rect, etc
+	void blitImage(size_t frame, size_t group, SDL_Surface *to);
+
+	//For clipping in rect, offsets of picture coordinates
+	int xOffset, yOffset;
+
+	ui8 alpha;
+
+public:
+	//called when next animation sequence is required
+	std::function<void()> callback;
+
+	//Set per-surface alpha, 0 = transparent, 255 = opaque
+	void setAlpha(ui32 alphaValue);
+
+	CShowableAnim(int x, int y, std::string name, ui8 flags=0, ui32 Delay=4, size_t Group=0);
+	~CShowableAnim();
+
+	//set animation to group or part of group
+	bool set(size_t Group);
+	bool set(size_t Group, size_t from, size_t to=-1);
+
+	//set rotation flags
+	void rotate(bool on, bool vertical=false);
+
+	//move displayed part of picture (if picture is clipped to rect)
+	void clipRect(int posX, int posY, int width, int height);
+
+	//set frame to first, call callback
+	virtual void reset();
+
+	//show current frame and increase counter
+	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
+};
+
+/// Creature-dependend animations like attacking, moving,...
+class CCreatureAnim: public CShowableAnim
+{
+public:
+
+	enum EHeroAnimType
+	{
+		HERO_HOLDING = 0,
+		HERO_IDLE = 1, // idling movement that happens from time to time
+		HERO_DEFEAT = 2, // played when army loses stack or on friendly fire
+		HERO_VICTORY = 3, // when enemy stack killed or huge damage is dealt
+		HERO_CAST_SPELL = 4 // spellcasting
+	};
+
+	enum EAnimType // list of creature animations, numbers were taken from def files
+	{
+		MOVING=0,
+		MOUSEON=1,
+		HOLDING=2,
+		HITTED=3,
+		DEFENCE=4,
+		DEATH=5,
+		//DEATH2=6, //unused?
+		TURN_L=7,
+		TURN_R=8, //same
+		//TURN_L2=9, //identical to previous?
+		//TURN_R2=10,
+		ATTACK_UP=11,
+		ATTACK_FRONT=12,
+		ATTACK_DOWN=13,
+		SHOOT_UP=14,
+		SHOOT_FRONT=15,
+		SHOOT_DOWN=16,
+		CAST_UP=17,
+		CAST_FRONT=18,
+		CAST_DOWN=19,
+		MOVE_START=20,
+		MOVE_END=21,
+		DEAD = 22 // new group, used to show dead stacks. If empty - last frame from "DEATH" will be copied here
+
+	};
+
+private:
+	//queue of animations waiting to be displayed
+	std::queue<EAnimType> queue;
+
+	//this function is used as callback if preview flag was set during construction
+	void loopPreview(bool warMachine);
+
+public:
+	//change anim to next if queue is not empty, call callback othervice
+	void reset();
+
+	//add sequence to the end of queue
+	void addLast(EAnimType newType);
+
+	void startPreview(bool warMachine);
+
+	//clear queue and set animation to this sequence
+	void clearAndSet(EAnimType type);
+
+	CCreatureAnim(int x, int y, std::string name, Rect picPos,
+				  ui8 flags= USE_RLE, EAnimType = HOLDING );
+
+};

+ 29 - 374
client/widgets/MiscWidgets.cpp

@@ -1,6 +1,8 @@
 #include "StdInc.h"
 #include "MiscWidgets.h"
 
+#include "CComponent.h"
+
 #include "../gui/CGuiHandler.h"
 #include "../gui/CCursorHandler.h"
 
@@ -10,12 +12,15 @@
 #include "../CGameInfo.h"
 #include "../windows/CAdvmapInterface.h"
 #include "../windows/CCastleInterface.h"
+#include "../windows/InfoWindows.h"
 
 #include "../../CCallback.h"
 
 #include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/CModHandler.h"
+#include "../../lib/CGameState.h"
 
 /*
  * MiscWidgets.cpp, part of VCMI engine
@@ -27,408 +32,58 @@
  *
  */
 
-void CSelWindow::selectionChange(unsigned to)
-{
-	for (unsigned i=0;i<components.size();i++)
-	{
-		CSelectableComponent * pom = dynamic_cast<CSelectableComponent*>(components[i]);
-		if (!pom)
-			continue;
-		pom->select(i==to);
-	}
-	redraw();
-}
-
-CSelWindow::CSelWindow(const std::string &Text, PlayerColor player, int charperline, const std::vector<CSelectableComponent*> &comps, const std::vector<std::pair<std::string,CFunctionList<void()> > > &Buttons, QueryID askID)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	ID = askID;
-	for(int i=0;i<Buttons.size();i++)
-	{
-		buttons.push_back(new CAdventureMapButton("","",Buttons[i].second,0,0,Buttons[i].first));
-		if(!i  &&  askID.getNum() >= 0)
-			buttons.back()->callback += boost::bind(&CSelWindow::madeChoice,this);
-		buttons[i]->callback += boost::bind(&CInfoWindow::close,this); //each button will close the window apart from call-defined actions
-	}
-
-	text = new CTextBox(Text, Rect(0, 0, 250, 100), 0, FONT_MEDIUM, CENTER, Colors::WHITE);
-
-	buttons.front()->assignedKeys.insert(SDLK_RETURN); //first button - reacts on enter
-	buttons.back()->assignedKeys.insert(SDLK_ESCAPE); //last button - reacts on escape
-
-	if(buttons.size() > 1  &&  askID.getNum() >= 0) //cancel button functionality
-		buttons.back()->callback += boost::bind(&CCallback::selectionMade,LOCPLINT->cb.get(),0,askID);
-
-	for(int i=0;i<comps.size();i++)
-	{
-		comps[i]->recActions = 255;
-		addChild(comps[i]);
-		components.push_back(comps[i]);
-		comps[i]->onSelect = boost::bind(&CSelWindow::selectionChange,this,i);
-		if(i<9)
-			comps[i]->assignedKeys.insert(SDLK_1+i);
-	}
-	CMessage::drawIWindow(this, Text, player);
-}
-
-void CSelWindow::madeChoice()
-{
-	if(ID.getNum() < 0)
-		return;
-	int ret = -1;
-	for (int i=0;i<components.size();i++)
-	{
-		if(dynamic_cast<CSelectableComponent*>(components[i])->selected)
-		{
-			ret = i;
-		}
-	}
-	LOCPLINT->cb->selectionMade(ret+1,ID);
-}
-
-CInfoWindow::CInfoWindow(std::string Text, PlayerColor player, const TCompsInfo &comps, const TButtonsInfo &Buttons, bool delComps)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	type |= BLOCK_ADV_HOTKEYS;
-	ID = QueryID(-1);
-	for(auto & Button : Buttons)
-	{
-		CAdventureMapButton *button = new CAdventureMapButton("","",boost::bind(&CInfoWindow::close,this),0,0,Button.first);
-		button->borderColor = Colors::METALLIC_GOLD;
-		button->borderEnabled = true;
-		button->callback.add(Button.second); //each button will close the window apart from call-defined actions
-		buttons.push_back(button);
-	}
-
-	text = new CTextBox(Text, Rect(0, 0, 250, 100), 0, FONT_MEDIUM, CENTER, Colors::WHITE);
-	if(!text->slider)
-	{
-		text->resize(text->label->textSize);
-	}
-
-	if(buttons.size())
-	{
-		buttons.front()->assignedKeys.insert(SDLK_RETURN); //first button - reacts on enter
-		buttons.back()->assignedKeys.insert(SDLK_ESCAPE); //last button - reacts on escape
-	}
-
-	for(auto & comp : comps)
-	{
-		comp->recActions = 0xff;
-		addChild(comp);
-		comp->recActions &= ~(SHOWALL | UPDATE);
-		components.push_back(comp);
-	}
-	setDelComps(delComps);
-	CMessage::drawIWindow(this,Text,player);
-}
-
-CInfoWindow::CInfoWindow()
-{
-	ID = QueryID(-1);
-	setDelComps(false);
-	text = nullptr;
-}
-
-void CInfoWindow::close()
-{
-	GH.popIntTotally(this);
-	if(LOCPLINT)
-		LOCPLINT->showingDialog->setn(false);
-}
-
-void CInfoWindow::show(SDL_Surface * to)
-{
-	CIntObject::show(to);
-}
-
-CInfoWindow::~CInfoWindow()
-{
-	if(!delComps)
-	{
-		for (auto & elem : components)
-			removeChild(elem);
-	}
-}
-
-void CInfoWindow::showAll(SDL_Surface * to)
-{
-	CSimpleWindow::show(to);
-	CIntObject::showAll(to);
-}
-
-void CInfoWindow::showInfoDialog(const std::string &text, const std::vector<CComponent *> *components, bool DelComps, PlayerColor player)
-{
-	CInfoWindow * window = CInfoWindow::create(text, player, components, DelComps);
-	GH.pushInt(window);
-}
-
-void CInfoWindow::showYesNoDialog(const std::string & text, const std::vector<CComponent*> *components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, bool DelComps, PlayerColor player)
-{
-	assert(!LOCPLINT || LOCPLINT->showingDialog->get());
-	std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
-	pom.push_back(std::pair<std::string,CFunctionList<void()> >("IOKAY.DEF",0));
-	pom.push_back(std::pair<std::string,CFunctionList<void()> >("ICANCEL.DEF",0));
-	CInfoWindow * temp = new CInfoWindow(text, player, components ? *components : std::vector<CComponent*>(), pom, DelComps);
-	for(auto & elem : onYes.funcs)
-		temp->buttons[0]->callback += elem;
-	for(auto & elem : onNo.funcs)
-		temp->buttons[1]->callback += elem;
-
-	GH.pushInt(temp);
-}
-
-void CInfoWindow::showOkDialog(const std::string & text, const std::vector<CComponent*> *components, const boost::function<void()> & onOk, bool delComps, PlayerColor player)
-{
-	std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
-	pom.push_back(std::pair<std::string,CFunctionList<void()> >("IOKAY.DEF",0));
-	CInfoWindow * temp = new CInfoWindow(text, player, *components, pom, delComps);
-	temp->buttons[0]->callback += onOk;
-
-	GH.pushInt(temp);
-}
-
-CInfoWindow * CInfoWindow::create(const std::string &text, PlayerColor playerID /*= 1*/, const std::vector<CComponent*> *components /*= nullptr*/, bool DelComps)
-{
-	std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
-	pom.push_back(std::pair<std::string,CFunctionList<void()> >("IOKAY.DEF",0));
-	CInfoWindow * ret = new CInfoWindow(text, playerID, components ? *components : std::vector<CComponent*>(), pom, DelComps);
-	return ret;
-}
-
-std::string CInfoWindow::genText(std::string title, std::string description)
-{
-	return std::string("{") + title + "}" + "\n\n" + description;
-}
-
-void CInfoWindow::setDelComps(bool DelComps)
-{
-	delComps = DelComps;
-	for(CComponent *comp : components)
-	{
-		if(delComps)
-			comp->recActions |= DISPOSE;
-		else
-			comp->recActions &= ~DISPOSE;
-	}
-}
-
-CInfoPopup::CInfoPopup(SDL_Surface * Bitmap, int x, int y, bool Free)
- :free(Free),bitmap(Bitmap)
-{
-	init(x, y);
-}
-
-
-CInfoPopup::CInfoPopup(SDL_Surface * Bitmap, const Point &p, EAlignment alignment, bool Free/*=false*/)
- : free(Free),bitmap(Bitmap)
-{
-	switch(alignment)
-	{
-	case BOTTOMRIGHT:
-		init(p.x - Bitmap->w, p.y - Bitmap->h);
-		break;
-	case CENTER:
-		init(p.x - Bitmap->w/2, p.y - Bitmap->h/2);
-		break;
-	case TOPLEFT:
-		init(p.x, p.y);
-		break;
-	default:
-		assert(0); //not implemented
-	}
-}
-
-CInfoPopup::CInfoPopup(SDL_Surface *Bitmap, bool Free)
-{
-	CCS->curh->hide();
-
-	free=Free;
-	bitmap=Bitmap;
-
-	if(bitmap)
-	{
-		pos.x = screen->w/2 - bitmap->w/2;
-		pos.y = screen->h/2 - bitmap->h/2;
-		pos.h = bitmap->h;
-		pos.w = bitmap->w;
-	}
-}
-
-void CInfoPopup::close()
-{
-	if(free)
-		SDL_FreeSurface(bitmap);
-	GH.popIntTotally(this);
-}
-
-void CInfoPopup::show(SDL_Surface * to)
-{
-	blitAt(bitmap,pos.x,pos.y,to);
-}
-
-CInfoPopup::~CInfoPopup()
-{
-	CCS->curh->show();
-}
-
-void CInfoPopup::init(int x, int y)
-{
-	CCS->curh->hide();
-
-	pos.x = x;
-	pos.y = y;
-	pos.h = bitmap->h;
-	pos.w = bitmap->w;
-
-	// Put the window back on screen if necessary
-	vstd::amax(pos.x, 0);
-	vstd::amax(pos.y, 0);
-	vstd::amin(pos.x, screen->w - bitmap->w);
-	vstd::amin(pos.y, screen->h - bitmap->h);
-}
-
-
-void CRClickPopup::clickRight(tribool down, bool previousState)
-{
-	if(down)
-		return;
-	close();
-}
-
-void CRClickPopup::close()
+void CHoverableArea::hover (bool on)
 {
-	GH.popIntTotally(this);
+	if (on)
+		GH.statusbar->setText(hoverText);
+	else if (GH.statusbar->getText()==hoverText)
+		GH.statusbar->clear();
 }
 
-void CRClickPopup::createAndPush(const std::string &txt, const CInfoWindow::TCompsInfo &comps)
+CHoverableArea::CHoverableArea()
 {
-	PlayerColor player = LOCPLINT ? LOCPLINT->playerID : PlayerColor(1); //if no player, then use blue
-
-	CSimpleWindow * temp = new CInfoWindow(txt, player, comps);
-	temp->center(Point(GH.current->motion)); //center on mouse
-	temp->fitToScreen(10);
-	auto  rcpi = new CRClickPopupInt(temp,true);
-	GH.pushInt(rcpi);
+	addUsedEvents(HOVER);
 }
 
-void CRClickPopup::createAndPush(const std::string &txt, CComponent * component)
+CHoverableArea::~CHoverableArea()
 {
-	CInfoWindow::TCompsInfo intComps;
-	intComps.push_back(component);
-
-	createAndPush(txt, intComps);
 }
 
-void CRClickPopup::createAndPush(const CGObjectInstance *obj, const Point &p, EAlignment alignment /*= BOTTOMRIGHT*/)
+void LRClickableAreaWText::clickLeft(tribool down, bool previousState)
 {
-	CIntObject *iWin = createInfoWin(p, obj); //try get custom infowindow for this obj
-	if(iWin)
-		GH.pushInt(iWin);
-	else
+	if(!down && previousState && !text.empty())
 	{
-		if (adventureInt->curHero())
-			CRClickPopup::createAndPush(obj->getHoverText(adventureInt->curHero()));
-		else
-			CRClickPopup::createAndPush(obj->getHoverText(LOCPLINT->playerID));
+		LOCPLINT->showInfoDialog(text);
 	}
 }
-
-CRClickPopup::CRClickPopup()
-{
-	addUsedEvents(RCLICK);
-}
-
-CRClickPopup::~CRClickPopup()
-{
-}
-
-void CRClickPopupInt::show(SDL_Surface * to)
-{
-	inner->show(to);
-}
-
-CRClickPopupInt::CRClickPopupInt( IShowActivatable *our, bool deleteInt )
+void LRClickableAreaWText::clickRight(tribool down, bool previousState)
 {
-	CCS->curh->hide();
-	inner = our;
-	delInner = deleteInt;
+	if (!text.empty())
+		adventureInt->handleRightClick(text, down);
 }
 
-CRClickPopupInt::~CRClickPopupInt()
+LRClickableAreaWText::LRClickableAreaWText()
 {
-	if(delInner)
-		delete inner;
-
-	CCS->curh->show();
+	init();
 }
 
-void CRClickPopupInt::showAll(SDL_Surface * to)
+LRClickableAreaWText::LRClickableAreaWText(const Rect &Pos, const std::string &HoverText /*= ""*/, const std::string &ClickText /*= ""*/)
 {
-	inner->showAll(to);
+	init();
+	pos = Pos + pos;
+	hoverText = HoverText;
+	text = ClickText;
 }
 
-Point CInfoBoxPopup::toScreen(Point p)
+LRClickableAreaWText::~LRClickableAreaWText()
 {
-	vstd::abetween(p.x, adventureInt->terrain.pos.x + 100, adventureInt->terrain.pos.x + adventureInt->terrain.pos.w - 100);
-	vstd::abetween(p.y, adventureInt->terrain.pos.y + 100, adventureInt->terrain.pos.y + adventureInt->terrain.pos.h - 100);
-
-	return p;
 }
 
-CInfoBoxPopup::CInfoBoxPopup(Point position, const CGTownInstance * town):
-	CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "TOWNQVBK", toScreen(position))
+void LRClickableAreaWText::init()
 {
-	InfoAboutTown iah;
-	LOCPLINT->cb->getTownInfo(town, iah);
-
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	new CTownTooltip(Point(9, 10), iah);
-}
-
-CInfoBoxPopup::CInfoBoxPopup(Point position, const CGHeroInstance * hero):
-	CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "HEROQVBK", toScreen(position))
-{
-	InfoAboutHero iah;
-	LOCPLINT->cb->getHeroInfo(hero, iah);
-
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	new CHeroTooltip(Point(9, 10), iah);
-}
-
-CInfoBoxPopup::CInfoBoxPopup(Point position, const CGGarrison * garr):
-	CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "TOWNQVBK", toScreen(position))
-{
-	InfoAboutTown iah;
-	LOCPLINT->cb->getTownInfo(garr, iah);
-
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	new CArmyTooltip(Point(9, 10), iah);
-}
-
-CIntObject * CRClickPopup::createInfoWin(Point position, const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero
-{
-	if(!specific)
-		specific = adventureInt->selection;
-
-	assert(specific);
-
-	switch(specific->ID)
-	{
-	case Obj::HERO:
-		return new CInfoBoxPopup(position, dynamic_cast<const CGHeroInstance *>(specific));
-	case Obj::TOWN:
-		return new CInfoBoxPopup(position, dynamic_cast<const CGTownInstance *>(specific));
-	case Obj::GARRISON:
-	case Obj::GARRISON2:
-		return new CInfoBoxPopup(position, dynamic_cast<const CGGarrison *>(specific));
-	default:
-		return nullptr;
-	}
+	addUsedEvents(LCLICK | RCLICK | HOVER);
 }
 
-
 void LRClickableAreaWTextComp::clickLeft(tribool down, bool previousState)
 {
 	if((!down) && previousState)

+ 42 - 101
client/widgets/MiscWidgets.h

@@ -1,6 +1,6 @@
 #pragma once
 
-#include "CComponent.h"
+#include "../gui/CIntObject.h"
 
 /*
  * MiscWidgets.h, part of VCMI engine
@@ -19,105 +19,33 @@ class CSelectableComponent;
 class InfoAboutArmy;
 class CArmedInstance;
 class IBonusBearer;
+class CAnimImage;
 
-/// text + comp. + ok button
-class CInfoWindow : public CSimpleWindow
-{ //window able to delete its components when closed
-	bool delComps; //whether comps will be deleted
-
-public:
-	typedef std::vector<std::pair<std::string,CFunctionList<void()> > > TButtonsInfo;
-	typedef std::vector<CComponent*> TCompsInfo;
-	QueryID ID; //for identification
-	CTextBox *text;
-	std::vector<CAdventureMapButton *> buttons;
-	std::vector<CComponent*> components;
-	CSlider *slider;
-
-	void setDelComps(bool DelComps);
-	virtual void close();
-
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
-	void sliderMoved(int to);
-
-	CInfoWindow(std::string Text, PlayerColor player, const TCompsInfo &comps = TCompsInfo(), const TButtonsInfo &Buttons = TButtonsInfo(), bool delComps = true); //c-tor
-	CInfoWindow(); //c-tor
-	~CInfoWindow(); //d-tor
-
-	//use only before the game starts! (showYesNoDialog in LOCPLINT must be used then)
-	static void showInfoDialog( const std::string & text, const std::vector<CComponent*> *components, bool DelComps = true, PlayerColor player = PlayerColor(1));
-	static void showOkDialog(const std::string & text, const std::vector<CComponent*> *components, const boost::function<void()> & onOk, bool delComps = true, PlayerColor player = PlayerColor(1));
-	static void showYesNoDialog( const std::string & text, const std::vector<CComponent*> *components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, bool DelComps = true, PlayerColor player = PlayerColor(1));
-	static CInfoWindow *create(const std::string &text, PlayerColor playerID = PlayerColor(1), const std::vector<CComponent*> *components = nullptr, bool DelComps = false);
-
-	/// create text from title and description: {title}\n\n description
-	static std::string genText(std::string title, std::string description);
-};
-
-/// popup displayed on R-click
-class CRClickPopup : public CIntObject
+/// Shows a text by moving the mouse cursor over the object
+class CHoverableArea: public virtual CIntObject
 {
 public:
-	virtual void close();
-	void clickRight(tribool down, bool previousState);
-
-	CRClickPopup();
-	virtual ~CRClickPopup(); //d-tor
-
-	static CIntObject* createInfoWin(Point position, const CGObjectInstance * specific);
-	static void createAndPush(const std::string &txt, const CInfoWindow::TCompsInfo &comps = CInfoWindow::TCompsInfo());
-	static void createAndPush(const std::string &txt, CComponent * component);
-	static void createAndPush(const CGObjectInstance *obj, const Point &p, EAlignment alignment = BOTTOMRIGHT);
-};
+	std::string hoverText;
 
-/// popup displayed on R-click
-class CRClickPopupInt : public CRClickPopup
-{
-public:
-	IShowActivatable *inner;
-	bool delInner;
+	virtual void hover (bool on);
 
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
-	CRClickPopupInt(IShowActivatable *our, bool deleteInt); //c-tor
-	virtual ~CRClickPopupInt(); //d-tor
+	CHoverableArea();
+	virtual ~CHoverableArea();
 };
 
-class CInfoPopup : public CRClickPopup
+/// Can interact on left and right mouse clicks, plus it shows a text when by hovering over it
+class LRClickableAreaWText: public CHoverableArea
 {
 public:
-	bool free; //TODO: comment me
-	SDL_Surface * bitmap; //popup background
-	void close();
-	void show(SDL_Surface * to);
-	CInfoPopup(SDL_Surface * Bitmap, int x, int y, bool Free=false); //c-tor
-	CInfoPopup(SDL_Surface * Bitmap, const Point &p, EAlignment alignment, bool Free=false); //c-tor
-	CInfoPopup(SDL_Surface * Bitmap = nullptr, bool Free = false); //default c-tor
-
-	void init(int x, int y);
-	~CInfoPopup(); //d-tor
-};
+	std::string text;
 
-/// popup on adventure map for town\hero objects
-class CInfoBoxPopup : public CWindowObject
-{
-	Point toScreen(Point pos);
-public:
-	CInfoBoxPopup(Point position, const CGTownInstance * town);
-	CInfoBoxPopup(Point position, const CGHeroInstance * hero);
-	CInfoBoxPopup(Point position, const CGGarrison * garr);
-};
+	LRClickableAreaWText();
+	LRClickableAreaWText(const Rect &Pos, const std::string &HoverText = "", const std::string &ClickText = "");
+	virtual ~LRClickableAreaWText();
+	void init();
 
-/// component selection window
-class CSelWindow : public CInfoWindow
-{ //warning - this window deletes its components by closing!
-public:
-	void selectionChange(unsigned to);
-	void madeChoice(); //looks for selected component and calls callback
-	CSelWindow(const std::string& text, PlayerColor player, int charperline ,const std::vector<CSelectableComponent*> &comps, const std::vector<std::pair<std::string,CFunctionList<void()> > > &Buttons, QueryID askID); //c-tor
-	CSelWindow(){}; //c-tor
-	//notification - this class inherits important destructor from CInfoWindow
+	virtual void clickLeft(tribool down, bool previousState);
+	virtual void clickRight(tribool down, bool previousState);
 };
 
 /// base class for hero/town/garrison tooltips
@@ -173,18 +101,6 @@ public:
 	~CMinorResDataBar(); //d-tor
 };
 
-class MoraleLuckBox : public LRClickableAreaWTextComp
-{
-	CAnimImage *image;
-public:
-	bool morale; //true if morale, false if luck
-	bool small;
-
-	void set(const IBonusBearer *node);
-
-	MoraleLuckBox(bool Morale, const Rect &r, bool Small=false);
-};
-
 /// Opens hero window by left-clicking on it
 class CHeroArea: public CIntObject
 {
@@ -198,6 +114,19 @@ public:
 	void hover(bool on);
 };
 
+/// Can interact on left and right mouse clicks
+class LRClickableAreaWTextComp: public LRClickableAreaWText
+{
+public:
+	int baseType;
+	int bonusValue, type;
+	virtual void clickLeft(tribool down, bool previousState);
+	virtual void clickRight(tribool down, bool previousState);
+
+	LRClickableAreaWTextComp(const Rect &Pos = Rect(0,0,0,0), int BaseType = -1);
+	CComponent * createComponent() const;
+};
+
 /// Opens town screen by left-clicking on it
 class LRClickableAreaOpenTown: public LRClickableAreaWTextComp
 {
@@ -207,3 +136,15 @@ public:
 	void clickRight(tribool down, bool previousState);
 	LRClickableAreaOpenTown();
 };
+
+class MoraleLuckBox : public LRClickableAreaWTextComp
+{
+	CAnimImage *image;
+public:
+	bool morale; //true if morale, false if luck
+	bool small;
+
+	void set(const IBonusBearer *node);
+
+	MoraleLuckBox(bool Morale, const Rect &r, bool Small=false);
+};

+ 234 - 0
client/widgets/ObjectLists.cpp

@@ -0,0 +1,234 @@
+#include "StdInc.h"
+#include "ObjectLists.h"
+
+#include "../gui/CGuiHandler.h"
+#include "Buttons.h"
+
+/*
+ * ObjectLists.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+
+static void intDeleter(CIntObject* object)
+{
+	delete object;
+}
+
+CObjectList::CObjectList(CreateFunc create, DestroyFunc destroy):
+createObject(create),
+destroyObject(destroy)
+{
+	if (!destroyObject)
+		destroyObject = intDeleter;
+}
+
+void CObjectList::deleteItem(CIntObject* item)
+{
+	if (!item)
+		return;
+	removeChild(item);
+	destroyObject(item);
+}
+
+CIntObject* CObjectList::createItem(size_t index)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	CIntObject * item = createObject(index);
+	if (item == nullptr)
+		item = new CIntObject();
+
+	item->recActions = defActions;
+
+	addChild(item);
+	return item;
+}
+
+CTabbedInt::CTabbedInt(CreateFunc create, DestroyFunc destroy, Point position, size_t ActiveID):
+CObjectList(create, destroy),
+activeTab(nullptr),
+activeID(ActiveID)
+{
+	pos += position;
+	reset();
+}
+
+void CTabbedInt::setActive(size_t which)
+{
+	if (which != activeID)
+	{
+		activeID = which;
+		reset();
+	}
+}
+
+void CTabbedInt::reset()
+{
+	deleteItem(activeTab);
+	activeTab = createItem(activeID);
+	activeTab->moveTo(pos.topLeft());
+
+	if (active)
+		redraw();
+}
+
+CIntObject * CTabbedInt::getItem()
+{
+	return activeTab;
+}
+
+CListBox::CListBox(CreateFunc create, DestroyFunc destroy, Point Pos, Point ItemOffset, size_t VisibleSize,
+				   size_t TotalSize, size_t InitialPos, int Slider, Rect SliderPos):
+	CObjectList(create, destroy),
+	first(InitialPos),
+	totalSize(TotalSize),
+	itemOffset(ItemOffset),
+    slider(nullptr)
+{
+	pos += Pos;
+	items.resize(VisibleSize, nullptr);
+
+	if (Slider & 1)
+	{
+		OBJ_CONSTRUCTION_CAPTURING_ALL;
+		slider = new CSlider(SliderPos.x, SliderPos.y, SliderPos.w, boost::bind(&CListBox::moveToPos, this, _1),
+			VisibleSize, TotalSize, InitialPos, Slider & 2, Slider & 4);
+	}
+	reset();
+}
+
+// Used to move active items after changing list position
+void CListBox::updatePositions()
+{
+	Point itemPos = pos.topLeft();
+	for (auto & elem : items)
+	{
+		(elem)->moveTo(itemPos);
+		itemPos += itemOffset;
+	}
+	if (active)
+	{
+		redraw();
+		if (slider)
+			slider->moveTo(first);
+	}
+}
+
+void CListBox::reset()
+{
+	size_t current = first;
+	for (auto & elem : items)
+	{
+		deleteItem(elem);
+		elem = createItem(current++);
+	}
+	updatePositions();
+}
+
+void CListBox::resize(size_t newSize)
+{
+	totalSize = newSize;
+	if (slider)
+		slider->setAmount(totalSize);
+	reset();
+}
+
+size_t CListBox::size()
+{
+	return totalSize;
+}
+
+CIntObject * CListBox::getItem(size_t which)
+{
+	if (which < first || which > first + items.size() || which > totalSize)
+		return nullptr;
+
+	size_t i=first;
+	for (auto iter = items.begin(); iter != items.end(); iter++, i++)
+		if( i == which)
+			return *iter;
+	return nullptr;
+}
+
+size_t CListBox::getIndexOf(CIntObject *item)
+{
+	size_t i=first;
+	for (auto iter = items.begin(); iter != items.end(); iter++, i++)
+		if(*iter == item)
+			return i;
+	return size_t(-1);
+}
+
+void CListBox::scrollTo(size_t which)
+{
+	//scroll up
+	if (first > which)
+		moveToPos(which);
+	//scroll down
+	else if (first + items.size() <= which && which < totalSize)
+		moveToPos(which - items.size() + 1);
+}
+
+void CListBox::moveToPos(size_t which)
+{
+	//Calculate new position
+	size_t maxPossible;
+	if (totalSize > items.size())
+		maxPossible = totalSize - items.size();
+	else
+		maxPossible = 0;
+
+	size_t newPos = std::min(which, maxPossible);
+
+	//If move distance is 1 (most of calls from Slider) - use faster shifts instead of resetting all items
+	if (first - newPos == 1)
+		moveToPrev();
+	else if (newPos - first == 1)
+		moveToNext();
+	else if (newPos != first)
+	{
+		first = newPos;
+		reset();
+	}
+}
+
+void CListBox::moveToNext()
+{
+	//Remove front item and insert new one to end
+	if (first + items.size() < totalSize)
+	{
+		first++;
+		deleteItem(items.front());
+		items.pop_front();
+		items.push_back(createItem(first+items.size()));
+		updatePositions();
+	}
+}
+
+void CListBox::moveToPrev()
+{
+	//Remove last item and insert new one at start
+	if (first)
+	{
+		first--;
+		deleteItem(items.back());
+		items.pop_back();
+		items.push_front(createItem(first));
+		updatePositions();
+	}
+}
+
+size_t CListBox::getPos()
+{
+	return first;
+}
+
+const std::list<CIntObject *> &CListBox::getItems()
+{
+	return items;
+}

+ 111 - 0
client/widgets/ObjectLists.h

@@ -0,0 +1,111 @@
+#pragma once
+
+#include "../gui/CIntObject.h"
+
+struct SDL_Surface;
+struct Rect;
+class CAnimImage;
+class CSlider;
+class CLabel;
+class CAnimation;
+class CDefHandler;
+
+/*
+ * ObjectLists.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+/// Used as base for Tabs and List classes
+class CObjectList : public CIntObject
+{
+public:
+	typedef std::function<CIntObject* (size_t)> CreateFunc;
+	typedef std::function<void(CIntObject *)> DestroyFunc;
+
+private:
+	CreateFunc createObject;
+	DestroyFunc destroyObject;
+
+protected:
+	//Internal methods for safe creation of items (Children capturing and activation/deactivation if needed)
+	void deleteItem(CIntObject* item);
+	CIntObject* createItem(size_t index);
+
+	CObjectList(CreateFunc create, DestroyFunc destroy = DestroyFunc());//Protected constructor
+};
+
+/// Window element with multiple tabs
+class CTabbedInt : public CObjectList
+{
+private:
+	CIntObject * activeTab;
+	size_t activeID;
+
+public:
+	//CreateFunc, DestroyFunc - see CObjectList
+	//Pos - position of object, all tabs will be moved to this position
+	//ActiveID - ID of initially active tab
+	CTabbedInt(CreateFunc create, DestroyFunc destroy = DestroyFunc(), Point position=Point(), size_t ActiveID=0);
+
+	void setActive(size_t which);
+	//recreate active tab
+	void reset();
+
+	//return currently active item
+	CIntObject * getItem();
+};
+
+/// List of IntObjects with optional slider
+class CListBox : public CObjectList
+{
+private:
+	std::list< CIntObject* > items;
+	size_t first;
+	size_t totalSize;
+
+	Point itemOffset;
+	CSlider * slider;
+
+	void updatePositions();
+public:
+	//CreateFunc, DestroyFunc - see CObjectList
+	//Pos - position of first item
+	//ItemOffset - distance between items in the list
+	//VisibleSize - maximal number of displayable at once items
+	//TotalSize
+	//Slider - slider style, bit field: 1 = present(disabled), 2=horisontal(vertical), 4=blue(brown)
+	//SliderPos - position of slider, if present
+	CListBox(CreateFunc create, DestroyFunc destroy, Point Pos, Point ItemOffset, size_t VisibleSize,
+		size_t TotalSize, size_t InitialPos=0, int Slider=0, Rect SliderPos=Rect() );
+
+	//recreate all visible items
+	void reset();
+
+	//change or get total amount of items in the list
+	void resize(size_t newSize);
+	size_t size();
+
+	//return item with index which or null if not present
+	CIntObject * getItem(size_t which);
+
+	//return currently active items
+	const std::list< CIntObject * > & getItems();
+
+	//get index of this item. -1 if not found
+	size_t getIndexOf(CIntObject * item);
+
+	//scroll list to make item which visible
+	void scrollTo(size_t which);
+
+	//scroll list to specified position
+	void moveToPos(size_t which);
+	void moveToNext();
+	void moveToPrev();
+
+	size_t getPos();
+};

+ 642 - 0
client/widgets/TextControls.cpp

@@ -0,0 +1,642 @@
+#include "StdInc.h"
+#include "TextControls.h"
+
+#include "Buttons.h"
+#include "Images.h"
+
+#include "../CMessage.h"
+#include "../gui/CGuiHandler.h"
+
+#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
+
+/*
+ * TextControls.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+std::string CLabel::visibleText()
+{
+	return text;
+}
+
+void CLabel::showAll(SDL_Surface * to)
+{
+	CIntObject::showAll(to);
+
+	if(!visibleText().empty())
+		blitLine(to, pos, visibleText());
+
+}
+
+CLabel::CLabel(int x, int y, EFonts Font /*= FONT_SMALL*/, EAlignment Align, const SDL_Color &Color /*= Colors::WHITE*/, const std::string &Text /*= ""*/)
+:CTextContainer(Align, Font, Color), text(Text)
+{
+	type |= REDRAW_PARENT;
+	autoRedraw = true;
+	pos.x += x;
+	pos.y += y;
+	pos.w = pos.h = 0;
+	bg = nullptr;
+
+	if (alignment == TOPLEFT) // causes issues for MIDDLE
+	{
+		pos.w = graphics->fonts[font]->getStringWidth(visibleText().c_str());
+		pos.h = graphics->fonts[font]->getLineHeight();
+	}
+}
+
+Point CLabel::getBorderSize()
+{
+	return Point(0, 0);
+}
+
+std::string CLabel::getText()
+{
+	return text;
+}
+
+void CLabel::setText(const std::string &Txt)
+{
+	text = Txt;
+	if(autoRedraw)
+	{
+		if(bg || !parent)
+			redraw();
+		else
+			parent->redraw();
+	}
+}
+
+CMultiLineLabel::CMultiLineLabel(Rect position, EFonts Font, EAlignment Align, const SDL_Color &Color, const std::string &Text):
+    CLabel(position.x, position.y, Font, Align, Color, Text),
+    visibleSize(0, 0, position.w, position.h)
+{
+	pos.w = position.w;
+	pos.h = position.h;
+	splitText(Text);
+}
+
+void CMultiLineLabel::setVisibleSize(Rect visibleSize)
+{
+	this->visibleSize = visibleSize;
+	redraw();
+}
+
+void CMultiLineLabel::scrollTextBy(int distance)
+{
+	scrollTextTo(visibleSize.y + distance);
+}
+
+void CMultiLineLabel::scrollTextTo(int distance)
+{
+	Rect size = visibleSize;
+	size.y = distance;
+	setVisibleSize(size);
+}
+
+void CMultiLineLabel::setText(const std::string &Txt)
+{
+	splitText(Txt);
+	CLabel::setText(Txt);
+}
+
+void CTextContainer::blitLine(SDL_Surface *to, Rect destRect, std::string what)
+{
+	const IFont * f = graphics->fonts[font];
+	Point where = destRect.topLeft();
+
+	// input is rect in which given text should be placed
+	// calculate proper position for top-left corner of the text
+	if (alignment == TOPLEFT)
+	{
+		where.x += getBorderSize().x;
+		where.y += getBorderSize().y;
+	}
+
+	if (alignment == CENTER)
+	{
+		where.x += (int(destRect.w) - int(f->getStringWidth(what))) / 2;
+		where.y += (int(destRect.h) - int(f->getLineHeight())) / 2;
+	}
+
+	if (alignment == BOTTOMRIGHT)
+	{
+		where.x += getBorderSize().x + destRect.w - f->getStringWidth(what);
+		where.y += getBorderSize().y + destRect.h - f->getLineHeight();
+	}
+
+	size_t begin = 0;
+	std::string delimeters = "{}";
+	size_t currDelimeter = 0;
+
+	do
+	{
+		size_t end = what.find_first_of(delimeters[currDelimeter % 2], begin);
+		if (begin != end)
+		{
+			std::string toPrint = what.substr(begin, end - begin);
+
+			if (currDelimeter % 2) // Enclosed in {} text - set to yellow
+				f->renderTextLeft(to, toPrint, Colors::YELLOW, where);
+			else // Non-enclosed text, use default color
+				f->renderTextLeft(to, toPrint, color, where);
+			begin = end;
+
+			where.x += f->getStringWidth(toPrint);
+		}
+		currDelimeter++;
+	}
+	while (begin++ != std::string::npos);
+}
+
+CTextContainer::CTextContainer(EAlignment alignment, EFonts font, SDL_Color color):
+	alignment(alignment),
+	font(font),
+	color(color)
+{}
+
+void CMultiLineLabel::showAll(SDL_Surface * to)
+{
+	CIntObject::showAll(to);
+
+	const IFont * f = graphics->fonts[font];
+
+	// calculate which lines should be visible
+	int totalLines = lines.size();
+	int beginLine  = visibleSize.y;
+	int endLine    = getTextLocation().h + visibleSize.y;
+
+	if (beginLine < 0)
+		beginLine = 0;
+	else
+		beginLine /= f->getLineHeight();
+
+	if (endLine < 0)
+		endLine = 0;
+	else
+		endLine /= f->getLineHeight();
+	endLine++;
+
+	// and where they should be displayed
+	Point lineStart = getTextLocation().topLeft() - visibleSize + Point(0, beginLine * f->getLineHeight());
+	Point lineSize  = Point(getTextLocation().w, f->getLineHeight());
+
+	CSDL_Ext::CClipRectGuard guard(to, getTextLocation()); // to properly trim text that is too big to fit
+
+	for (int i = beginLine; i < std::min(totalLines, endLine); i++)
+	{
+		if (!lines[i].empty()) //non-empty line
+			blitLine(to, Rect(lineStart, lineSize), lines[i]);
+
+		lineStart.y += f->getLineHeight();
+	}
+}
+
+void CMultiLineLabel::splitText(const std::string &Txt)
+{
+	lines.clear();
+
+	const IFont * f = graphics->fonts[font];
+	int lineHeight =  f->getLineHeight();
+
+	lines = CMessage::breakText(Txt, pos.w, font);
+
+	 textSize.y = lineHeight * lines.size();
+	 textSize.x = 0;
+	for(const std::string &line : lines)
+		vstd::amax( textSize.x, f->getStringWidth(line.c_str()));
+	redraw();
+}
+
+Rect CMultiLineLabel::getTextLocation()
+{
+	// this method is needed for vertical alignment alignment of text
+	// when height of available text is smaller than height of widget
+	// in this case - we should add proper offset to display text at required position
+	if (pos.h <= textSize.y)
+		return pos;
+
+	Point textSize(pos.w, graphics->fonts[font]->getLineHeight() * lines.size());
+	Point textOffset(pos.w - textSize.x, pos.h - textSize.y);
+
+	switch(alignment)
+	{
+	case TOPLEFT:     return Rect(pos.topLeft(), textSize);
+	case CENTER:      return Rect(pos.topLeft() + textOffset / 2, textSize);
+	case BOTTOMRIGHT: return Rect(pos.topLeft() + textOffset, textSize);
+	}
+	assert(0);
+	return Rect();
+}
+
+CLabelGroup::CLabelGroup(EFonts Font, EAlignment Align, const SDL_Color &Color):
+	font(Font), align(Align), color(Color)
+{}
+
+void CLabelGroup::add(int x, int y, const std::string &text)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	new CLabel(x, y, font, align, color, text);
+}
+
+CTextBox::CTextBox(std::string Text, const Rect &rect, int SliderStyle, EFonts Font /*= FONT_SMALL*/, EAlignment Align /*= TOPLEFT*/, const SDL_Color &Color /*= Colors::WHITE*/):
+    sliderStyle(SliderStyle),
+    slider(nullptr)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	label = new CMultiLineLabel(rect, Font, Align, Color);
+
+	type |= REDRAW_PARENT;
+	pos.x += rect.x;
+	pos.y += rect.y;
+	pos.h = rect.h;
+	pos.w = rect.w;
+
+	assert(pos.w >= 40); //we need some space
+	setText(Text);
+}
+
+void CTextBox::sliderMoved(int to)
+{
+	label->scrollTextTo(to);
+}
+
+void CTextBox::resize(Point newSize)
+{
+	pos.w = newSize.x;
+	pos.h = newSize.y;
+	label->pos.w = pos.w;
+	label->pos.h = pos.h;
+	if (slider)
+		vstd::clear_pointer(slider); // will be recreated if needed later
+
+	setText(label->getText()); // force refresh
+}
+
+void CTextBox::setText(const std::string &text)
+{
+	label->setText(text);
+	if (label->textSize.y <= label->pos.h && slider)
+	{
+		// slider is no longer needed
+		vstd::clear_pointer(slider);
+		label->pos.w = pos.w;
+		label->setText(text);
+	}
+	else if (label->textSize.y > label->pos.h && !slider)
+	{
+		// create slider and update widget
+		label->pos.w = pos.w - 32;
+		label->setText(text);
+
+		OBJ_CONSTRUCTION_CAPTURING_ALL;
+		slider = new CSlider(pos.w - 32, 0, pos.h, boost::bind(&CTextBox::sliderMoved, this, _1),
+		                     label->pos.h, label->textSize.y, 0, false, sliderStyle);
+		slider->scrollStep = graphics->fonts[label->font]->getLineHeight();
+	}
+}
+
+void CGStatusBar::setText(const std::string & Text)
+{
+	if(!textLock)
+		CLabel::setText(Text);
+}
+
+void CGStatusBar::clear()
+{
+	setText("");
+}
+
+CGStatusBar::CGStatusBar(CPicture *BG, EFonts Font /*= FONT_SMALL*/, EAlignment Align /*= CENTER*/, const SDL_Color &Color /*= Colors::WHITE*/)
+: CLabel(BG->pos.x, BG->pos.y, Font, Align, Color, "")
+{
+	init();
+	bg = BG;
+	addChild(bg);
+	pos = bg->pos;
+	getBorderSize();
+    textLock = false;
+}
+
+CGStatusBar::CGStatusBar(int x, int y, std::string name/*="ADROLLVR.bmp"*/, int maxw/*=-1*/)
+: CLabel(x, y, FONT_SMALL, CENTER)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	init();
+	bg = new CPicture(name);
+	pos = bg->pos;
+	if((unsigned int)maxw < pos.w)
+	{
+		vstd::amin(pos.w, maxw);
+		bg->srcRect = new Rect(0, 0, maxw, pos.h);
+	}
+    textLock = false;
+}
+
+CGStatusBar::~CGStatusBar()
+{
+	GH.statusbar = oldStatusBar;
+}
+
+void CGStatusBar::show(SDL_Surface * to)
+{
+    showAll(to);
+}
+
+void CGStatusBar::init()
+{
+	oldStatusBar = GH.statusbar;
+	GH.statusbar = this;
+}
+
+Point CGStatusBar::getBorderSize()
+{
+	//Width of borders where text should not be printed
+	static const Point borderSize(5,1);
+
+	switch(alignment)
+	{
+	case TOPLEFT:     return Point(borderSize.x, borderSize.y);
+	case CENTER:      return Point(pos.w/2, pos.h/2);
+	case BOTTOMRIGHT: return Point(pos.w - borderSize.x, pos.h - borderSize.y);
+	}
+	assert(0);
+	return Point();
+}
+
+void CGStatusBar::lock(bool shouldLock)
+{
+    textLock = shouldLock;
+}
+
+CTextInput::CTextInput(const Rect &Pos, EFonts font, const CFunctionList<void(const std::string &)> &CB):
+    CLabel(Pos.x, Pos.y, font, CENTER),
+    cb(CB)
+{
+	type |= REDRAW_PARENT;
+	focus = false;
+	pos.h = Pos.h;
+	pos.w = Pos.w;
+	captureAllKeys = true;
+	bg = nullptr;
+	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
+	giveFocus();
+}
+
+CTextInput::CTextInput( const Rect &Pos, const Point &bgOffset, const std::string &bgName, const CFunctionList<void(const std::string &)> &CB )
+:cb(CB)
+{
+	focus = false;
+	pos += Pos;
+	captureAllKeys = true;
+	OBJ_CONSTRUCTION;
+	bg = new CPicture(bgName, bgOffset.x, bgOffset.y);
+	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
+	giveFocus();
+}
+
+CTextInput::CTextInput(const Rect &Pos, SDL_Surface *srf)
+{
+	focus = false;
+	pos += Pos;
+	captureAllKeys = true;
+	OBJ_CONSTRUCTION;
+	bg = new CPicture(Pos, 0, true);
+	Rect hlp = Pos;
+	if(srf)
+		CSDL_Ext::blitSurface(srf, &hlp, *bg, nullptr);
+	else
+		SDL_FillRect(*bg, nullptr, 0);
+	pos.w = bg->pos.w;
+	pos.h = bg->pos.h;
+	bg->pos = pos;
+	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
+	giveFocus();
+}
+
+void CTextInput::focusGot()
+{
+	CSDL_Ext::startTextInput(&pos);	
+}
+
+void CTextInput::focusLost()
+{
+	CSDL_Ext::stopTextInput();
+}
+
+
+std::string CTextInput::visibleText()
+{
+	return focus ? text + newText + "_" : text;
+}
+
+void CTextInput::clickLeft( tribool down, bool previousState )
+{
+	if(down && !focus)
+		giveFocus();
+}
+
+void CTextInput::keyPressed( const SDL_KeyboardEvent & key )
+{
+
+	if(!focus || key.state != SDL_PRESSED)
+		return;
+
+	if(key.keysym.sym == SDLK_TAB)
+	{
+		moveFocus();
+		GH.breakEventHandling();
+		return;
+	}
+
+	bool redrawNeeded = false;
+	#ifdef VCMI_SDL1
+	std::string oldText = text;
+	#endif // 0	
+	switch(key.keysym.sym)
+	{
+	case SDLK_DELETE: // have index > ' ' so it won't be filtered out by default section
+		return;
+	case SDLK_BACKSPACE:
+		if(!newText.empty())
+		{
+			Unicode::trimRight(newText);
+			redrawNeeded = true;
+		}
+		else if(!text.empty())
+		{
+			Unicode::trimRight(text);
+			redrawNeeded = true;
+		}			
+		break;
+	default:
+		#ifdef VCMI_SDL1
+		if (key.keysym.unicode < ' ')
+			return;
+		else
+		{
+			text += key.keysym.unicode; //TODO 16-/>8
+			redrawNeeded = true;
+		}			
+		#endif // 0
+		break;
+	}
+	#ifdef VCMI_SDL1
+	filters(text, oldText);
+	#endif // 0
+	if (redrawNeeded)
+	{
+		redraw();
+		cb(text);
+	}	
+}
+
+void CTextInput::setText( const std::string &nText, bool callCb )
+{
+	CLabel::setText(nText);
+	if(callCb)
+		cb(text);
+}
+
+bool CTextInput::captureThisEvent(const SDL_KeyboardEvent & key)
+{
+	if(key.keysym.sym == SDLK_RETURN || key.keysym.sym == SDLK_KP_ENTER)
+		return false;
+	
+	#ifdef VCMI_SDL1
+	//this should allow all non-printable keys to go through (for example arrows)
+	if (key.keysym.unicode < ' ')
+		return false;
+
+	return true;
+	#else
+	return false;
+	#endif
+}
+
+#ifndef VCMI_SDL1
+void CTextInput::textInputed(const SDL_TextInputEvent & event)
+{
+	if(!focus)
+		return;
+	std::string oldText = text;
+	
+	text += event.text;	
+	
+	filters(text,oldText);
+	if (text != oldText)
+	{
+		redraw();
+		cb(text);
+	}	
+	newText = "";
+}
+
+void CTextInput::textEdited(const SDL_TextEditingEvent & event)
+{
+	if(!focus)
+		return;
+		
+	newText = event.text;
+	redraw();
+	cb(text+newText);	
+}
+
+#endif
+
+
+void CTextInput::filenameFilter(std::string & text, const std::string &)
+{
+	static const std::string forbiddenChars = "<>:\"/\\|?*\r\n"; //if we are entering a filename, some special characters won't be allowed
+	size_t pos;
+	while ((pos = text.find_first_of(forbiddenChars)) != std::string::npos)
+		text.erase(pos, 1);
+}
+
+void CTextInput::numberFilter(std::string & text, const std::string & oldText, int minValue, int maxValue)
+{
+	assert(minValue < maxValue);
+
+	if (text.empty())
+		text = "0";
+
+	size_t pos = 0;
+	if (text[0] == '-') //allow '-' sign as first symbol only
+		pos++;
+
+	while (pos < text.size())
+	{
+		if (text[pos] < '0' || text[pos] > '9')
+		{
+			text = oldText;
+			return; //new text is not number.
+		}
+		pos++;
+	}
+	try
+	{
+		int value = boost::lexical_cast<int>(text);
+		if (value < minValue)
+			text = boost::lexical_cast<std::string>(minValue);
+		else if (value > maxValue)
+			text = boost::lexical_cast<std::string>(maxValue);
+	}
+	catch(boost::bad_lexical_cast &)
+	{
+		//Should never happen. Unless I missed some cases
+        logGlobal->warnStream() << "Warning: failed to convert "<< text << " to number!";
+		text = oldText;
+	}
+}
+
+CFocusable::CFocusable()
+{
+	focusables.push_back(this);
+}
+
+CFocusable::~CFocusable()
+{
+	if(inputWithFocus == this)
+	{
+		focusLost();
+		inputWithFocus = nullptr;
+	}	
+
+	focusables -= this;
+}
+void CFocusable::giveFocus()
+{
+	if(inputWithFocus)
+	{
+		inputWithFocus->focus = false;
+		inputWithFocus->focusLost();
+		inputWithFocus->redraw();
+	}
+
+	focus = true;
+	inputWithFocus = this;
+	focusGot();
+	redraw();	
+}
+
+void CFocusable::moveFocus()
+{
+	auto i = vstd::find(focusables, this),
+		ourIt = i;
+	for(i++; i != ourIt; i++)
+	{
+		if(i == focusables.end())
+			i = focusables.begin();
+
+		if((*i)->active)
+		{
+			(*i)->giveFocus();
+			break;;
+		}
+	}
+}

+ 189 - 0
client/widgets/TextControls.h

@@ -0,0 +1,189 @@
+#pragma once
+
+#include "../gui/CIntObject.h"
+#include "../gui/SDL_Extensions.h"
+#include "../../lib/FunctionList.h"
+
+/*
+ * TextControls.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+class CSlider;
+
+/// Base class for all text-related widgets.
+/// Controls text blitting-related options
+class CTextContainer : public virtual CIntObject
+{
+protected:
+	/// returns size of border, for left- or right-aligned text
+	virtual Point getBorderSize() = 0;
+	/// do actual blitting of line. Text "what" will be placed at "where" and aligned according to alignment
+	void blitLine(SDL_Surface * to, Rect where, std::string what);
+
+	CTextContainer(EAlignment alignment, EFonts font, SDL_Color color);
+
+public:
+	EAlignment alignment;
+	EFonts font;
+	SDL_Color color; // default font color. Can be overridden by placing "{}" into the string
+};
+
+/// Label which shows text
+class CLabel : public CTextContainer
+{
+protected:
+	Point getBorderSize() override;
+	virtual std::string visibleText();
+
+	CPicture *bg;
+public:
+
+	std::string text;
+	bool autoRedraw;  //whether control will redraw itself on setTxt
+
+	std::string getText();
+	virtual void setText(const std::string &Txt);
+
+	CLabel(int x=0, int y=0, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT,
+	       const SDL_Color &Color = Colors::WHITE, const std::string &Text =  "");
+	void showAll(SDL_Surface * to); //shows statusbar (with current text)
+};
+
+/// Small helper class to manage group of similar labels
+class CLabelGroup : public CIntObject
+{
+	std::list<CLabel*> labels;
+	EFonts font;
+	EAlignment align;
+	SDL_Color color;
+public:
+	CLabelGroup(EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE);
+	void add(int x=0, int y=0, const std::string &text =  "");
+};
+
+/// Multi-line label that can display multiple lines of text
+/// If text is too big to fit into requested area remaining part will not be visible
+class CMultiLineLabel : public CLabel
+{
+	// text to blit, split into lines that are no longer than widget width
+	std::vector<std::string> lines;
+
+	// area of text that actually will be printed, default is widget size
+	Rect visibleSize;
+
+	void splitText(const std::string &Txt);
+	Rect getTextLocation();
+public:
+	// total size of text, x = longest line of text, y = total height of lines
+	Point textSize;
+
+	CMultiLineLabel(Rect position, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE, const std::string &Text =  "");
+
+	void setText(const std::string &Txt);
+	void showAll(SDL_Surface * to);
+
+	void setVisibleSize(Rect visibleSize);
+	// scrolls text visible in widget. Positive value will move text up
+	void scrollTextTo(int distance);
+	void scrollTextBy(int distance);
+};
+
+/// a multi-line label that tries to fit text with given available width and height;
+/// if not possible, it creates a slider for scrolling text
+class CTextBox : public CIntObject
+{
+	int sliderStyle;
+public:
+	CMultiLineLabel * label;
+	CSlider *slider;
+
+	CTextBox(std::string Text, const Rect &rect, int SliderStyle, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE);
+
+	void resize(Point newSize);
+	void setText(const std::string &Txt);
+	void sliderMoved(int to);
+};
+
+/// Status bar which is shown at the bottom of the in-game screens
+class CGStatusBar : public CLabel
+{
+	bool textLock; //Used for blocking changes to the text
+	void init();
+
+	CGStatusBar *oldStatusBar;
+protected:
+	Point getBorderSize() override;
+
+public:
+
+	void clear();//clears statusbar and refreshes
+	void setText(const std::string & Text) override; //prints text and refreshes statusbar
+
+	void show(SDL_Surface * to); //shows statusbar (with current text)
+
+	CGStatusBar(CPicture *BG, EFonts Font = FONT_SMALL, EAlignment Align = CENTER, const SDL_Color &Color = Colors::WHITE); //given CPicture will be captured by created sbar and it's pos will be used as pos for sbar
+	CGStatusBar(int x, int y, std::string name, int maxw=-1);
+	~CGStatusBar();
+
+	void lock(bool shouldLock); //If true, current text cannot be changed until lock(false) is called
+};
+
+/// UIElement which can get input focus
+class CFocusable : public virtual CIntObject
+{
+protected:
+	virtual void focusGot(){};
+	virtual void focusLost(){};
+public:
+	bool focus; //only one focusable control can have focus at one moment
+
+	void giveFocus(); //captures focus
+	void moveFocus(); //moves focus to next active control (may be used for tab switching)
+
+	static std::list<CFocusable*> focusables; //all existing objs
+	static CFocusable *inputWithFocus; //who has focus now
+	CFocusable();
+	~CFocusable();
+};
+
+/// Text input box where players can enter text
+class CTextInput : public CLabel, public CFocusable
+{
+	std::string newText;
+protected:
+	std::string visibleText() override;
+
+	void focusGot() override;
+	void focusLost() override;
+public:
+	CFunctionList<void(const std::string &)> cb;
+	CFunctionList<void(std::string &, const std::string &)> filters;
+	void setText(const std::string &nText, bool callCb = false);
+
+	CTextInput(const Rect &Pos, EFonts font, const CFunctionList<void(const std::string &)> &CB);
+	CTextInput(const Rect &Pos, const Point &bgOffset, const std::string &bgName, const CFunctionList<void(const std::string &)> &CB);
+	CTextInput(const Rect &Pos, SDL_Surface *srf = nullptr);
+
+	void clickLeft(tribool down, bool previousState) override;
+	void keyPressed(const SDL_KeyboardEvent & key) override;
+	bool captureThisEvent(const SDL_KeyboardEvent & key) override;
+	
+#ifndef VCMI_SDL1
+	void textInputed(const SDL_TextInputEvent & event) override;
+	void textEdited(const SDL_TextEditingEvent & event) override;
+	
+	
+#endif // VCMI_SDL1	
+
+	//Filter that will block all characters not allowed in filenames
+	static void filenameFilter(std::string &text, const std::string & oldText);
+	//Filter that will allow only input of numbers in range min-max (min-max are allowed)
+	//min-max should be set via something like boost::bind
+	static void numberFilter(std::string &text, const std::string & oldText, int minValue, int maxValue);
+};

+ 36 - 1
client/windows/CAdvmapInterface.cpp

@@ -21,8 +21,8 @@
 #include "../gui/CCursorHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/SDL_Extensions.h"
-#include "../widgets/CIntObjectClasses.h"
 #include "../widgets/MiscWidgets.h"
+#include "../windows/InfoWindows.h"
 
 #include "../../CCallback.h"
 
@@ -1530,3 +1530,38 @@ void CAdvMapInt::adjustActiveness(bool aiTurnStart)
 	if(wasActive) 
 		activate();
 }
+
+CAdventureOptions::CAdventureOptions():
+	CWindowObject(PLAYER_COLORED, "ADVOPTS")
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	exit = new CAdventureMapButton("","",boost::bind(&CAdventureOptions::close, this), 204, 313, "IOK6432.DEF",SDLK_RETURN);
+	exit->assignedKeys.insert(SDLK_ESCAPE);
+
+	scenInfo = new CAdventureMapButton("","", boost::bind(&CAdventureOptions::close, this), 24, 198, "ADVINFO.DEF",SDLK_i);
+	scenInfo->callback += CAdventureOptions::showScenarioInfo;
+	//viewWorld = new CAdventureMapButton("","",boost::bind(&CGuiHandler::popIntTotally, &GH, this), 204, 313, "IOK6432.DEF",SDLK_RETURN);
+
+	puzzle = new CAdventureMapButton("","", boost::bind(&CAdventureOptions::close, this), 24, 81, "ADVPUZ.DEF");
+	puzzle->callback += boost::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT);
+
+	dig = new CAdventureMapButton("","", boost::bind(&CAdventureOptions::close, this), 24, 139, "ADVDIG.DEF");
+	if(const CGHeroInstance *h = adventureInt->curHero())
+		dig->callback += boost::bind(&CPlayerInterface::tryDiggging, LOCPLINT, h);
+	else
+		dig->block(true);
+}
+
+void CAdventureOptions::showScenarioInfo()
+{
+	auto campState = LOCPLINT->cb->getStartInfo()->campState;
+	if(campState)
+	{
+		GH.pushInt(new CBonusSelection(campState));
+	}
+	else
+	{
+		GH.pushInt(new CScenarioInfo(LOCPLINT->cb->getMapHeader(), LOCPLINT->cb->getStartInfo()));
+	}
+}

+ 14 - 0
client/windows/CAdvmapInterface.h

@@ -1,6 +1,10 @@
 #pragma once
 
 #include "../widgets/AdventureMapClasses.h"
+#include "CWindowObject.h"
+
+#include "../widgets/TextControls.h"
+#include "../widgets/Buttons.h"
 
 class CDefHandler;
 class CCallback;
@@ -24,6 +28,16 @@ class IShipyard;
  *
  */
 
+/// Adventure options dialogue where you can view the world, dig, play the replay of the last turn,...
+class CAdventureOptions : public CWindowObject
+{
+public:
+	CAdventureMapButton *exit, *viewWorld, *puzzle, *dig, *scenInfo, *replay;
+
+	CAdventureOptions();
+	static void showScenarioInfo();
+};
+
 /// Holds information about which tiles of the terrain are shown/not shown at the screen
 class CTerrainRect
 	:  public CIntObject

+ 3 - 2
client/windows/CCastleInterface.cpp

@@ -16,9 +16,9 @@
 
 #include "../gui/CGuiHandler.h"
 #include "../gui/SDL_Extensions.h"
-#include "../widgets/CAnimation.h"
-#include "../widgets/CIntObjectClasses.h"
+#include "../windows/InfoWindows.h"
 #include "../widgets/MiscWidgets.h"
+#include "../widgets/CComponent.h"
 
 #include "../../CCallback.h"
 #include "../../lib/CArtHandler.h"
@@ -30,6 +30,7 @@
 #include "../../lib/CTownHandler.h"
 #include "../../lib/GameConstants.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
 
 using namespace boost::assign;
 

+ 1 - 1
client/windows/CCastleInterface.h

@@ -1,7 +1,7 @@
 #pragma once
 
-#include "../widgets/CAnimation.h"
 #include "../widgets/CGarrisonInt.h"
+#include "../widgets/Images.h"
 
 class CAdventureMapButton;
 class CBuilding;

+ 6 - 2
client/windows/CCreatureWindow.cpp

@@ -3,9 +3,12 @@
 
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
+#include "../widgets/Buttons.h"
+#include "../widgets/CComponent.h"
+#include "../widgets/Images.h"
+#include "../widgets/TextControls.h"
+#include "../widgets/ObjectLists.h"
 #include "../gui/CGuiHandler.h"
-#include "../widgets/CIntObjectClasses.h"
-#include "../widgets/CAnimation.h"
 
 #include "../../CCallback.h"
 #include "../../lib/BattleState.h"
@@ -14,6 +17,7 @@
 #include "../../lib/CModHandler.h"
 #include "../../lib/CHeroHandler.h"
 #include "../../lib/CSpellHandler.h"
+#include "../../lib/CGameState.h"
 
 using namespace CSDL_Ext;
 

+ 3 - 1
client/windows/CCreatureWindow.h

@@ -2,6 +2,7 @@
 
 #include "../../lib/HeroBonus.h"
 #include "../widgets/MiscWidgets.h"
+#include "CWindowObject.h"
 
 /*
  * CCreatureWindow.h, part of VCMI engine
@@ -18,7 +19,8 @@ class CCommanderInstance;
 class CStackInstance;
 class CStack;
 struct UpgradeInfo;
-class LRClickableAreaWTextComp;
+class CTabbedInt;
+class CAdventureMapButton;
 
 class CClickableObject : public LRClickableAreaWText
 {

+ 1 - 2
client/windows/CHeroWindow.cpp

@@ -17,9 +17,8 @@
 
 #include "../gui/SDL_Extensions.h"
 #include "../gui/CGuiHandler.h"
-#include "../widgets/CAnimation.h"
-#include "../widgets/CIntObjectClasses.h"
 #include "../widgets/MiscWidgets.h"
+#include "../widgets/CComponent.h"
 
 #include "../../CCallback.h"
 

+ 3 - 0
client/windows/CHeroWindow.h

@@ -25,6 +25,9 @@ class LRClickableAreaWText;
 class LRClickableAreaWTextComp;
 class CArtifactsOfHero;
 class MoraleLuckBox;
+class CHighlightableButton;
+class CHighlightableButtonsGroup;
+class CGStatusBar;
 
 /// Button which switches hero selection
 class CHeroSwitcher : public CIntObject

+ 4 - 2
client/windows/CKingdomInterface.cpp

@@ -8,9 +8,9 @@
 #include "../CMT.h"
 #include "../CPlayerInterface.h"
 #include "../gui/CGuiHandler.h"
-#include "../widgets/CAnimation.h"
-#include "../widgets/CIntObjectClasses.h"
+#include "../widgets/CComponent.h"
 #include "../widgets/MiscWidgets.h"
+#include "../windows/InfoWindows.h"
 
 #include "../../CCallback.h"
 
@@ -21,6 +21,8 @@
 #include "../../lib/CModHandler.h"
 #include "../../lib/CTownHandler.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
+#include "../../lib/mapObjects/MiscObjects.h"
 
 /*
  * CKingdomInterface.cpp, part of VCMI engine

+ 3 - 0
client/windows/CKingdomInterface.h

@@ -15,6 +15,9 @@ class LRClickableAreaOpenTown;
 class CComponent;
 class CHeroArea;
 class MoraleLuckBox;
+class CListBox;
+class CTabbedInt;
+class CGStatusBar;
 
 /*
  * CKingdomInterface.h, part of VCMI engine

+ 0 - 1
client/windows/CQuestLog.cpp

@@ -11,7 +11,6 @@
 
 #include "../gui/CGuiHandler.h"
 #include "../gui/SDL_Extensions.h"
-#include "../widgets/CIntObjectClasses.h"
 
 #include "../../CCallback.h"
 #include "../../lib/CArtHandler.h"

+ 4 - 2
client/windows/CQuestLog.h

@@ -1,8 +1,10 @@
 #pragma once
 
-#include "../widgets/CIntObjectClasses.h"
-#include "../widgets/CAnimation.h"
 #include "../widgets/AdventureMapClasses.h"
+#include "../widgets/TextControls.h"
+#include "../widgets/MiscWidgets.h"
+#include "../widgets/Images.h"
+#include "CWindowObject.h"
 
 /*
  * CQuestLog.h, part of VCMI engine

+ 5 - 0
client/windows/CSpellWindow.cpp

@@ -3,6 +3,7 @@
 
 #include "CAdvmapInterface.h"
 #include "GUIClasses.h"
+#include "InfoWindows.h"
 
 #include "../CBitmapHandler.h"
 #include "../CDefHandler.h"
@@ -14,9 +15,11 @@
 #include "../Graphics.h"
 
 #include "../battle/CBattleInterface.h"
+#include "../gui/CAnimation.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/SDL_Extensions.h"
 #include "../widgets/MiscWidgets.h"
+#include "../widgets/CComponent.h"
 
 #include "../../CCallback.h"
 
@@ -26,6 +29,8 @@
 #include "../../lib/CHeroHandler.h"
 #include "../../lib/CSpellHandler.h"
 #include "../../lib/GameConstants.h"
+#include "../../lib/CGameState.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
 
 /*
  * CSpellWindow.cpp, part of VCMI engine

+ 1 - 1
client/windows/CSpellWindow.h

@@ -1,6 +1,6 @@
 #pragma once
 
-#include "../widgets/CIntObjectClasses.h"
+#include "CWindowObject.h"
 
 /*
  * CSpellWindow.h, part of VCMI engine

+ 4 - 1
client/windows/CTradeWindow.cpp

@@ -5,7 +5,7 @@
 
 #include "../gui/CGuiHandler.h"
 #include "../gui/CCursorHandler.h"
-#include "../widgets/CAnimation.h"
+#include "../widgets/Images.h"
 
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
@@ -17,7 +17,10 @@
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/CHeroHandler.h"
+#include "../../lib/CGameState.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
+#include "../../lib/mapObjects/CGMarket.h"
 
 
 /*

+ 4 - 0
client/windows/CTradeWindow.h

@@ -1,6 +1,8 @@
 #pragma once
 
 #include "../widgets/CArtifactHolder.h"
+#include "CWindowObject.h"
+#include "../../lib/FunctionList.h"
 
 /*
  * CTradeWindow.h, part of VCMI engine
@@ -13,6 +15,8 @@
  */
 
 class IMarket;
+class CSlider;
+class CTextBox;
 
 class CTradeWindow : public CWindowObject, public CWindowWithArtifacts //base for markets and altar of sacrifice
 {

+ 242 - 0
client/windows/CWindowObject.cpp

@@ -0,0 +1,242 @@
+#include "StdInc.h"
+#include "CWindowObject.h"
+
+#include "../widgets/MiscWidgets.h"
+
+#include "../gui/SDL_Pixels.h"
+#include "../gui/SDL_Extensions.h"
+#include "../gui/CGuiHandler.h"
+#include "../gui/CCursorHandler.h"
+
+#include "../battle/CBattleInterface.h"
+#include "../battle/CBattleInterfaceClasses.h"
+
+#include "../CBitmapHandler.h"
+#include "../Graphics.h"
+#include "../CGameInfo.h"
+#include "../CPlayerInterface.h"
+#include "../CMessage.h"
+#include "../CMusicHandler.h"
+#include "../windows/CAdvmapInterface.h"
+
+#include "../../CCallback.h"
+
+#include "../../lib/CConfigHandler.h"
+#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
+
+/*
+ * CWindowObject.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+CWindowObject::CWindowObject(int options_, std::string imageName, Point centerAt):
+    CIntObject(getUsedEvents(options_), Point()),
+    shadow(nullptr),
+    options(options_),
+    background(createBg(imageName, options & PLAYER_COLORED))
+{
+	assert(parent == nullptr); //Safe to remove, but windows should not have parent
+
+	if (options & RCLICK_POPUP)
+		CCS->curh->hide();
+
+	if (background)
+		pos = background->center(centerAt);
+	else
+		center(centerAt);
+
+	if (!(options & SHADOW_DISABLED))
+		setShadow(true);
+}
+
+CWindowObject::CWindowObject(int options_, std::string imageName):
+    CIntObject(getUsedEvents(options_), Point()),
+    shadow(nullptr),
+    options(options_),
+    background(createBg(imageName, options & PLAYER_COLORED))
+{
+	assert(parent == nullptr); //Safe to remove, but windows should not have parent
+
+	if (options & RCLICK_POPUP)
+		CCS->curh->hide();
+
+	if (background)
+		pos = background->center();
+	else
+		center(Point(screen->w/2, screen->h/2));
+
+	if (!(options & SHADOW_DISABLED))
+		setShadow(true);
+}
+
+CWindowObject::~CWindowObject()
+{
+	setShadow(false);
+}
+
+CPicture * CWindowObject::createBg(std::string imageName, bool playerColored)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	if (imageName.empty())
+		return nullptr;
+
+	auto  image = new CPicture(imageName);
+	if (playerColored)
+		image->colorize(LOCPLINT->playerID);
+	return image;
+}
+
+void CWindowObject::setBackground(std::string filename)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	delete background;
+	background = createBg(filename, options & PLAYER_COLORED);
+
+	if (background)
+		pos = background->center(Point(pos.w/2 + pos.x, pos.h/2 + pos.y));
+
+	updateShadow();
+}
+
+int CWindowObject::getUsedEvents(int options)
+{
+	if (options & RCLICK_POPUP)
+		return RCLICK;
+	return 0;
+}
+
+void CWindowObject::updateShadow()
+{
+	setShadow(false);
+	if (!(options & SHADOW_DISABLED))
+		setShadow(true);
+}
+
+void CWindowObject::setShadow(bool on)
+{
+	//size of shadow
+	static const int size = 8;
+
+	if (on == bool(shadow))
+		return;
+
+	vstd::clear_pointer(shadow);
+
+	//object too small to cast shadow
+	if (pos.h <= size || pos.w <= size)
+		return;
+
+	if (on)
+	{
+
+		//helper to set last row
+		auto blitAlphaRow = [](SDL_Surface *surf, size_t row)
+		{
+			Uint8 * ptr = (Uint8*)surf->pixels + surf->pitch * (row);
+
+			for (size_t i=0; i< surf->w; i++)
+			{
+				Channels::px<4>::a.set(ptr, 128);
+				ptr+=4;
+			}
+		};
+
+		// helper to set last column
+		auto blitAlphaCol = [](SDL_Surface *surf, size_t col)
+		{
+			Uint8 * ptr = (Uint8*)surf->pixels + 4 * (col);
+
+			for (size_t i=0; i< surf->h; i++)
+			{
+				Channels::px<4>::a.set(ptr, 128);
+				ptr+= surf->pitch;
+			}
+		};
+
+		static SDL_Surface * shadowCornerTempl = nullptr;
+		static SDL_Surface * shadowBottomTempl = nullptr;
+		static SDL_Surface * shadowRightTempl = nullptr;
+
+		//one-time initialization
+		if (!shadowCornerTempl)
+		{
+			//create "template" surfaces
+			shadowCornerTempl = CSDL_Ext::createSurfaceWithBpp<4>(size, size);
+			shadowBottomTempl = CSDL_Ext::createSurfaceWithBpp<4>(1, size);
+			shadowRightTempl  = CSDL_Ext::createSurfaceWithBpp<4>(size, 1);
+
+			Uint32 shadowColor = SDL_MapRGBA(shadowCornerTempl->format, 0, 0, 0, 192);
+
+			//fill with shadow body color
+			SDL_FillRect(shadowCornerTempl, nullptr, shadowColor);
+			SDL_FillRect(shadowBottomTempl, nullptr, shadowColor);
+			SDL_FillRect(shadowRightTempl,  nullptr, shadowColor);
+
+			//fill last row and column with more transparent color
+			blitAlphaCol(shadowRightTempl , size-1);
+			blitAlphaCol(shadowCornerTempl, size-1);
+			blitAlphaRow(shadowBottomTempl, size-1);
+			blitAlphaRow(shadowCornerTempl, size-1);
+		}
+
+		OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+		//FIXME: do something with this points
+		Point shadowStart;
+		if (options & BORDERED)
+			shadowStart = Point(size - 14, size - 14);
+		else
+			shadowStart = Point(size, size);
+
+		Point shadowPos;
+		if (options & BORDERED)
+			shadowPos = Point(pos.w + 14, pos.h + 14);
+		else
+			shadowPos = Point(pos.w, pos.h);
+
+		Point fullsize;
+		if (options & BORDERED)
+			fullsize = Point(pos.w + 28, pos.h + 29);
+		else
+			fullsize = Point(pos.w, pos.h);
+
+		//create base 8x8 piece of shadow
+		SDL_Surface * shadowCorner = CSDL_Ext::copySurface(shadowCornerTempl);
+		SDL_Surface * shadowBottom = CSDL_Ext::scaleSurfaceFast(shadowBottomTempl, fullsize.x - size, size);
+		SDL_Surface * shadowRight  = CSDL_Ext::scaleSurfaceFast(shadowRightTempl,  size, fullsize.y - size);
+
+		blitAlphaCol(shadowBottom, 0);
+		blitAlphaRow(shadowRight, 0);
+
+		//generate "shadow" object with these 3 pieces in it
+		shadow = new CIntObject;
+		shadow->addChild(new CPicture(shadowCorner, shadowPos.x, shadowPos.y));
+		shadow->addChild(new CPicture(shadowRight,  shadowPos.x, shadowStart.y));
+		shadow->addChild(new CPicture(shadowBottom, shadowStart.x, shadowPos.y));
+	}
+}
+
+void CWindowObject::showAll(SDL_Surface *to)
+{
+	CIntObject::showAll(to);
+	if ((options & BORDERED) && (pos.h != to->h || pos.w != to->w))
+		CMessage::drawBorder(LOCPLINT ? LOCPLINT->playerID : PlayerColor(1), to, pos.w+28, pos.h+29, pos.x-14, pos.y-15);
+}
+
+void CWindowObject::close()
+{
+	GH.popIntTotally(this);
+}
+
+void CWindowObject::clickRight(tribool down, bool previousState)
+{
+	close();
+	CCS->curh->show();
+}

+ 65 - 0
client/windows/CWindowObject.h

@@ -0,0 +1,65 @@
+#pragma once
+
+#include "../gui/CIntObject.h"
+//#include "../gui/SDL_Extensions.h"
+
+//#include "../../lib/FunctionList.h"
+
+struct SDL_Surface;
+struct Rect;
+class CAnimImage;
+class CLabel;
+class CAnimation;
+class CDefHandler;
+
+/*
+ * CWindowObject.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+/// Basic class for windows
+class CWindowObject : public CIntObject
+{
+	CPicture * createBg(std::string imageName, bool playerColored);
+	int getUsedEvents(int options);
+
+	CIntObject *shadow;
+	void setShadow(bool on);
+
+	int options;
+
+protected:
+	CPicture * background;
+
+	//Simple function with call to GH.popInt
+	void close();
+	//Used only if RCLICK_POPUP was set
+	void clickRight(tribool down, bool previousState);
+	//To display border
+	void showAll(SDL_Surface *to);
+	//change or set background image
+	void setBackground(std::string filename);
+	void updateShadow();
+public:
+	enum EOptions
+	{
+		PLAYER_COLORED=1, //background will be player-colored
+		RCLICK_POPUP=2, // window will behave as right-click popup
+		BORDERED=4, // window will have border if current resolution is bigger than size of window
+		SHADOW_DISABLED=8 //this window won't display any shadow
+	};
+
+	/*
+	 * options - EOpions enum
+	 * imageName - name for background image, can be empty
+	 * centerAt - position of window center. Default - center of the screen
+	*/
+	CWindowObject(int options, std::string imageName, Point centerAt);
+	CWindowObject(int options, std::string imageName = "");
+	~CWindowObject();
+};

+ 2 - 1
client/windows/GUIClasses.cpp

@@ -26,8 +26,9 @@
 #include "../gui/SDL_Extensions.h"
 #include "../gui/CCursorHandler.h"
 
-#include "../widgets/CAnimation.h"
+#include "../widgets/CComponent.h"
 #include "../widgets/MiscWidgets.h"
+#include "../windows/InfoWindows.h"
 
 #include "../../CCallback.h"
 

+ 9 - 2
client/windows/GUIClasses.h

@@ -5,7 +5,8 @@
 #include "../lib/CConfigHandler.h"
 #include "../widgets/CArtifactHolder.h"
 #include "../widgets/CGarrisonInt.h"
-#include "../widgets/CAnimation.h"
+#include "../widgets/Images.h"
+#include "../windows/CWindowObject.h"
 
 /*
  * GUIClasses.h, part of VCMI engine
@@ -23,8 +24,14 @@ class CCreaturePic;
 class MoraleLuckBox;
 class CHeroArea;
 class CMinorResDataBar;
-
+class CSlider;
 class CComponentBox;
+class CTextInput;
+class CListBox;
+class CLabelGroup;
+class CHighlightableButton;
+class CHighlightableButtonsGroup;
+class CGStatusBar;
 
 /// Recruitment window where you can recruit creatures
 class CRecruitmentWindow : public CWindowObject

+ 456 - 0
client/windows/InfoWindows.cpp

@@ -0,0 +1,456 @@
+#include "StdInc.h"
+#include "InfoWindows.h"
+
+#include "../CBitmapHandler.h"
+#include "../Graphics.h"
+#include "../CGameInfo.h"
+#include "../CPlayerInterface.h"
+#include "../CMessage.h"
+#include "../CMusicHandler.h"
+
+#include "../windows/CAdvmapInterface.h"
+#include "../widgets/CComponent.h"
+#include "../widgets/MiscWidgets.h"
+
+#include "../gui/SDL_Pixels.h"
+#include "../gui/SDL_Extensions.h"
+#include "../gui/CGuiHandler.h"
+#include "../gui/CCursorHandler.h"
+
+#include "../battle/CBattleInterface.h"
+#include "../battle/CBattleInterfaceClasses.h"
+
+#include "../../CCallback.h"
+
+#include "../../lib/CGameState.h"
+#include "../../lib/CConfigHandler.h"
+#include "../../lib/CondSh.h"
+#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
+#include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
+#include "../../lib/mapObjects/MiscObjects.h"
+
+/*
+ * InfoWindows.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+void CSimpleWindow::show(SDL_Surface * to)
+{
+	if(bitmap)
+		blitAt(bitmap,pos.x,pos.y,to);
+}
+CSimpleWindow::~CSimpleWindow()
+{
+	if (bitmap)
+	{
+		SDL_FreeSurface(bitmap);
+		bitmap=nullptr;
+	}
+}
+
+void CSelWindow::selectionChange(unsigned to)
+{
+	for (unsigned i=0;i<components.size();i++)
+	{
+		CSelectableComponent * pom = dynamic_cast<CSelectableComponent*>(components[i]);
+		if (!pom)
+			continue;
+		pom->select(i==to);
+	}
+	redraw();
+}
+
+CSelWindow::CSelWindow(const std::string &Text, PlayerColor player, int charperline, const std::vector<CSelectableComponent*> &comps, const std::vector<std::pair<std::string,CFunctionList<void()> > > &Buttons, QueryID askID)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	ID = askID;
+	for(int i=0;i<Buttons.size();i++)
+	{
+		buttons.push_back(new CAdventureMapButton("","",Buttons[i].second,0,0,Buttons[i].first));
+		if(!i  &&  askID.getNum() >= 0)
+			buttons.back()->callback += boost::bind(&CSelWindow::madeChoice,this);
+		buttons[i]->callback += boost::bind(&CInfoWindow::close,this); //each button will close the window apart from call-defined actions
+	}
+
+	text = new CTextBox(Text, Rect(0, 0, 250, 100), 0, FONT_MEDIUM, CENTER, Colors::WHITE);
+
+	buttons.front()->assignedKeys.insert(SDLK_RETURN); //first button - reacts on enter
+	buttons.back()->assignedKeys.insert(SDLK_ESCAPE); //last button - reacts on escape
+
+	if(buttons.size() > 1  &&  askID.getNum() >= 0) //cancel button functionality
+		buttons.back()->callback += boost::bind(&CCallback::selectionMade,LOCPLINT->cb.get(),0,askID);
+
+	for(int i=0;i<comps.size();i++)
+	{
+		comps[i]->recActions = 255;
+		addChild(comps[i]);
+		components.push_back(comps[i]);
+		comps[i]->onSelect = boost::bind(&CSelWindow::selectionChange,this,i);
+		if(i<9)
+			comps[i]->assignedKeys.insert(SDLK_1+i);
+	}
+	CMessage::drawIWindow(this, Text, player);
+}
+
+void CSelWindow::madeChoice()
+{
+	if(ID.getNum() < 0)
+		return;
+	int ret = -1;
+	for (int i=0;i<components.size();i++)
+	{
+		if(dynamic_cast<CSelectableComponent*>(components[i])->selected)
+		{
+			ret = i;
+		}
+	}
+	LOCPLINT->cb->selectionMade(ret+1,ID);
+}
+
+CInfoWindow::CInfoWindow(std::string Text, PlayerColor player, const TCompsInfo &comps, const TButtonsInfo &Buttons, bool delComps)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	type |= BLOCK_ADV_HOTKEYS;
+	ID = QueryID(-1);
+	for(auto & Button : Buttons)
+	{
+		CAdventureMapButton *button = new CAdventureMapButton("","",boost::bind(&CInfoWindow::close,this),0,0,Button.first);
+		button->borderColor = Colors::METALLIC_GOLD;
+		button->borderEnabled = true;
+		button->callback.add(Button.second); //each button will close the window apart from call-defined actions
+		buttons.push_back(button);
+	}
+
+	text = new CTextBox(Text, Rect(0, 0, 250, 100), 0, FONT_MEDIUM, CENTER, Colors::WHITE);
+	if(!text->slider)
+	{
+		text->resize(text->label->textSize);
+	}
+
+	if(buttons.size())
+	{
+		buttons.front()->assignedKeys.insert(SDLK_RETURN); //first button - reacts on enter
+		buttons.back()->assignedKeys.insert(SDLK_ESCAPE); //last button - reacts on escape
+	}
+
+	for(auto & comp : comps)
+	{
+		comp->recActions = 0xff;
+		addChild(comp);
+		comp->recActions &= ~(SHOWALL | UPDATE);
+		components.push_back(comp);
+	}
+	setDelComps(delComps);
+	CMessage::drawIWindow(this,Text,player);
+}
+
+CInfoWindow::CInfoWindow()
+{
+	ID = QueryID(-1);
+	setDelComps(false);
+	text = nullptr;
+}
+
+void CInfoWindow::close()
+{
+	GH.popIntTotally(this);
+	if(LOCPLINT)
+		LOCPLINT->showingDialog->setn(false);
+}
+
+void CInfoWindow::show(SDL_Surface * to)
+{
+	CIntObject::show(to);
+}
+
+CInfoWindow::~CInfoWindow()
+{
+	if(!delComps)
+	{
+		for (auto & elem : components)
+			removeChild(elem);
+	}
+}
+
+void CInfoWindow::showAll(SDL_Surface * to)
+{
+	CSimpleWindow::show(to);
+	CIntObject::showAll(to);
+}
+
+void CInfoWindow::showInfoDialog(const std::string &text, const std::vector<CComponent *> *components, bool DelComps, PlayerColor player)
+{
+	CInfoWindow * window = CInfoWindow::create(text, player, components, DelComps);
+	GH.pushInt(window);
+}
+
+void CInfoWindow::showYesNoDialog(const std::string & text, const std::vector<CComponent*> *components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, bool DelComps, PlayerColor player)
+{
+	assert(!LOCPLINT || LOCPLINT->showingDialog->get());
+	std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
+	pom.push_back(std::pair<std::string,CFunctionList<void()> >("IOKAY.DEF",0));
+	pom.push_back(std::pair<std::string,CFunctionList<void()> >("ICANCEL.DEF",0));
+	CInfoWindow * temp = new CInfoWindow(text, player, components ? *components : std::vector<CComponent*>(), pom, DelComps);
+	for(auto & elem : onYes.funcs)
+		temp->buttons[0]->callback += elem;
+	for(auto & elem : onNo.funcs)
+		temp->buttons[1]->callback += elem;
+
+	GH.pushInt(temp);
+}
+
+void CInfoWindow::showOkDialog(const std::string & text, const std::vector<CComponent*> *components, const boost::function<void()> & onOk, bool delComps, PlayerColor player)
+{
+	std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
+	pom.push_back(std::pair<std::string,CFunctionList<void()> >("IOKAY.DEF",0));
+	CInfoWindow * temp = new CInfoWindow(text, player, *components, pom, delComps);
+	temp->buttons[0]->callback += onOk;
+
+	GH.pushInt(temp);
+}
+
+CInfoWindow * CInfoWindow::create(const std::string &text, PlayerColor playerID /*= 1*/, const std::vector<CComponent*> *components /*= nullptr*/, bool DelComps)
+{
+	std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
+	pom.push_back(std::pair<std::string,CFunctionList<void()> >("IOKAY.DEF",0));
+	CInfoWindow * ret = new CInfoWindow(text, playerID, components ? *components : std::vector<CComponent*>(), pom, DelComps);
+	return ret;
+}
+
+std::string CInfoWindow::genText(std::string title, std::string description)
+{
+	return std::string("{") + title + "}" + "\n\n" + description;
+}
+
+void CInfoWindow::setDelComps(bool DelComps)
+{
+	delComps = DelComps;
+	for(CComponent *comp : components)
+	{
+		if(delComps)
+			comp->recActions |= DISPOSE;
+		else
+			comp->recActions &= ~DISPOSE;
+	}
+}
+
+CInfoPopup::CInfoPopup(SDL_Surface * Bitmap, int x, int y, bool Free)
+ :free(Free),bitmap(Bitmap)
+{
+	init(x, y);
+}
+
+
+CInfoPopup::CInfoPopup(SDL_Surface * Bitmap, const Point &p, EAlignment alignment, bool Free/*=false*/)
+ : free(Free),bitmap(Bitmap)
+{
+	switch(alignment)
+	{
+	case BOTTOMRIGHT:
+		init(p.x - Bitmap->w, p.y - Bitmap->h);
+		break;
+	case CENTER:
+		init(p.x - Bitmap->w/2, p.y - Bitmap->h/2);
+		break;
+	case TOPLEFT:
+		init(p.x, p.y);
+		break;
+	default:
+		assert(0); //not implemented
+	}
+}
+
+CInfoPopup::CInfoPopup(SDL_Surface *Bitmap, bool Free)
+{
+	CCS->curh->hide();
+
+	free=Free;
+	bitmap=Bitmap;
+
+	if(bitmap)
+	{
+		pos.x = screen->w/2 - bitmap->w/2;
+		pos.y = screen->h/2 - bitmap->h/2;
+		pos.h = bitmap->h;
+		pos.w = bitmap->w;
+	}
+}
+
+void CInfoPopup::close()
+{
+	if(free)
+		SDL_FreeSurface(bitmap);
+	GH.popIntTotally(this);
+}
+
+void CInfoPopup::show(SDL_Surface * to)
+{
+	blitAt(bitmap,pos.x,pos.y,to);
+}
+
+CInfoPopup::~CInfoPopup()
+{
+	CCS->curh->show();
+}
+
+void CInfoPopup::init(int x, int y)
+{
+	CCS->curh->hide();
+
+	pos.x = x;
+	pos.y = y;
+	pos.h = bitmap->h;
+	pos.w = bitmap->w;
+
+	// Put the window back on screen if necessary
+	vstd::amax(pos.x, 0);
+	vstd::amax(pos.y, 0);
+	vstd::amin(pos.x, screen->w - bitmap->w);
+	vstd::amin(pos.y, screen->h - bitmap->h);
+}
+
+
+void CRClickPopup::clickRight(tribool down, bool previousState)
+{
+	if(down)
+		return;
+	close();
+}
+
+void CRClickPopup::close()
+{
+	GH.popIntTotally(this);
+}
+
+void CRClickPopup::createAndPush(const std::string &txt, const CInfoWindow::TCompsInfo &comps)
+{
+	PlayerColor player = LOCPLINT ? LOCPLINT->playerID : PlayerColor(1); //if no player, then use blue
+
+	CSimpleWindow * temp = new CInfoWindow(txt, player, comps);
+	temp->center(Point(GH.current->motion)); //center on mouse
+	temp->fitToScreen(10);
+	auto  rcpi = new CRClickPopupInt(temp,true);
+	GH.pushInt(rcpi);
+}
+
+void CRClickPopup::createAndPush(const std::string &txt, CComponent * component)
+{
+	CInfoWindow::TCompsInfo intComps;
+	intComps.push_back(component);
+
+	createAndPush(txt, intComps);
+}
+
+void CRClickPopup::createAndPush(const CGObjectInstance *obj, const Point &p, EAlignment alignment /*= BOTTOMRIGHT*/)
+{
+	CIntObject *iWin = createInfoWin(p, obj); //try get custom infowindow for this obj
+	if(iWin)
+		GH.pushInt(iWin);
+	else
+	{
+		if (adventureInt->curHero())
+			CRClickPopup::createAndPush(obj->getHoverText(adventureInt->curHero()));
+		else
+			CRClickPopup::createAndPush(obj->getHoverText(LOCPLINT->playerID));
+	}
+}
+
+CRClickPopup::CRClickPopup()
+{
+	addUsedEvents(RCLICK);
+}
+
+CRClickPopup::~CRClickPopup()
+{
+}
+
+void CRClickPopupInt::show(SDL_Surface * to)
+{
+	inner->show(to);
+}
+
+CRClickPopupInt::CRClickPopupInt( IShowActivatable *our, bool deleteInt )
+{
+	CCS->curh->hide();
+	inner = our;
+	delInner = deleteInt;
+}
+
+CRClickPopupInt::~CRClickPopupInt()
+{
+	if(delInner)
+		delete inner;
+
+	CCS->curh->show();
+}
+
+void CRClickPopupInt::showAll(SDL_Surface * to)
+{
+	inner->showAll(to);
+}
+
+Point CInfoBoxPopup::toScreen(Point p)
+{
+	vstd::abetween(p.x, adventureInt->terrain.pos.x + 100, adventureInt->terrain.pos.x + adventureInt->terrain.pos.w - 100);
+	vstd::abetween(p.y, adventureInt->terrain.pos.y + 100, adventureInt->terrain.pos.y + adventureInt->terrain.pos.h - 100);
+
+	return p;
+}
+
+CInfoBoxPopup::CInfoBoxPopup(Point position, const CGTownInstance * town):
+	CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "TOWNQVBK", toScreen(position))
+{
+	InfoAboutTown iah;
+	LOCPLINT->cb->getTownInfo(town, iah);
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	new CTownTooltip(Point(9, 10), iah);
+}
+
+CInfoBoxPopup::CInfoBoxPopup(Point position, const CGHeroInstance * hero):
+	CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "HEROQVBK", toScreen(position))
+{
+	InfoAboutHero iah;
+	LOCPLINT->cb->getHeroInfo(hero, iah);
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	new CHeroTooltip(Point(9, 10), iah);
+}
+
+CInfoBoxPopup::CInfoBoxPopup(Point position, const CGGarrison * garr):
+	CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "TOWNQVBK", toScreen(position))
+{
+	InfoAboutTown iah;
+	LOCPLINT->cb->getTownInfo(garr, iah);
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	new CArmyTooltip(Point(9, 10), iah);
+}
+
+CIntObject * CRClickPopup::createInfoWin(Point position, const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero
+{
+	if(!specific)
+		specific = adventureInt->selection;
+
+	assert(specific);
+
+	switch(specific->ID)
+	{
+	case Obj::HERO:
+		return new CInfoBoxPopup(position, dynamic_cast<const CGHeroInstance *>(specific));
+	case Obj::TOWN:
+		return new CInfoBoxPopup(position, dynamic_cast<const CGTownInstance *>(specific));
+	case Obj::GARRISON:
+	case Obj::GARRISON2:
+		return new CInfoBoxPopup(position, dynamic_cast<const CGGarrison *>(specific));
+	default:
+		return nullptr;
+	}
+}

+ 137 - 0
client/windows/InfoWindows.h

@@ -0,0 +1,137 @@
+#pragma once
+
+#include "CWindowObject.h"
+//#include "../gui/SDL_Extensions.h"
+#include "../../lib/FunctionList.h"
+
+struct SDL_Surface;
+struct Rect;
+class CAnimImage;
+class CLabel;
+class CAnimation;
+class CDefHandler;
+class CComponent;
+class CSelectableComponent;
+class CGGarrison;
+class CTextBox;
+class CAdventureMapButton;
+class CSlider;
+
+/*
+ * InfoWindows.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+// Window GUI class
+class CSimpleWindow : public CIntObject
+{
+public:
+	SDL_Surface * bitmap; //background
+	virtual void show(SDL_Surface * to);
+	CSimpleWindow():bitmap(nullptr){}; //c-tor
+	virtual ~CSimpleWindow(); //d-tor
+};
+
+/// text + comp. + ok button
+class CInfoWindow : public CSimpleWindow
+{ //window able to delete its components when closed
+	bool delComps; //whether comps will be deleted
+
+public:
+	typedef std::vector<std::pair<std::string,CFunctionList<void()> > > TButtonsInfo;
+	typedef std::vector<CComponent*> TCompsInfo;
+	QueryID ID; //for identification
+	CTextBox *text;
+	std::vector<CAdventureMapButton *> buttons;
+	std::vector<CComponent*> components;
+	CSlider *slider;
+
+	void setDelComps(bool DelComps);
+	virtual void close();
+
+	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
+	void sliderMoved(int to);
+
+	CInfoWindow(std::string Text, PlayerColor player, const TCompsInfo &comps = TCompsInfo(), const TButtonsInfo &Buttons = TButtonsInfo(), bool delComps = true); //c-tor
+	CInfoWindow(); //c-tor
+	~CInfoWindow(); //d-tor
+
+	//use only before the game starts! (showYesNoDialog in LOCPLINT must be used then)
+	static void showInfoDialog( const std::string & text, const std::vector<CComponent*> *components, bool DelComps = true, PlayerColor player = PlayerColor(1));
+	static void showOkDialog(const std::string & text, const std::vector<CComponent*> *components, const boost::function<void()> & onOk, bool delComps = true, PlayerColor player = PlayerColor(1));
+	static void showYesNoDialog( const std::string & text, const std::vector<CComponent*> *components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, bool DelComps = true, PlayerColor player = PlayerColor(1));
+	static CInfoWindow *create(const std::string &text, PlayerColor playerID = PlayerColor(1), const std::vector<CComponent*> *components = nullptr, bool DelComps = false);
+
+	/// create text from title and description: {title}\n\n description
+	static std::string genText(std::string title, std::string description);
+};
+
+/// popup displayed on R-click
+class CRClickPopup : public CIntObject
+{
+public:
+	virtual void close();
+	void clickRight(tribool down, bool previousState);
+
+	CRClickPopup();
+	virtual ~CRClickPopup(); //d-tor
+
+	static CIntObject* createInfoWin(Point position, const CGObjectInstance * specific);
+	static void createAndPush(const std::string &txt, const CInfoWindow::TCompsInfo &comps = CInfoWindow::TCompsInfo());
+	static void createAndPush(const std::string &txt, CComponent * component);
+	static void createAndPush(const CGObjectInstance *obj, const Point &p, EAlignment alignment = BOTTOMRIGHT);
+};
+
+/// popup displayed on R-click
+class CRClickPopupInt : public CRClickPopup
+{
+public:
+	IShowActivatable *inner;
+	bool delInner;
+
+	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
+	CRClickPopupInt(IShowActivatable *our, bool deleteInt); //c-tor
+	virtual ~CRClickPopupInt(); //d-tor
+};
+
+class CInfoPopup : public CRClickPopup
+{
+public:
+	bool free; //TODO: comment me
+	SDL_Surface * bitmap; //popup background
+	void close();
+	void show(SDL_Surface * to);
+	CInfoPopup(SDL_Surface * Bitmap, int x, int y, bool Free=false); //c-tor
+	CInfoPopup(SDL_Surface * Bitmap, const Point &p, EAlignment alignment, bool Free=false); //c-tor
+	CInfoPopup(SDL_Surface * Bitmap = nullptr, bool Free = false); //default c-tor
+
+	void init(int x, int y);
+	~CInfoPopup(); //d-tor
+};
+
+/// popup on adventure map for town\hero objects
+class CInfoBoxPopup : public CWindowObject
+{
+	Point toScreen(Point pos);
+public:
+	CInfoBoxPopup(Point position, const CGTownInstance * town);
+	CInfoBoxPopup(Point position, const CGHeroInstance * hero);
+	CInfoBoxPopup(Point position, const CGGarrison * garr);
+};
+
+/// component selection window
+class CSelWindow : public CInfoWindow
+{ //warning - this window deletes its components by closing!
+public:
+	void selectionChange(unsigned to);
+	void madeChoice(); //looks for selected component and calls callback
+	CSelWindow(const std::string& text, PlayerColor player, int charperline ,const std::vector<CSelectableComponent*> &comps, const std::vector<std::pair<std::string,CFunctionList<void()> > > &Buttons, QueryID askID); //c-tor
+	CSelWindow(){}; //c-tor
+	//notification - this class inherits important destructor from CInfoWindow
+};