From 9d92b77163bfa07f97b51d9a418f7f6f898f1378 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tam=C3=A1s=20B=C3=A1lint=20Misius?= <lbphacker@gmail.com>
Date: Fri, 12 Apr 2019 17:09:34 +0200
Subject: [PATCH] Clean up sign-related code a bit

Also draw search signs with purple text and thread signs with red.
---
 src/graphics/RasterDrawMethods.inl |  36 +---
 src/graphics/Renderer.cpp          |  28 ++--
 src/gui/game/GameController.cpp    |  55 +++---
 src/gui/game/GameController.h      |   1 +
 src/gui/game/GameView.cpp          |  35 ++--
 src/gui/game/SignTool.cpp          |  18 +-
 src/lua/LuaScriptInterface.cpp     |  15 +-
 src/simulation/Sign.cpp            | 260 +++++++++++++++--------------
 src/simulation/Sign.h              |  31 +++-
 9 files changed, 235 insertions(+), 244 deletions(-)

diff --git a/src/graphics/RasterDrawMethods.inl b/src/graphics/RasterDrawMethods.inl
index 1c9a31614..4d7b0be40 100644
--- a/src/graphics/RasterDrawMethods.inl
+++ b/src/graphics/RasterDrawMethods.inl
@@ -58,34 +58,14 @@ int PIXELMETHODS_CLASS::drawtext(int x, int y, String str, int r, int g, int b,
 			if(!s[1]) break;
 			switch (s[1])
 			{
-			case 'w':
-				r = g = b = 255;
-				break;
-			case 'g':
-				r = g = b = 192;
-				break;
-			case 'o':
-				r = 255;
-				g = 216;
-				b = 32;
-				break;
-			case 'r':
-				r = 255;
-				g = b = 0;
-				break;
-			case 'l':
-				r = 255;
-				g = b = 75;
-				break;
-			case 'b':
-				r = g = 0;
-				b = 255;
-				break;
-			case 't':
-				b = 255;
-				g = 170;
-				r = 32;
-				break;
+			case 'w': r = 255; g = 255; b = 255; break;
+			case 'g': r = 192; g = 192; b = 192; break;
+			case 'o': r = 255; g = 216; b =  32; break;
+			case 'r': r = 255; g =   0; b =   0; break;
+			case 'l': r = 255; g =  75; b =  75; break;
+			case 'b': r =   0; g =   0; b = 255; break;
+			case 't': b = 255; g = 170; r =  32; break;
+			case 'u': r = 147; g =  83; b = 211; break;
 			}
 			if(invert)
 			{
diff --git a/src/graphics/Renderer.cpp b/src/graphics/Renderer.cpp
index 7b495838a..905fd473e 100644
--- a/src/graphics/Renderer.cpp
+++ b/src/graphics/Renderer.cpp
@@ -981,28 +981,21 @@ void Renderer::DrawSigns()
 	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, partsFbo);
 	glTranslated(0, MENUSIZE, 0);
 #endif
-	for (size_t i = 0; i < signs.size(); i++)
-		if (signs[i].text.length())
+	for (auto &currentSign : signs)
+	{
+		if (currentSign.text.length())
 		{
-			String::value_type type = 0;
-			String text = signs[i].getText(sim);
-			sign::splitsign(signs[i].text, &type);
-			signs[i].pos(text, x, y, w, h);
+			String text = currentSign.getDisplayText(sim, x, y, w, h);
 			clearrect(x, y, w+1, h);
 			drawrect(x, y, w+1, h, 192, 192, 192, 255);
-			if (!type)
-				drawtext(x+3, y+3, text, 255, 255, 255, 255);
-			else if(type == 'b')
-				drawtext(x+3, y+3, text, 211, 211, 40, 255);
-			else
-				drawtext(x+3, y+3, text, 0, 191, 255, 255);
+			drawtext(x+3, y+3, text, 255, 255, 255, 255);
 
-			if (signs[i].ju != sign::None)
+			if (currentSign.ju != sign::None)
 			{
-				int x = signs[i].x;
-				int y = signs[i].y;
-				int dx = 1 - signs[i].ju;
-				int dy = (signs[i].y > 18) ? -1 : 1;
+				int x = currentSign.x;
+				int y = currentSign.y;
+				int dx = 1 - currentSign.ju;
+				int dy = (currentSign.y > 18) ? -1 : 1;
 #ifdef OGLR
 				glBegin(GL_LINES);
 				glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
@@ -1019,6 +1012,7 @@ void Renderer::DrawSigns()
 #endif
 			}
 		}
+	}
 #ifdef OGLR
 	glTranslated(0, -MENUSIZE, 0);
 	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFbo);
diff --git a/src/gui/game/GameController.cpp b/src/gui/game/GameController.cpp
index 5b558bf69..4366e8dcd 100644
--- a/src/gui/game/GameController.cpp
+++ b/src/gui/game/GameController.cpp
@@ -311,7 +311,7 @@ int GameController::GetSignAt(int x, int y)
 	for (int i = sim->signs.size()-1; i >= 0; i--)
 	{
 		int signx, signy, signw, signh;
-		sim->signs[i].pos(sim->signs[i].getText(sim), signx, signy, signw, signh);
+		sim->signs[i].getDisplayText(sim, signx, signy, signw, signh);
 		if (x>=signx && x<=signx+signw && y>=signy && y<=signy+signh)
 			return i;
 	}
@@ -324,6 +324,11 @@ String GameController::GetSignText(int signID)
 	return gameModel->GetSimulation()->signs[signID].text;
 }
 
+std::pair<int, sign::Type> GameController::GetSignSplit(int signID)
+{
+	return gameModel->GetSimulation()->signs[signID].split();
+}
+
 void GameController::PlaceSave(ui::Point position, bool includePressure)
 {
 	bool incPressure = Client::Ref().GetPrefBool("Simulation.LoadPressure", true);
@@ -640,9 +645,10 @@ bool GameController::MouseDown(int x, int y, unsigned button)
 			foundSignID = GetSignAt(x, y);
 			if (foundSignID != -1)
 			{
-				sign foundSign = gameModel->GetSimulation()->signs[foundSignID];
-				if (sign::splitsign(foundSign.text))
+				if (gameModel->GetSimulation()->signs[foundSignID].split().first)
+				{
 					return false;
+				}
 			}
 		}
 	}
@@ -665,40 +671,31 @@ bool GameController::MouseUp(int x, int y, unsigned button, char type)
 			int foundSignID = GetSignAt(x, y);
 			if (foundSignID != -1)
 			{
-				sign foundSign = gameModel->GetSimulation()->signs[foundSignID];
+				sign &foundSign = gameModel->GetSimulation()->signs[foundSignID];
 				String str = foundSign.text;
-				String::value_type type;
-				int pos = sign::splitsign(str, &type);
-				if (pos)
+				auto si = gameModel->GetSimulation()->signs[foundSignID].split();
+				if (si.first)
 				{
 					ret = false;
-					if (type == 'c' || type == 't' || type == 's')
+					switch (si.second)
 					{
-						String link = str.Substr(3, pos-3);
-						switch (type)
+					case sign::Type::Save:
 						{
-						case 'c':
-						{
-							int saveID = link.ToNumber<int>(true);
+							int saveID = str.Substr(3, si.first - 3).ToNumber<int>(true);
 							if (saveID)
 								OpenSavePreview(saveID, 0, false);
-							break;
 						}
-						case 't':
-						{
-							// buff is already confirmed to be a number by sign::splitsign
-							Platform::OpenURI(ByteString::Build(SCHEME "powdertoy.co.uk/Discussions/Thread/View.html?Thread=", link.ToUtf8()));
-							break;
-						}
-						case 's':
-							OpenSearch(link);
-							break;
-						}
-					}
-					else if (type == 'b')
-					{
-						Simulation * sim = gameModel->GetSimulation();
-						sim->create_part(-1, foundSign.x, foundSign.y, PT_SPRK);
+						break;
+					case sign::Type::Thread:
+						Platform::OpenURI(ByteString::Build(SCHEME "powdertoy.co.uk/Discussions/Thread/View.html?Thread=", str.Substr(3, si.first - 3).ToUtf8()));
+						break;
+					case sign::Type::Search:
+						OpenSearch(str.Substr(3, si.first - 3));
+						break;
+					case sign::Type::Button:
+						gameModel->GetSimulation()->create_part(-1, foundSign.x, foundSign.y, PT_SPRK);
+						break;
+					default: break;
 					}
 				}
 			}
diff --git a/src/gui/game/GameController.h b/src/gui/game/GameController.h
index 50a7bb550..cbecda932 100644
--- a/src/gui/game/GameController.h
+++ b/src/gui/game/GameController.h
@@ -59,6 +59,7 @@ public:
 	GameView * GetView();
 	int GetSignAt(int x, int y);
 	String GetSignText(int signID);
+	std::pair<int, sign::Type> GetSignSplit(int signID);
 
 	bool MouseMove(int x, int y, int dx, int dy);
 	bool MouseDown(int x, int y, unsigned button);
diff --git a/src/gui/game/GameView.cpp b/src/gui/game/GameView.cpp
index 039442649..9162828cc 100644
--- a/src/gui/game/GameView.cpp
+++ b/src/gui/game/GameView.cpp
@@ -1726,24 +1726,25 @@ void GameView::OnTick(float dt)
 	if (foundSignID != -1)
 	{
 		String str = c->GetSignText(foundSignID);
-		String::value_type type = '\0';
-		int pos = sign::splitsign(str, &type);
-		if (type == 'c' || type == 't' || type == 's')
+		auto si = c->GetSignSplit(foundSignID);
+
+		StringBuilder tooltip;
+		switch (si.second)
+		{
+		case sign::Type::Save:
+			tooltip << "Go to save ID:" << str.Substr(3, si.first - 3);
+			break;
+		case sign::Type::Thread:
+			tooltip << "Open forum thread " << str.Substr(3, si.first - 3) << " in browser";
+			break;
+		case sign::Type::Search:
+			tooltip << "Search for " << str.Substr(3, si.first - 3);
+			break;
+		default: break;
+		}
+
+		if (tooltip.Size())
 		{
-			String linkSign = str.Substr(3, pos-3);
-			StringBuilder tooltip;
-			switch (type)
-			{
-			case 'c':
-				tooltip << "Go to save ID:" << linkSign;
-				break;
-			case 't':
-				tooltip << "Open forum thread " << linkSign << " in browser";
-				break;
-			case 's':
-				tooltip << "Search for " << linkSign;
-				break;
-			}
 			ToolTip(ui::Point(0, Size.Y), tooltip.Build());
 		}
 	}
diff --git a/src/gui/game/SignTool.cpp b/src/gui/game/SignTool.cpp
index db9688981..1a8cad401 100644
--- a/src/gui/game/SignTool.cpp
+++ b/src/gui/game/SignTool.cpp
@@ -193,23 +193,15 @@ void SignWindow::OnTryExit(ui::Window::ExitMethod method)
 
 void SignWindow::DoDraw()
 {
-	for(std::vector<sign>::iterator iter = sim->signs.begin(), end = sim->signs.end(); iter != end; ++iter)
+	for (auto &currentSign : sim->signs)
 	{
-		sign & currentSign = *iter;
 		int x, y, w, h, dx, dy;
-		String::value_type type = 0;
 		Graphics * g = GetGraphics();
-		String text = currentSign.getText(sim);
-		sign::splitsign(currentSign.text, &type);
-		currentSign.pos(text, x, y, w, h);
+
+		String text = currentSign.getDisplayText(sim, x, y, w, h);
 		g->clearrect(x, y, w+1, h);
 		g->drawrect(x, y, w+1, h, 192, 192, 192, 255);
-		if (!type)
-			g->drawtext(x+3, y+3, text, 255, 255, 255, 255);
-		else if(type == 'b')
-			g->drawtext(x+3, y+3, text, 211, 211, 40, 255);
-		else
-			g->drawtext(x+3, y+3, text, 0, 191, 255, 255);
+		g->drawtext(x+3, y+3, text, 255, 255, 255, 255);
 
 		if (currentSign.ju != sign::None)
 		{
@@ -294,7 +286,7 @@ void SignTool::Click(Simulation * sim, Brush * brush, ui::Point position)
 	int signX, signY, signW, signH, signIndex = -1;
 	for (size_t i = 0; i < sim->signs.size(); i++)
 	{
-		sim->signs[i].pos(sim->signs[i].getText(sim), signX, signY, signW, signH);
+		sim->signs[i].getDisplayText(sim, signX, signY, signW, signH);
 		if (position.X > signX && position.X < signX+signW && position.Y > signY && position.Y < signY+signH)
 		{
 			signIndex = i;
diff --git a/src/lua/LuaScriptInterface.cpp b/src/lua/LuaScriptInterface.cpp
index 393506186..02cf8cdd9 100644
--- a/src/lua/LuaScriptInterface.cpp
+++ b/src/lua/LuaScriptInterface.cpp
@@ -564,10 +564,11 @@ int LuaScriptInterface::simulation_signIndex(lua_State *l)
 		return lua_pushnil(l), 1;
 	}
 
+	int x, y, w, h;
 	if (!key.compare("text"))
 		return lua_pushstring(l, luacon_sim->signs[id].text.ToUtf8().c_str()), 1;
 	else if (!key.compare("displayText"))
-		return lua_pushstring(l, luacon_sim->signs[id].getText(luacon_sim).ToUtf8().c_str()), 1;
+		return lua_pushstring(l, luacon_sim->signs[id].getDisplayText(luacon_sim, x, y, w, h, false).ToUtf8().c_str()), 1;
 	else if (!key.compare("justification"))
 		return lua_pushnumber(l, (int)luacon_sim->signs[id].ju), 1;
 	else if (!key.compare("x"))
@@ -576,29 +577,25 @@ int LuaScriptInterface::simulation_signIndex(lua_State *l)
 		return lua_pushnumber(l, luacon_sim->signs[id].y), 1;
 	else if (!key.compare("screenX"))
 	{
-		int x, y, w, h;
-		luacon_sim->signs[id].pos(luacon_sim->signs[id].getText(luacon_sim), x, y, w, h);
+		luacon_sim->signs[id].getDisplayText(luacon_sim, x, y, w, h);
 		lua_pushnumber(l, x);
 		return 1;
 	}
 	else if (!key.compare("screenY"))
 	{
-		int x, y, w, h;
-		luacon_sim->signs[id].pos(luacon_sim->signs[id].getText(luacon_sim), x, y, w, h);
+		luacon_sim->signs[id].getDisplayText(luacon_sim, x, y, w, h);
 		lua_pushnumber(l, y);
 		return 1;
 	}
 	else if (!key.compare("width"))
 	{
-		int x, y, w, h;
-		luacon_sim->signs[id].pos(luacon_sim->signs[id].getText(luacon_sim), x, y, w, h);
+		luacon_sim->signs[id].getDisplayText(luacon_sim, x, y, w, h);
 		lua_pushnumber(l, w);
 		return 1;
 	}
 	else if (!key.compare("height"))
 	{
-		int x, y, w, h;
-		luacon_sim->signs[id].pos(luacon_sim->signs[id].getText(luacon_sim), x, y, w, h);
+		luacon_sim->signs[id].getDisplayText(luacon_sim, x, y, w, h);
 		lua_pushnumber(l, h);
 		return 1;
 	}
diff --git a/src/simulation/Sign.cpp b/src/simulation/Sign.cpp
index 7e5805fba..7ae3f13a5 100644
--- a/src/simulation/Sign.cpp
+++ b/src/simulation/Sign.cpp
@@ -11,139 +11,153 @@ sign::sign(String text_, int x_, int y_, Justification justification_):
 {
 }
 
-void sign::pos(String signText, int & x0, int & y0, int & w, int & h)
-{
-	w = Graphics::textwidth(signText.c_str()) + 5;
-	h = 15;
-	x0 = (ju == Right) ? x - w :
-		  (ju == Left) ? x : x - w/2;
-	y0 = (y > 18) ? y - 18 : y + 4;
-}
-
-int sign::splitsign(String str, String::value_type *type)
-{
-	if (str[0] == '{' && (str[1] == 'c' || str[1] == 't' || str[1] == 'b' || str[1] == 's'))
-	{
-		size_t strIndex = 2;
-		// Signs with text arguments
-		if (str[1] == 's')
-		{
-			if (str[2] == ':')
-			{
-				strIndex = 3;
-				while (strIndex < str.length() && str[strIndex] != '|')
-					strIndex++;
-			}
-			else
-				return 0;
-		}
-		// Signs with number arguments
-		if (str[1] == 'c' || str[1] == 't')
-		{
-			if (str[2] == ':' && str[3] >= '0' && str[3] <= '9')
-			{
-				strIndex = 4;
-				while (str[strIndex] >= '0' && str[strIndex] <= '9')
-					strIndex++;
-			}
-			else
-				return 0;
-		}
-
-		if (str[strIndex] == '|')
-		{
-			if (str[str.length() - 1] == '}')
-			{
-				if (type)
-					*type = str[1];
-				return strIndex;
-			}
-		}
-	}
-	return 0;
-}
-
-String sign::getText(Simulation *sim)
+String sign::getDisplayText(Simulation *sim, int &x0, int &y0, int &w, int &h, bool colorize)
 {
+	String drawable_text;
+	auto si = std::make_pair(0, Type::Normal);
 	if (text.find('{') == text.npos)
 	{
-		return text;
+		drawable_text = text;
 	}
-
-	if (int pos = splitsign(text))
+	else
 	{
-		return text.Between(pos + 1, text.size() - 1);
-	}
-
-	Particle const *part = nullptr;
-	float pressure = 0.0f;
-	float aheat = 0.0f;
-	if (x >= 0 && x < XRES && y >= 0 && y < YRES)
-	{
-		if (sim->photons[y][x])
+		si = split();
+		if (si.first)
 		{
-			part = &(sim->parts[ID(sim->photons[y][x])]);
-		}
-		else if (sim->pmap[y][x])
-		{
-			part = &(sim->parts[ID(sim->pmap[y][x])]);
-		}
-		pressure = sim->pv[y/CELL][x/CELL];
-		aheat = sim->hv[y/CELL][x/CELL] - 273.15f;
-	}
-
-	String remaining_text = text;
-	StringBuilder formatted_text;
-	while (auto split_left_curly = remaining_text.SplitBy('{'))
-	{
-		String after_left_curly = split_left_curly.After();
-		if (auto split_right_curly = after_left_curly.SplitBy('}'))
-		{
-			formatted_text << split_left_curly.Before();
-			remaining_text = split_right_curly.After();
-			String between_curlies = split_right_curly.Before();
-			if (between_curlies == "t" || between_curlies == "temp")
-			{
-				formatted_text << Format::Precision(Format::ShowPoint(part ? part->temp - 273.15f : 0.0f), 2);
-			}
-			else if (between_curlies == "p" || between_curlies == "pres")
-			{
-				formatted_text << Format::Precision(Format::ShowPoint(pressure), 2);
-			}
-			else if (between_curlies == "a" || between_curlies == "aheat")
-			{
-				formatted_text << Format::Precision(Format::ShowPoint(aheat), 2);
-			}
-			else if (between_curlies == "type")
-			{
-				formatted_text << (part ? sim->BasicParticleInfo(*part) : (formatted_text.Size() ? String::Build("empty") : String::Build("Empty")));
-			}
-			else if (between_curlies == "ctype")
-			{
-				formatted_text << (part ? ((part->ctype && sim->IsValidElement(part->ctype)) ? sim->ElementResolve(part->ctype, -1) : String::Build(part->ctype)) : (formatted_text.Size() ? String::Build("empty") : String::Build("Empty")));
-			}
-			else if (between_curlies == "life")
-			{
-				formatted_text << (part ? part->life : 0);
-			}
-			else if (between_curlies == "tmp")
-			{
-				formatted_text << (part ? part->tmp : 0);
-			}
-			else if (between_curlies == "tmp2")
-			{
-				formatted_text << (part ? part->tmp2 : 0);
-			}
-			else
-			{
-				formatted_text << '{' << between_curlies << '}';
-			}
+			drawable_text = text.Between(si.first + 1, text.size() - 1);
 		}
 		else
 		{
+			Particle const *part = nullptr;
+			float pressure = 0.0f;
+			float aheat = 0.0f;
+			if (x >= 0 && x < XRES && y >= 0 && y < YRES)
+			{
+				if (sim->photons[y][x])
+				{
+					part = &(sim->parts[ID(sim->photons[y][x])]);
+				}
+				else if (sim->pmap[y][x])
+				{
+					part = &(sim->parts[ID(sim->pmap[y][x])]);
+				}
+				pressure = sim->pv[y/CELL][x/CELL];
+				aheat = sim->hv[y/CELL][x/CELL] - 273.15f;
+			}
+
+			String remaining_text = text;
+			StringBuilder formatted_text;
+			while (auto split_left_curly = remaining_text.SplitBy('{'))
+			{
+				String after_left_curly = split_left_curly.After();
+				if (auto split_right_curly = after_left_curly.SplitBy('}'))
+				{
+					formatted_text << split_left_curly.Before();
+					remaining_text = split_right_curly.After();
+					String between_curlies = split_right_curly.Before();
+					if (between_curlies == "t" || between_curlies == "temp")
+					{
+						formatted_text << Format::Precision(Format::ShowPoint(part ? part->temp - 273.15f : 0.0f), 2);
+					}
+					else if (between_curlies == "p" || between_curlies == "pres")
+					{
+						formatted_text << Format::Precision(Format::ShowPoint(pressure), 2);
+					}
+					else if (between_curlies == "a" || between_curlies == "aheat")
+					{
+						formatted_text << Format::Precision(Format::ShowPoint(aheat), 2);
+					}
+					else if (between_curlies == "type")
+					{
+						formatted_text << (part ? sim->BasicParticleInfo(*part) : (formatted_text.Size() ? String::Build("empty") : String::Build("Empty")));
+					}
+					else if (between_curlies == "ctype")
+					{
+						formatted_text << (part ? ((part->ctype && sim->IsValidElement(part->ctype)) ? sim->ElementResolve(part->ctype, -1) : String::Build(part->ctype)) : (formatted_text.Size() ? String::Build("empty") : String::Build("Empty")));
+					}
+					else if (between_curlies == "life")
+					{
+						formatted_text << (part ? part->life : 0);
+					}
+					else if (between_curlies == "tmp")
+					{
+						formatted_text << (part ? part->tmp : 0);
+					}
+					else if (between_curlies == "tmp2")
+					{
+						formatted_text << (part ? part->tmp2 : 0);
+					}
+					else
+					{
+						formatted_text << '{' << between_curlies << '}';
+					}
+				}
+				else
+				{
+					break;
+				}
+			}
+			formatted_text << remaining_text;
+			drawable_text = formatted_text.Build();
+		}
+	}
+
+	if (colorize)
+	{
+		switch (si.second)
+		{
+		case Normal: break;
+		case Save:   drawable_text = "\bt" + drawable_text; break;
+		case Thread: drawable_text = "\bl" + drawable_text; break;
+		case Button: drawable_text = "\bo" + drawable_text; break;
+		case Search: drawable_text = "\bu" + drawable_text; break;
+		}
+	}
+
+	w = Graphics::textwidth(drawable_text.c_str()) + 5;
+	h = 15;
+	x0 = (ju == Right) ? x - w : (ju == Left) ? x : x - w/2;
+	y0 = (y > 18) ? y - 18 : y + 4;
+
+	return drawable_text;
+}
+
+std::pair<int, sign::Type> sign::split()
+{
+	String::size_type pipe = 0;
+	if (text.size() >= 4 && text.front() == '{' && text.back() == '}')
+	{
+		switch (text[1])
+		{
+		case 'c':
+		case 't':
+			if (text[2] == ':' && (pipe = text.find('|', 4)) != text.npos)
+			{
+				for (String::size_type i = 3; i < pipe; ++i)
+				{
+					if (text[i] < '0' || text[i] > '9')
+					{
+						return std::make_pair(0, Type::Normal);
+					}
+				}
+				return std::make_pair(pipe, text[1] == 'c' ? Type::Save : Type::Thread);
+			}
+			break;
+
+		case 'b':
+			if (text[2] == '|')
+			{
+				return std::make_pair(2, Type::Button);
+			}
+			break;
+
+		case 's':
+			if (text[2] == ':' && (pipe = text.find('|', 3)) != text.npos)
+			{
+				return std::make_pair(pipe, Type::Search);
+			}
 			break;
 		}
 	}
-	formatted_text << remaining_text;
-	return formatted_text.Build();
+	return std::make_pair(0, Type::Normal);
 }
diff --git a/src/simulation/Sign.h b/src/simulation/Sign.h
index 6eb83db46..66dc052aa 100644
--- a/src/simulation/Sign.h
+++ b/src/simulation/Sign.h
@@ -3,21 +3,36 @@
 
 #include "common/String.h"
 
+#include <utility>
+
 class Simulation;
 
-class sign
+struct sign
 {
-public:
-	enum Justification { Left = 0, Middle = 1, Right = 2, None = 3 };
-	sign(String text_, int x_, int y_, Justification justification_);
+	enum Justification
+	{
+		Left = 0,
+		Middle = 1,
+		Right = 2,
+		None = 3
+	};
+
+	enum Type
+	{
+		Normal,
+		Save,
+		Thread,
+		Button,
+		Search
+	};
+
 	int x, y;
 	Justification ju;
 	String text;
 
-	String getText(Simulation *sim);
-	void pos(String signText, int & x0, int & y0, int & w, int & h);
-
-	static int splitsign(String str, String::value_type *type = NULL);
+	sign(String text_, int x_, int y_, Justification justification_);
+	String getDisplayText(Simulation *sim, int &x, int &y, int &w, int &h, bool colorize = true);
+	std::pair<int, Type> split();
 };
 
 #endif