From 92848f29722c875f035d5719a41d0665e72d034c Mon Sep 17 00:00:00 2001
From: jsalmon3 <>
Date: Mon, 13 Oct 2003 03:43:18 +0000
Subject: [PATCH] Rewrote a lot of the info panel

---
 src/include/interface.h |   5 +-
 src/include/ui.h        |  81 ++++---
 src/ui/mainscr.cpp      | 146 +++++++-----
 src/ui/mouse.cpp        | 162 ++++++++++----
 src/ui/script_ui.cpp    | 477 ++++++++++++++++++++++++++++++++--------
 src/ui/ui.cpp           | 199 +++++++++++++----
 6 files changed, 814 insertions(+), 256 deletions(-)

diff --git a/src/include/interface.h b/src/include/interface.h
index cb9309726..f4f90b4b4 100644
--- a/src/include/interface.h
+++ b/src/include/interface.h
@@ -92,8 +92,11 @@ struct _button_action_ {
 
     /// Button area under cursor
 enum _button_area_ {
-    ButtonAreaInfo,			/// Info panel button
+    ButtonAreaSelected,			/// Selected button
     ButtonAreaTraining,			/// Training button
+    ButtonAreaUpgrading,		/// Upgrading button
+    ButtonAreaResearching,		/// Researching button
+    ButtonAreaTransporting,		/// Transporting button
     ButtonAreaButton,			/// Button panel button
     ButtonAreaMenu,			/// Menu button
 };   
diff --git a/src/include/ui.h b/src/include/ui.h
index 4f02d8fee..c25881d5b 100644
--- a/src/include/ui.h
+++ b/src/include/ui.h
@@ -185,16 +185,6 @@ typedef struct _ui_ {
     int		ResourceY;		/// Resource Y position
 
     struct {
-#if 0
-	int	IconX;			/// Icon X position
-	int	IconY;			/// Icon Y position
-	IconConfig	Icon;		/// Icon for resource
-	int	TextX;			/// Text X position
-	int	TextX;			/// Text Y position
-	char*	Text;			/// Text for resource
-	int	AmountX;		/// Amount X position
-	int	AmountY;		/// Amount Y position
-#endif
 	GraphicConfig Icon;		/// icon image
 	int	IconRow;		/// icon image row (frame)
 	int	IconX;			/// icon X position
@@ -213,17 +203,60 @@ typedef struct _ui_ {
     int		InfoPanelW;		/// Info panel width
     int		InfoPanelH;		/// Info panel height
 
-    // Complete bar
-    VMemType	CompleteBarColorRGB;	/// color for complete bar
-    VMemType	CompleteBarColor;	/// color for complete bar
-    int		CompleteBarX;		/// complete bar X position
-    int		CompleteBarY;		/// complete bar Y position
-    int		CompleteBarW;		/// complete bar width
-    int		CompleteBarH;		/// complete bar height
-    char*	CompleteBarText;	/// complete bar text
-    unsigned	CompleteBarFont;	/// complete bar font
-    int		CompleteTextX;		/// complete text X position
-    int		CompleteTextY;		/// complete text Y position
+    Button*	SingleSelectedButton;
+    char*	SingleSelectedText;
+    int		SingleSelectedFont;
+    int		SingleSelectedTextX;
+    int		SingleSelectedTextY;
+    Button*	SelectedButtons;	/// Selected buttons
+    int		NumSelectedButtons;	/// Number of selected buttons
+    char*	SelectedText;
+    int		SelectedFont;
+    int		SelectedTextX;
+    int		SelectedTextY;
+
+    Button*	SingleTrainingButton;
+    char*	SingleTrainingText;
+    int		SingleTrainingFont;
+    int		SingleTrainingTextX;
+    int		SingleTrainingTextY;
+    Button*	TrainingButtons;	/// Training buttons
+    int		NumTrainingButtons;	/// Number of training buttons
+    char*	TrainingText;
+    int		TrainingFont;
+    int		TrainingTextX;
+    int		TrainingTextY;
+
+    Button*	UpgradingButton;
+    char*	UpgradingText;
+    int		UpgradingFont;
+    int		UpgradingTextX;
+    int		UpgradingTextY;
+
+    Button*	ResearchingButton;
+    char*	ResearchingText;
+    int		ResearchingFont;
+    int		ResearchingTextX;
+    int		ResearchingTextY;
+
+    Button*	TransportingButtons;
+    int		NumTransportingButtons;	/// Number of transporting buttons
+    char*	TransportingText;
+    int		TransportingFont;
+    int		TransportingTextX;
+    int		TransportingTextY;
+
+    // Completed bar
+    VMemType	CompletedBarColorRGB;	/// color for completed bar
+    VMemType	CompletedBarColor;	/// color for completed bar
+    int		CompletedBarX;		/// completed bar X position
+    int		CompletedBarY;		/// completed bar Y position
+    int		CompletedBarW;		/// completed bar width
+    int		CompletedBarH;		/// completed bar height
+    char*	CompletedBarText;	/// completed bar text
+    int		CompletedBarFont;	/// completed bar font
+    int		CompletedBarTextX;	/// completed bar text X position
+    int		CompletedBarTextY;	/// completed bar text Y position
 
     // Button panel
     GraphicConfig ButtonPanel;		/// Button panel background
@@ -273,12 +306,8 @@ typedef struct _ui_ {
     int		StatusLineY;		/// status line screen Y position
     int		StatusLineTextX;	/// status line screen text X position
     int		StatusLineTextY;	/// status line screen text Y position
-    unsigned	StatusLineFont;		/// Status line font
+    int		StatusLineFont;		/// Status line font
 
-    Button*	InfoButtons;		/// Info buttons
-    int		NumInfoButtons;		/// Number of info buttons
-    Button*	TrainingButtons;	/// Training buttons
-    int		NumTrainingButtons;	/// Number of training buttons
     Button*	ButtonButtons;		/// Button panel buttons
     int		NumButtonButtons;	/// Number of button panel buttons
 
diff --git a/src/ui/mainscr.cpp b/src/ui/mainscr.cpp
index a45c752c7..b8cd398e5 100644
--- a/src/ui/mainscr.cpp
+++ b/src/ui/mainscr.cpp
@@ -119,10 +119,10 @@ local void UiDrawManaBar(const Unit* unit, int x, int y)
     int f;
 
     y += IconHeight + 7;
-    VideoFillRectangleClip(ColorBlack, x, y + 3,IconWidth + 7, 4);
+    VideoFillRectangleClip(ColorBlack, x, y + 3, IconWidth + 7, 4);
     if (unit->HP) {
-	/* s0m3body: mana bar should represent proportional value of Mana with respect to
-	 * MaxMana (unit->Type->_MaxMana) for the unit */
+	// s0m3body: mana bar should represent proportional value of Mana
+	// with respect to MaxMana (unit->Type->_MaxMana) for the unit
 	f = (100 * unit->Mana) / unit->Type->_MaxMana;
 	f = (f * (IconWidth + 5)) / 100;
 	VideoFillRectangleClip(ColorBlue, x + 1, y + 3 + 1, f, 2);
@@ -143,11 +143,11 @@ local void UiDrawCompleted(int full, int ready)
 	return;
     }
     f = (100 * ready) / full;
-    f = (f * TheUI.CompleteBarW) / 100;
-    VideoFillRectangleClip(TheUI.CompleteBarColor,
-	TheUI.CompleteBarX, TheUI.CompleteBarY, f, TheUI.CompleteBarH);
-    VideoDrawText(TheUI.CompleteTextX, TheUI.CompleteTextY,
-	TheUI.CompleteBarFont, TheUI.CompleteBarText);
+    f = (f * TheUI.CompletedBarW) / 100;
+    VideoFillRectangleClip(TheUI.CompletedBarColor,
+	TheUI.CompletedBarX, TheUI.CompletedBarY, f, TheUI.CompletedBarH);
+    VideoDrawText(TheUI.CompletedBarTextX, TheUI.CompletedBarTextY,
+	TheUI.CompletedBarFont, TheUI.CompletedBarText);
 }
 
 /**
@@ -198,13 +198,19 @@ global void DrawUnitInfo(const Unit* unit)
     //
     //	Draw icon in upper left corner
     //
-    x = TheUI.InfoButtons[0].X;
-    y = TheUI.InfoButtons[0].Y;
-    DrawUnitIcon(unit->Player,type->Icon.Icon,
-	(ButtonAreaUnderCursor == ButtonAreaInfo && ButtonUnderCursor == 0) ?
-	    (IconActive | (MouseButtons & LeftButton)) : 0,
-	x, y);
-    UiDrawLifeBar(unit, x, y);
+    if (TheUI.SingleSelectedText) {
+	VideoDrawText(TheUI.SingleSelectedTextX, TheUI.SingleSelectedTextY,
+	    TheUI.SingleSelectedFont, TheUI.SingleSelectedText);
+    }
+    if (TheUI.SingleSelectedButton) {
+	x = TheUI.SingleSelectedButton->X;
+	y = TheUI.SingleSelectedButton->Y;
+	DrawUnitIcon(unit->Player, type->Icon.Icon,
+	    (ButtonAreaUnderCursor == ButtonAreaSelected && ButtonUnderCursor == 0) ?
+		(IconActive | (MouseButtons & LeftButton)) : 0,
+	    x, y);
+	UiDrawLifeBar(unit, x, y);
+    }
 
     if (unit->Player == ThisPlayer) {	// Only for own units.
 	if (unit->HP && unit->HP < 10000) {
@@ -301,24 +307,36 @@ global void DrawUnitInfo(const Unit* unit)
 	//	Building training units.
 	//
 	if (unit->Orders[0].Action == UnitActionTrain) {
-	    if (OriginalTraining || unit->Data.Train.Count == 1) {
-		VideoDrawText(x + 37, y + 8 + 78, GameFont, "Training:");
-		DrawUnitIcon(unit->Player,unit->Data.Train.What[0]->Icon.Icon,
-		    0, x + 107, y + 8 + 70);
+	    if (unit->Data.Train.Count == 1) {
+		if (TheUI.SingleTrainingText) {
+		    VideoDrawText(TheUI.SingleTrainingTextX, TheUI.SingleTrainingTextY,
+			TheUI.SingleTrainingFont, TheUI.SingleTrainingText);
+		}
+		if (TheUI.SingleTrainingButton) {
+		    DrawUnitIcon(unit->Player, unit->Data.Train.What[0]->Icon.Icon,
+			(ButtonAreaUnderCursor == ButtonAreaTraining &&
+			    ButtonUnderCursor == 0) ?
+			    (IconActive | (MouseButtons & LeftButton)) : 0,
+			TheUI.SingleTrainingButton->X, TheUI.SingleTrainingButton->Y);
+		}
 
 		UiDrawCompleted(unit->Data.Train.What[0]->Stats[
 		    unit->Player->Player].Costs[TimeCost],
 		    unit->Data.Train.Ticks);
 	    } else {
-		VideoDrawTextCentered(x + 114, y + 8 + 29, GameFont, "Training...");
-
-		for (i = 0; i < unit->Data.Train.Count; ++i) {
-		    DrawUnitIcon(unit->Player,
-			unit->Data.Train.What[i]->Icon.Icon,
-			(ButtonAreaUnderCursor == ButtonAreaTraining &&
-			    ButtonUnderCursor == i) ?
-			    (IconActive | (MouseButtons&LeftButton)) : 0,
-			TheUI.TrainingButtons[i].X, TheUI.TrainingButtons[i].Y);
+		if (TheUI.TrainingText) {
+		    VideoDrawTextCentered(TheUI.TrainingTextX, TheUI.TrainingTextY,
+			TheUI.TrainingFont, TheUI.TrainingText);
+		}
+		if (TheUI.TrainingButtons) {
+		    for (i = 0; i < unit->Data.Train.Count &&
+			    i < TheUI.NumTrainingButtons; ++i) {
+			DrawUnitIcon(unit->Player, unit->Data.Train.What[i]->Icon.Icon,
+			    (ButtonAreaUnderCursor == ButtonAreaTraining &&
+				ButtonUnderCursor == i) ?
+				(IconActive | (MouseButtons & LeftButton)) : 0,
+			    TheUI.TrainingButtons[i].X, TheUI.TrainingButtons[i].Y);
+		    }
 		}
 
 		UiDrawCompleted(unit->Data.Train.What[0]->Stats[
@@ -332,9 +350,17 @@ global void DrawUnitInfo(const Unit* unit)
 	//	Building upgrading to better type.
 	//
 	if (unit->Orders[0].Action == UnitActionUpgradeTo) {
-	    VideoDrawText(x + 29, y + 8 + 78, GameFont, "Upgrading:");
-	    DrawUnitIcon(unit->Player, unit->Orders[0].Type->Icon.Icon,
-		0, x + 107, y + 8 + 70);
+	    if (TheUI.UpgradingText) {
+		VideoDrawText(TheUI.UpgradingTextX, TheUI.UpgradingTextY,
+		    TheUI.UpgradingFont, TheUI.UpgradingText);
+	    }
+	    if (TheUI.UpgradingButton) {
+		DrawUnitIcon(unit->Player, unit->Orders[0].Type->Icon.Icon,
+		    (ButtonAreaUnderCursor == ButtonAreaUpgrading &&
+			ButtonUnderCursor == 0) ?
+			(IconActive | (MouseButtons & LeftButton)) : 0,
+		    TheUI.UpgradingButton->X, TheUI.UpgradingButton->Y);
+	    }
 
 	    UiDrawCompleted(unit->Orders[0].Type->Stats[
 		unit->Player->Player].Costs[TimeCost],
@@ -346,10 +372,17 @@ global void DrawUnitInfo(const Unit* unit)
 	//	Building research new technologie.
 	//
 	if (unit->Orders[0].Action == UnitActionResearch) {
-	    VideoDrawText(16, y + 8 + 78, GameFont, "Researching:");
-	    DrawUnitIcon(unit->Player,
-		unit->Data.Research.Upgrade->Icon.Icon,
-		0, x + 107, y + 8 + 70);
+	    if (TheUI.ResearchingText) {
+		VideoDrawText(TheUI.ResearchingTextX, TheUI.ResearchingTextY,
+		    TheUI.ResearchingFont, TheUI.ResearchingText);
+	    }
+	    if (TheUI.ResearchingButton) {
+		DrawUnitIcon(unit->Player, unit->Data.Research.Upgrade->Icon.Icon,
+		    (ButtonAreaUnderCursor == ButtonAreaResearching &&
+			ButtonUnderCursor == 0) ?
+			(IconActive | (MouseButtons & LeftButton)) : 0,
+		    TheUI.ResearchingButton->X, TheUI.ResearchingButton->Y);
+	    }
 
 	    UiDrawCompleted(unit->Data.Research.Upgrade->Costs[TimeCost],
 		unit->Player->UpgradeTimers.Upgrades[
@@ -383,19 +416,23 @@ global void DrawUnitInfo(const Unit* unit)
 	// We displayed at least one resource
 	return;
     }
-    
+
     if (type->Transporter && unit->InsideCount) {
+	if (TheUI.TransportingText) {
+	    VideoDrawText(TheUI.TransportingTextX, TheUI.TransportingTextY,
+		TheUI.TransportingFont, TheUI.TransportingText);
+	}
 	uins = unit->UnitInside;
 	for (i = 0; i < unit->InsideCount; ++i, uins = uins->NextContained) {
 	    DrawUnitIcon(unit->Player,uins->Type->Icon.Icon,
-		(ButtonAreaUnderCursor == ButtonAreaInfo && ButtonUnderCursor == i + 3) ?
+		(ButtonAreaUnderCursor == ButtonAreaSelected && ButtonUnderCursor == i) ?
 		    (IconActive | (MouseButtons & LeftButton)) : 0,
-		TheUI.InfoButtons[i + 3].X, TheUI.InfoButtons[i + 3].Y);
-	    UiDrawLifeBar(uins, TheUI.InfoButtons[i + 3].X, TheUI.InfoButtons[i + 3].Y);
+		TheUI.TransportingButtons[i].X, TheUI.TransportingButtons[i].Y);
+	    UiDrawLifeBar(uins, TheUI.TransportingButtons[i].X, TheUI.TransportingButtons[i].Y);
 	    if (uins->Type->CanCastSpell) {
-		UiDrawManaBar(uins, TheUI.InfoButtons[i + 3].X, TheUI.InfoButtons[i + 3].Y);
+		UiDrawManaBar(uins, TheUI.TransportingButtons[i].X, TheUI.TransportingButtons[i].Y);
 	    }
-	    if (ButtonAreaUnderCursor == ButtonAreaInfo && ButtonUnderCursor == i + 3) {
+	    if (ButtonAreaUnderCursor == ButtonAreaTransporting && ButtonUnderCursor == i) {
 		if (uins->Name) {
 		    char buf[128];
 		    sprintf(buf, "%s %s", uins->Type->Name, uins->Name);
@@ -441,13 +478,13 @@ global void DrawUnitInfo(const Unit* unit)
 		if (stats->PiercingDamage < 30 && stats->BasicDamage < 30) {
 		    sprintf(buf, "%d-%d~<+%d+%d~>",
 			(stats->PiercingDamage + 1) / 2, i,
-			stats->BasicDamage-type->_BasicDamage +
+			stats->BasicDamage - type->_BasicDamage +
 			    (int)isqrt(unit->XP / 100) * XpDamage,
 			stats->PiercingDamage-type->_PiercingDamage);
 		} else {
 		    sprintf(buf, "%d-%d~<+%d+%d~>",
-			(stats->PiercingDamage+stats->BasicDamage - 30) / 2, i,
-			stats->BasicDamage-type->_BasicDamage +
+			(stats->PiercingDamage + stats->BasicDamage - 30) / 2, i,
+			stats->BasicDamage - type->_BasicDamage +
 			    (int)isqrt(unit->XP / 100) * XpDamage,
 			stats->PiercingDamage-type->_PiercingDamage);
 		}
@@ -1013,7 +1050,7 @@ global void SetCosts(int mana, int food, const int* costs)
     }
 
     if (costs) {
-	for (i=0; i < MaxCosts; ++i) {
+	for (i = 0; i < MaxCosts; ++i) {
 	    if (Costs[i] != costs[i]) {
 		Costs[i] = costs[i];
 		MustRedraw |= RedrawCosts;
@@ -1034,10 +1071,7 @@ global void SetCosts(int mana, int food, const int* costs)
 */
 global void ClearCosts(void)
 {
-    int costs[MaxCosts];
-
-    memset(costs, 0, sizeof(costs));
-    SetCosts(0, 0, costs);
+    SetCosts(0, 0, NULL);
 }
 
 /*----------------------------------------------------------------------------
@@ -1082,13 +1116,13 @@ global void DrawInfoPanel(void)
             for (i = 0; i < NumSelected; ++i) {
 	        DrawUnitIcon(ThisPlayer,
 		    Selected[i]->Type->Icon.Icon,
-		    (ButtonAreaUnderCursor == ButtonAreaInfo && ButtonUnderCursor == i) ?
+		    (ButtonAreaUnderCursor == ButtonAreaSelected && ButtonUnderCursor == i) ?
 			(IconActive | (MouseButtons & LeftButton)) : 0,
-		    TheUI.InfoButtons[i].X, TheUI.InfoButtons[i].Y);
+		    TheUI.SelectedButtons[i].X, TheUI.SelectedButtons[i].Y);
 		UiDrawLifeBar(Selected[i],
-		    TheUI.InfoButtons[i].X, TheUI.InfoButtons[i].Y);
+		    TheUI.SelectedButtons[i].X, TheUI.SelectedButtons[i].Y);
 
-		if (ButtonAreaUnderCursor == ButtonAreaInfo &&
+		if (ButtonAreaUnderCursor == ButtonAreaSelected &&
 			ButtonUnderCursor == i) {
 		    if (Selected[i]->Name) {
 			char buf[128];
@@ -1121,7 +1155,7 @@ global void DrawInfoPanel(void)
 	    }
 	    DrawInfoPanelBackground(i);
 	    DrawUnitInfo(Selected[0]);
-	    if (ButtonAreaUnderCursor == ButtonAreaInfo && ButtonUnderCursor == 0) {
+	    if (ButtonAreaUnderCursor == ButtonAreaSelected && ButtonUnderCursor == 0) {
 		if (Selected[0]->Name) {
 		    char buf[128];
 
@@ -1225,9 +1259,9 @@ global void UpdateTimer(void)
 {
     if (GameTimer.Running) {
 	if (GameTimer.Increasing) {
-	    GameTimer.Cycles += GameCycle-GameTimer.LastUpdate;
+	    GameTimer.Cycles += GameCycle - GameTimer.LastUpdate;
 	} else {
-	    GameTimer.Cycles -= GameCycle-GameTimer.LastUpdate;
+	    GameTimer.Cycles -= GameCycle - GameTimer.LastUpdate;
 	    if (GameTimer.Cycles < 0) {
 		GameTimer.Cycles = 0;
 	    }
diff --git a/src/ui/mouse.cpp b/src/ui/mouse.cpp
index 39c11cb9a..da23ec0e3 100644
--- a/src/ui/mouse.cpp
+++ b/src/ui/mouse.cpp
@@ -409,8 +409,8 @@ local void HandleMouseOn(int x, int y)
     for (i = 0; i < TheUI.NumButtonButtons; ++i) {
 	if (x >= TheUI.ButtonButtons[i].X &&
 		x <= TheUI.ButtonButtons[i].X + TheUI.ButtonButtons[i].Width + 7 &&
-		y>TheUI.ButtonButtons[i].Y &&
-		y<=TheUI.ButtonButtons[i].Y + TheUI.ButtonButtons[i].Height + 7) {
+		y > TheUI.ButtonButtons[i].Y &&
+		y <= TheUI.ButtonButtons[i].Y + TheUI.ButtonButtons[i].Height + 7) {
 	    ButtonAreaUnderCursor = ButtonAreaButton;
 	    ButtonUnderCursor = i;
 	    CursorOn = CursorOnButton;
@@ -418,28 +418,77 @@ local void HandleMouseOn(int x, int y)
 	    return;
 	}
     }
-    if (NumSelected == 1 && Selected[0]->Type->Building &&
-	    Selected[0]->Orders[0].Action == UnitActionTrain &&
-	    Selected[0]->Data.Train.Count > 1) {
-	for (i = 0; i < TheUI.NumTrainingButtons; ++i) {
-	    if (x >= TheUI.TrainingButtons[i].X &&
-		    x <= TheUI.TrainingButtons[i].X + TheUI.TrainingButtons[i].Width + 7 &&
-		    y > TheUI.TrainingButtons[i].Y &&
-		    y <= TheUI.TrainingButtons[i].Y + TheUI.TrainingButtons[i].Height + 7) {
-		ButtonAreaUnderCursor = ButtonAreaTraining;
-		ButtonUnderCursor = i;
+    if (NumSelected == 1 && Selected[0]->Type->Building) {
+	if (Selected[0]->Orders[0].Action == UnitActionTrain) {
+	    if (Selected[0]->Data.Train.Count == 1) {
+		if (TheUI.SingleTrainingButton &&
+			x >= TheUI.SingleTrainingButton->X &&
+			x <= TheUI.SingleTrainingButton->X + TheUI.SingleTrainingButton->Width + 7 &&
+			y > TheUI.SingleTrainingButton->Y &&
+			y <= TheUI.SingleTrainingButton->Y + TheUI.SingleTrainingButton->Height + 7) {
+		    ButtonAreaUnderCursor = ButtonAreaTraining;
+		    ButtonUnderCursor = 0;
+		    CursorOn = CursorOnButton;
+		    MustRedraw |= RedrawButtonPanel;
+		    return;
+		}
+	    } else {
+		for (i = 0; i < TheUI.NumTrainingButtons; ++i) {
+		    if (x >= TheUI.TrainingButtons[i].X &&
+			    x <= TheUI.TrainingButtons[i].X + TheUI.TrainingButtons[i].Width + 7 &&
+			    y > TheUI.TrainingButtons[i].Y &&
+			    y <= TheUI.TrainingButtons[i].Y + TheUI.TrainingButtons[i].Height + 7) {
+			ButtonAreaUnderCursor = ButtonAreaTraining;
+			ButtonUnderCursor = i;
+			CursorOn = CursorOnButton;
+			MustRedraw |= RedrawButtonPanel;
+			return;
+		    }
+		}
+	    }
+	} else if (Selected[0]->Orders[0].Action == UnitActionUpgradeTo) {
+	    if (x >= TheUI.UpgradingButton->X &&
+		    x <= TheUI.UpgradingButton->X + TheUI.UpgradingButton->Width + 7 &&
+		    y > TheUI.UpgradingButton->Y &&
+		    y <= TheUI.UpgradingButton->Y + TheUI.UpgradingButton->Height + 7) {
+		ButtonAreaUnderCursor = ButtonAreaUpgrading;
+		ButtonUnderCursor = 0;
+		CursorOn = CursorOnButton;
+		MustRedraw |= RedrawButtonPanel;
+		return;
+	    }
+	} else if (Selected[0]->Orders[0].Action == UnitActionResearch) {
+	    if (x >= TheUI.ResearchingButton->X &&
+		    x <= TheUI.ResearchingButton->X + TheUI.ResearchingButton->Width + 7 &&
+		    y > TheUI.ResearchingButton->Y &&
+		    y <= TheUI.ResearchingButton->Y + TheUI.ResearchingButton->Height + 7) {
+		ButtonAreaUnderCursor = ButtonAreaResearching;
+		ButtonUnderCursor = 0;
 		CursorOn = CursorOnButton;
 		MustRedraw |= RedrawButtonPanel;
 		return;
 	    }
 	}
+    }
+    if (NumSelected == 1) {
+	if (TheUI.SingleSelectedButton &&
+		x >= TheUI.SingleSelectedButton->X &&
+		x <= TheUI.SingleSelectedButton->X + TheUI.SingleSelectedButton->Width + 7 &&
+		y > TheUI.SingleSelectedButton->Y &&
+		y <= TheUI.SingleSelectedButton->Y + TheUI.SingleSelectedButton->Height + 7) {
+	    ButtonAreaUnderCursor = ButtonAreaSelected;
+	    ButtonUnderCursor = 0;
+	    CursorOn = CursorOnButton;
+	    MustRedraw |= RedrawButtonPanel;
+	    return;
+	}
     } else {
-	for (i = 0; i < TheUI.NumInfoButtons; ++i) {
-	    if (x >= TheUI.InfoButtons[i].X &&
-		    x <= TheUI.InfoButtons[i].X + TheUI.InfoButtons[i].Width + 7 &&
-		    y > TheUI.InfoButtons[i].Y &&
-		    y <= TheUI.InfoButtons[i].Y + TheUI.InfoButtons[i].Height + 7) {
-		ButtonAreaUnderCursor = ButtonAreaInfo;
+	for (i = 0; i < TheUI.NumSelectedButtons; ++i) {
+	    if (x >= TheUI.SelectedButtons[i].X &&
+		    x <= TheUI.SelectedButtons[i].X + TheUI.SelectedButtons[i].Width + 7 &&
+		    y > TheUI.SelectedButtons[i].Y &&
+		    y <= TheUI.SelectedButtons[i].Y + TheUI.SelectedButtons[i].Height + 7) {
+		ButtonAreaUnderCursor = ButtonAreaSelected;
 		ButtonUnderCursor = i;
 		CursorOn = CursorOnButton;
 		MustRedraw |= RedrawButtonPanel;
@@ -451,7 +500,11 @@ local void HandleMouseOn(int x, int y)
     if (ButtonUnderCursor != -1) {	// remove old display
 	if (ButtonAreaUnderCursor == ButtonAreaMenu) {
 	    MustRedraw |= RedrawMenuButton;
-	} else if (ButtonAreaUnderCursor == ButtonAreaInfo) {
+	} else if (ButtonAreaUnderCursor == ButtonAreaSelected ||
+		ButtonAreaUnderCursor == ButtonAreaTraining ||
+		ButtonAreaUnderCursor == ButtonAreaUpgrading ||
+		ButtonAreaUnderCursor == ButtonAreaResearching ||
+		ButtonAreaUnderCursor == ButtonAreaTransporting) {
 	    MustRedraw |= RedrawInfoPanel;
 	} else {
 	    MustRedraw |= RedrawButtonPanel;
@@ -1410,7 +1463,7 @@ global void UIHandleButtonDown(unsigned button)
 	//
 	//	clicked on info panel - selection shown
 	//
-	if (NumSelected > 1 && ButtonAreaUnderCursor == ButtonAreaInfo) {
+	if (NumSelected > 1 && ButtonAreaUnderCursor == ButtonAreaSelected) {
 	    if (!GameObserve && !GamePaused) {
 		DoSelectionButtons(ButtonUnderCursor, button);
 	    }
@@ -1432,9 +1485,9 @@ global void UIHandleButtonDown(unsigned button)
 		    MustRedraw |= RedrawMenuButton;
 		}
 	    //
-	    //	clicked on info panel
+	    //	clicked on selected button
 	    //
-	    } else if (ButtonAreaUnderCursor == ButtonAreaInfo) {
+	    } else if (ButtonAreaUnderCursor == ButtonAreaSelected) {
 		//
 		//  clicked on single unit shown
 		//
@@ -1442,26 +1495,6 @@ global void UIHandleButtonDown(unsigned button)
 		    PlayGameSound(GameSounds.Click.Sound, MaxSampleVolume);
 		    ViewportCenterViewpoint(TheUI.SelectedViewport, Selected[0]->X,
 			Selected[0]->Y);
-		//
-		//  for transporter
-		//
-		} else if (ButtonUnderCursor > 2 && ButtonUnderCursor < 9) {
-		    if (NumSelected == 1 && Selected[0]->Type->Transporter) {
-			if (!GameObserve && !GamePaused) {
-			    if (Selected[0]->InsideCount >= ButtonUnderCursor - 3) {
-
-				// FIXME: should check if valid here.
-				// n0b0dy: check WHAT?
-				uins=Selected[0]->UnitInside;
-				for (i = 0; i < ButtonUnderCursor - 3; ++i) {
-				    uins = uins->NextContained;
-				}
-				SendCommandUnload(Selected[0],
-				    Selected[0]->X, Selected[0]->Y, uins,
-				    !(KeyModifiers & ModifierShift));
-			    }
-			}
-		    }
 		}
 	    //
 	    //	clicked on training button
@@ -1478,18 +1511,57 @@ global void UIHandleButtonDown(unsigned button)
 		    }
 		}
 	    //
+	    //	clicked on upgrading button
+	    //
+	    } else if (ButtonAreaUnderCursor == ButtonAreaUpgrading) {
+		if (!GameObserve && !GamePaused) {
+		    if (ButtonUnderCursor == 0 && NumSelected == 1) {
+			DebugLevel0Fn("Cancel upgrade %s\n" _C_
+			    Selected[0]->Type->Ident);
+			SendCommandCancelUpgradeTo(Selected[0]);
+		    }
+		}
+	    //
+	    //	clicked on researching button
+	    //
+	    } else if (ButtonAreaUnderCursor == ButtonAreaResearching) {
+		if (!GameObserve && !GamePaused) {
+		    if (ButtonUnderCursor == 0 && NumSelected == 1) {
+			DebugLevel0Fn("Cancel research %s\n" _C_
+			    Selected[0]->Type->Ident);
+			SendCommandCancelResearch(Selected[0]);
+		    }
+		}
+	    //
 	    //	clicked on button panel
 	    //
+	    } else if (ButtonAreaUnderCursor == ButtonAreaTransporting) {
+		//
+		//  for transporter
+		//
+		if (!GameObserve && !GamePaused) {
+		    if (Selected[0]->InsideCount >= ButtonUnderCursor) {
+			// FIXME: should check if valid here.
+			// n0b0dy: check WHAT?
+			uins = Selected[0]->UnitInside;
+			for (i = 0; i < ButtonUnderCursor; ++i) {
+			    uins = uins->NextContained;
+			}
+			SendCommandUnload(Selected[0],
+			    Selected[0]->X, Selected[0]->Y, uins,
+			    !(KeyModifiers & ModifierShift));
+		    }
+		}
 	    } else if (ButtonAreaUnderCursor == ButtonAreaButton) {
 		if (!GameObserve && !GamePaused) {
 		    DoButtonButtonClicked(ButtonUnderCursor);
 		}
 	    }
-	} else if ((MouseButtons&MiddleButton)) {
+	} else if ((MouseButtons & MiddleButton)) {
 	    //
 	    //	clicked on info panel - single unit shown
 	    //
-	    if (ButtonAreaUnderCursor == ButtonAreaInfo &&
+	    if (ButtonAreaUnderCursor == ButtonAreaSelected &&
 		    ButtonUnderCursor == 0 && NumSelected == 1) {
 		PlayGameSound(GameSounds.Click.Sound, MaxSampleVolume);
 		if (TheUI.SelectedViewport->Unit == Selected[0]) {
@@ -1498,7 +1570,7 @@ global void UIHandleButtonDown(unsigned button)
 		    TheUI.SelectedViewport->Unit = Selected[0];
 		}
 	    }
-	} else if ((MouseButtons&RightButton)) {
+	} else if ((MouseButtons & RightButton)) {
 	}
     }
 }
diff --git a/src/ui/script_ui.cpp b/src/ui/script_ui.cpp
index 0ca4cdc17..6313d317b 100644
--- a/src/ui/script_ui.cpp
+++ b/src/ui/script_ui.cpp
@@ -55,6 +55,14 @@
 
 global char* ClickMissile;
 global char* DamageMissile;
+
+typedef struct _info_text_ {
+    char* Text;
+    int Font;
+    int X;
+    int Y;
+} InfoText;
+
 /*----------------------------------------------------------------------------
 --	Functions
 ----------------------------------------------------------------------------*/
@@ -690,6 +698,311 @@ local char* SCM_PopNewStr(SCM* list)
     return gh_scm2newstr(value, NULL);
 }
 
+/**
+**	Parse info panel text
+*/
+local void CclParseInfoText(SCM list, InfoText* text)
+{
+    SCM value;
+
+    memset(text, 0, sizeof(*text));
+
+    while (!gh_null_p(list)) {
+	value = gh_car(list);
+	list = gh_cdr(list);
+	if (gh_eq_p(value, gh_symbol2scm("text"))) {
+	    value = gh_car(list);
+	    list = gh_cdr(list);
+	    text->Text = gh_scm2newstr(value, NULL);
+	} else if (gh_eq_p(value, gh_symbol2scm("font"))) {
+	    value = gh_car(list);
+	    list = gh_cdr(list);
+	    text->Font = CclFontByIdentifier(value);
+	} else if (gh_eq_p(value, gh_symbol2scm("pos"))) {
+	    value = gh_car(list);
+	    list = gh_cdr(list);
+	    text->X = gh_scm2int(gh_car(value));
+	    text->Y = gh_scm2int(gh_car(gh_cdr(value)));
+	} else {
+	    errl("Unsupported tag", value);
+	}
+    }
+}
+
+/**
+**	Parse info panel icon
+*/
+local void CclParseInfoIcon(SCM list, Button* icon)
+{
+    SCM value;
+
+    while (!gh_null_p(list)) {
+	value = gh_car(list);
+	list = gh_cdr(list);
+	if (gh_eq_p(value, gh_symbol2scm("pos"))) {
+	    value = gh_car(list);
+	    list = gh_cdr(list);
+	    icon->X = gh_scm2int(gh_car(value));
+	    icon->Y = gh_scm2int(gh_car(gh_cdr(value)));
+	} else if (gh_eq_p(value, gh_symbol2scm("size"))) {
+	    value = gh_car(list);
+	    list = gh_cdr(list);
+	    icon->Width = gh_scm2int(gh_car(value));
+	    icon->Height = gh_scm2int(gh_car(gh_cdr(value)));
+	} else {
+	    errl("Unsupported tag", value);
+	}
+    }
+}
+
+/**
+**	Parse info panel selected section
+*/
+local void CclParseSelected(SCM list, UI* ui)
+{
+    SCM value;
+    SCM sublist;
+    InfoText text;
+
+    while (!gh_null_p(list)) {
+	value = gh_car(list);
+	list = gh_cdr(list);
+	if (gh_eq_p(value, gh_symbol2scm("single"))) {
+	    sublist = gh_car(list);
+	    list = gh_cdr(list);
+	    while (!gh_null_p(sublist)) {
+		value = gh_car(sublist);
+		sublist = gh_cdr(sublist);
+		if (gh_eq_p(value, gh_symbol2scm("text"))) {
+		    value = gh_car(sublist);
+		    sublist = gh_cdr(sublist);
+		    CclParseInfoText(value, &text);
+		    ui->SingleSelectedText = text.Text;
+		    ui->SingleSelectedFont = text.Font;
+		    ui->SingleSelectedTextX = text.X;
+		    ui->SingleSelectedTextY = text.Y;
+		} else if (gh_eq_p(value, gh_symbol2scm("icon"))) {
+		    value = gh_car(sublist);
+		    sublist = gh_cdr(sublist);
+		    ui->SingleSelectedButton = calloc(1, sizeof(Button));
+		    CclParseInfoIcon(value, ui->SingleSelectedButton);
+		} else {
+		    errl("Unsupported tag", value);
+		}
+	    }
+	} else if (gh_eq_p(value, gh_symbol2scm("multiple"))) {
+	    sublist = gh_car(list);
+	    list = gh_cdr(list);
+	    while (!gh_null_p(sublist)) {
+		value = gh_car(sublist);
+		sublist = gh_cdr(sublist);
+		if (gh_eq_p(value, gh_symbol2scm("text"))) {
+		    value = gh_car(sublist);
+		    sublist = gh_cdr(sublist);
+		    CclParseInfoText(value, &text);
+		    ui->SelectedText = text.Text;
+		    ui->SelectedFont = text.Font;
+		    ui->SelectedTextX = text.X;
+		    ui->SelectedTextY = text.Y;
+		} else if (gh_eq_p(value, gh_symbol2scm("icons"))) {
+		    SCM slist;
+		    int i;
+
+		    slist = gh_car(sublist);
+		    sublist = gh_cdr(sublist);
+		    ui->NumSelectedButtons = gh_length(slist);
+		    ui->SelectedButtons = calloc(ui->NumSelectedButtons,
+			sizeof(Button));
+		    i = 0;
+		    while (!gh_null_p(slist)) {
+			value = gh_car(slist);
+			slist = gh_cdr(slist);
+			CclParseInfoIcon(value, &ui->SelectedButtons[i++]);
+		    }
+		} else {
+		    errl("Unsupported tag", value);
+		}
+	    }
+	} else {
+	    errl("Unsupported tag", value);
+	}
+    }
+}
+
+/**
+**	Parse info panel training section
+*/
+local void CclParseTraining(SCM list, UI* ui)
+{
+    SCM value;
+    SCM sublist;
+    InfoText text;
+
+    while (!gh_null_p(list)) {
+	value = gh_car(list);
+	list = gh_cdr(list);
+	if (gh_eq_p(value, gh_symbol2scm("single"))) {
+	    sublist = gh_car(list);
+	    list = gh_cdr(list);
+	    while (!gh_null_p(sublist)) {
+		value = gh_car(sublist);
+		sublist = gh_cdr(sublist);
+		if (gh_eq_p(value, gh_symbol2scm("text"))) {
+		    value = gh_car(sublist);
+		    sublist = gh_cdr(sublist);
+		    CclParseInfoText(value, &text);
+		    ui->SingleTrainingText = text.Text;
+		    ui->SingleTrainingFont = text.Font;
+		    ui->SingleTrainingTextX = text.X;
+		    ui->SingleTrainingTextY = text.Y;
+		} else if (gh_eq_p(value, gh_symbol2scm("icon"))) {
+		    value = gh_car(sublist);
+		    sublist = gh_cdr(sublist);
+		    ui->SingleTrainingButton = calloc(1, sizeof(Button));
+		    CclParseInfoIcon(value, ui->SingleTrainingButton);
+		} else {
+		    errl("Unsupported tag", value);
+		}
+	    }
+	} else if (gh_eq_p(value, gh_symbol2scm("multiple"))) {
+	    sublist = gh_car(list);
+	    list = gh_cdr(list);
+	    while (!gh_null_p(sublist)) {
+		value = gh_car(sublist);
+		sublist = gh_cdr(sublist);
+		if (gh_eq_p(value, gh_symbol2scm("text"))) {
+		    value = gh_car(sublist);
+		    sublist = gh_cdr(sublist);
+		    CclParseInfoText(value, &text);
+		    ui->TrainingText = text.Text;
+		    ui->TrainingFont = text.Font;
+		    ui->TrainingTextX = text.X;
+		    ui->TrainingTextY = text.Y;
+		} else if (gh_eq_p(value, gh_symbol2scm("icons"))) {
+		    SCM slist;
+		    int i;
+
+		    slist = gh_car(sublist);
+		    sublist = gh_cdr(sublist);
+		    ui->NumTrainingButtons = gh_length(slist);
+		    ui->TrainingButtons = calloc(ui->NumTrainingButtons,
+			sizeof(Button));
+		    i = 0;
+		    while (!gh_null_p(slist)) {
+			value = gh_car(slist);
+			slist = gh_cdr(slist);
+			CclParseInfoIcon(value, &ui->TrainingButtons[i++]);
+		    }
+		} else {
+		    errl("Unsupported tag", value);
+		}
+	    }
+	} else {
+	    errl("Unsupported tag", value);
+	}
+    }
+}
+
+/**
+**	Parse info panel upgrading section
+*/
+local void CclParseUpgrading(SCM list, UI* ui)
+{
+    SCM value;
+    InfoText text;
+
+    while (!gh_null_p(list)) {
+	value = gh_car(list);
+	list = gh_cdr(list);
+	if (gh_eq_p(value, gh_symbol2scm("text"))) {
+	    value = gh_car(list);
+	    list = gh_cdr(list);
+	    CclParseInfoText(value, &text);
+	    ui->UpgradingText = text.Text;
+	    ui->UpgradingFont = text.Font;
+	    ui->UpgradingTextX = text.X;
+	    ui->UpgradingTextY = text.Y;
+	} else if (gh_eq_p(value, gh_symbol2scm("icon"))) {
+	    value = gh_car(list);
+	    list = gh_cdr(list);
+	    ui->UpgradingButton = calloc(1, sizeof(Button));
+	    CclParseInfoIcon(value, ui->UpgradingButton);
+	} else {
+	    errl("Unsupported tag", value);
+	}
+    }
+}
+
+/**
+**	Parse info panel researching section
+*/
+local void CclParseResearching(SCM list, UI* ui)
+{
+    SCM value;
+    InfoText text;
+
+    while (!gh_null_p(list)) {
+	value = gh_car(list);
+	list = gh_cdr(list);
+	if (gh_eq_p(value, gh_symbol2scm("text"))) {
+	    value = gh_car(list);
+	    list = gh_cdr(list);
+	    CclParseInfoText(value, &text);
+	    ui->ResearchingText = text.Text;
+	    ui->ResearchingFont = text.Font;
+	    ui->ResearchingTextX = text.X;
+	    ui->ResearchingTextY = text.Y;
+	} else if (gh_eq_p(value, gh_symbol2scm("icon"))) {
+	    value = gh_car(list);
+	    list = gh_cdr(list);
+	    ui->ResearchingButton = calloc(1, sizeof(Button));
+	    CclParseInfoIcon(value, ui->ResearchingButton);
+	} else {
+	    errl("Unsupported tag", value);
+	}
+    }
+}
+
+/**
+**	Parse info panel transporting section
+*/
+local void CclParseTransporting(SCM list, UI* ui)
+{
+    SCM value;
+    InfoText text;
+
+    while (!gh_null_p(list)) {
+	value = gh_car(list);
+	list = gh_cdr(list);
+	if (gh_eq_p(value, gh_symbol2scm("text"))) {
+	    value = gh_car(list);
+	    list = gh_cdr(list);
+	    CclParseInfoText(value, &text);
+	    ui->TransportingText = text.Text;
+	    ui->TransportingFont = text.Font;
+	    ui->TransportingTextX = text.X;
+	    ui->TransportingTextY = text.Y;
+	} else if (gh_eq_p(value, gh_symbol2scm("icons"))) {
+	    SCM sublist;
+	    int i;
+
+	    sublist = gh_car(list);
+	    list = gh_cdr(list);
+	    ui->NumTransportingButtons = gh_length(sublist);
+	    ui->TransportingButtons = calloc(ui->NumTransportingButtons,
+		sizeof(Button));
+	    i = 0;
+	    while (!gh_null_p(sublist)) {
+		value = gh_car(sublist);
+		sublist = gh_cdr(sublist);
+		CclParseInfoIcon(value, &ui->TransportingButtons[i++]);
+	    }
+	} else {
+	    errl("Unsupported tag", value);
+	}
+    }
+}
+
 /**
 **	Define the look+feel of the user interface.
 **
@@ -900,48 +1213,95 @@ local SCM CclDefineUI(SCM list)
 		}
 	    }
 	} else if (gh_eq_p(value, gh_symbol2scm("info-panel"))) {
-	    sublist = gh_car(list);
-	    list = gh_cdr(list);
-	    ui->InfoPanel.File = SCM_PopNewStr(&sublist);
-	    ui->InfoPanelX = SCM_PopInt(&sublist);
-	    ui->InfoPanelY = SCM_PopInt(&sublist);
-	    ui->InfoPanelW = SCM_PopInt(&sublist);
-	    ui->InfoPanelH = SCM_PopInt(&sublist);
-	} else if (gh_eq_p(value, gh_symbol2scm("completed-bar"))) {
 	    sublist = gh_car(list);
 	    list = gh_cdr(list);
 	    while (!gh_null_p(sublist)) {
 		value = gh_car(sublist);
 		sublist = gh_cdr(sublist);
-		if (gh_eq_p(value, gh_symbol2scm("color"))) {
+		if (gh_eq_p(value, gh_symbol2scm("panel"))) {
+		    SCM slist;
+
+		    slist = gh_car(sublist);
+		    sublist = gh_cdr(sublist);
+		    while (!gh_null_p(slist)) {
+			value = gh_car(slist);
+			slist = gh_cdr(slist);
+			if (gh_eq_p(value, gh_symbol2scm("file"))) {
+			    value = gh_car(slist);
+			    slist = gh_cdr(slist);
+			    ui->InfoPanel.File = gh_scm2newstr(value, NULL);
+			} else if (gh_eq_p(value, gh_symbol2scm("pos"))) {
+			    value = gh_car(slist);
+			    slist = gh_cdr(slist);
+			    ui->InfoPanelX = gh_scm2int(gh_car(value));
+			    ui->InfoPanelY = gh_scm2int(gh_car(gh_cdr(value)));
+			} else if (gh_eq_p(value, gh_symbol2scm("size"))) {
+			    value = gh_car(slist);
+			    slist = gh_cdr(slist);
+			    ui->InfoPanelW = gh_scm2int(gh_car(value));
+			    ui->InfoPanelH = gh_scm2int(gh_car(gh_cdr(value)));
+			} else {
+			    errl("Unsupported tag", value);
+			}
+		    }
+		} else if (gh_eq_p(value, gh_symbol2scm("selected"))) {
 		    value = gh_car(sublist);
 		    sublist = gh_cdr(sublist);
-		    ui->CompleteBarColorRGB.D24.a = gh_scm2int(gh_car(value));
-		    ui->CompleteBarColorRGB.D24.b = gh_scm2int(gh_car(gh_cdr(value)));
-		    ui->CompleteBarColorRGB.D24.c = gh_scm2int(gh_car(gh_cdr(gh_cdr(value))));
-		} else if (gh_eq_p(value, gh_symbol2scm("pos"))) {
+		    CclParseSelected(value, ui);
+		} else if (gh_eq_p(value, gh_symbol2scm("training"))) {
 		    value = gh_car(sublist);
 		    sublist = gh_cdr(sublist);
-		    ui->CompleteBarX = gh_scm2int(gh_car(value));
-		    ui->CompleteBarY = gh_scm2int(gh_car(gh_cdr(value)));
-		} else if (gh_eq_p(value, gh_symbol2scm("size"))) {
+		    CclParseTraining(value, ui);
+		} else if (gh_eq_p(value, gh_symbol2scm("upgrading"))) {
 		    value = gh_car(sublist);
 		    sublist = gh_cdr(sublist);
-		    ui->CompleteBarW = gh_scm2int(gh_car(value));
-		    ui->CompleteBarH = gh_scm2int(gh_car(gh_cdr(value)));
-		} else if (gh_eq_p(value, gh_symbol2scm("text"))) {
+		    CclParseUpgrading(value, ui);
+		} else if (gh_eq_p(value, gh_symbol2scm("researching"))) {
 		    value = gh_car(sublist);
 		    sublist = gh_cdr(sublist);
-		    ui->CompleteBarText = gh_scm2newstr(value, NULL);
-		} else if (gh_eq_p(value, gh_symbol2scm("font"))) {
+		    CclParseResearching(value, ui);
+		} else if (gh_eq_p(value, gh_symbol2scm("transporting"))) {
 		    value = gh_car(sublist);
 		    sublist = gh_cdr(sublist);
-		    ui->CompleteBarFont = CclFontByIdentifier(value);
-		} else if (gh_eq_p(value, gh_symbol2scm("text-pos"))) {
-		    value = gh_car(sublist);
+		    CclParseTransporting(value, ui);
+		} else if (gh_eq_p(value, gh_symbol2scm("completed-bar"))) {
+		    SCM slist;
+
+		    slist = gh_car(sublist);
 		    sublist = gh_cdr(sublist);
-		    ui->CompleteTextX = gh_scm2int(gh_car(value));
-		    ui->CompleteTextY = gh_scm2int(gh_car(gh_cdr(value)));
+		    while (!gh_null_p(slist)) {
+			value = gh_car(slist);
+			slist = gh_cdr(slist);
+			if (gh_eq_p(value, gh_symbol2scm("color"))) {
+			    value = gh_car(slist);
+			    slist = gh_cdr(slist);
+			    ui->CompletedBarColorRGB.D24.a = gh_scm2int(gh_car(value));
+			    ui->CompletedBarColorRGB.D24.b = gh_scm2int(gh_car(gh_cdr(value)));
+			    ui->CompletedBarColorRGB.D24.c = gh_scm2int(gh_car(gh_cdr(gh_cdr(value))));
+			} else if (gh_eq_p(value, gh_symbol2scm("pos"))) {
+			    value = gh_car(slist);
+			    slist = gh_cdr(slist);
+			    ui->CompletedBarX = gh_scm2int(gh_car(value));
+			    ui->CompletedBarY = gh_scm2int(gh_car(gh_cdr(value)));
+			} else if (gh_eq_p(value, gh_symbol2scm("size"))) {
+			    value = gh_car(slist);
+			    slist = gh_cdr(slist);
+			    ui->CompletedBarW = gh_scm2int(gh_car(value));
+			    ui->CompletedBarH = gh_scm2int(gh_car(gh_cdr(value)));
+			} else if (gh_eq_p(value, gh_symbol2scm("text"))) {
+			    InfoText text;
+
+			    value = gh_car(slist);
+			    slist = gh_cdr(slist);
+			    CclParseInfoText(value, &text);
+			    ui->CompletedBarText = text.Text;
+			    ui->CompletedBarFont = text.Font;
+			    ui->CompletedBarTextX = text.X;
+			    ui->CompletedBarTextY = text.Y;
+			} else {
+			    errl("Unsupported tag", value);
+			}
+		    }
 		} else {
 		    errl("Unsupported tag", value);
 		}
@@ -1128,71 +1488,6 @@ local SCM CclDefineUI(SCM list)
 		    errl("Unsupported tag", value);
 		}
 	    }
-	} else if (gh_eq_p(value, gh_symbol2scm("info-buttons"))) {
-	    SCM slist;
-	    SCM sslist;
-	    Button* b;
-
-	    slist = gh_car(list);
-	    list = gh_cdr(list);
-	    while (!gh_null_p(slist)) {
-		sslist = gh_car(slist);
-		slist = gh_cdr(slist);
-		ui->NumInfoButtons++;
-		ui->InfoButtons = realloc(ui->InfoButtons,
-		    ui->NumInfoButtons*sizeof(*ui->InfoButtons));
-		b = &ui->InfoButtons[ui->NumInfoButtons - 1];
-		while (!gh_null_p(sslist)) {
-		    value = gh_car(sslist);
-		    sslist = gh_cdr(sslist);
-		    if (gh_eq_p(value, gh_symbol2scm("pos"))) {
-			value = gh_car(sslist);
-			sslist = gh_cdr(sslist);
-			b->X = gh_scm2int(gh_car(value));
-			b->Y = gh_scm2int(gh_car(gh_cdr(value)));
-		    } else if (gh_eq_p(value, gh_symbol2scm("size"))) {
-			value = gh_car(sslist);
-			sslist = gh_cdr(sslist);
-			b->Width = gh_scm2int(gh_car(value));
-			b->Height = gh_scm2int(gh_car(gh_cdr(value)));
-		    } else {
-			errl("Unsupported tag", value);
-		    }
-		}
-	    }
-	} else if (gh_eq_p(value, gh_symbol2scm("training-buttons"))) {
-	    SCM slist;
-	    SCM sslist;
-	    Button* b;
-
-	    slist = gh_car(list);
-	    list = gh_cdr(list);
-	    while (!gh_null_p(slist)) {
-		sslist = gh_car(slist);
-		slist = gh_cdr(slist);
-		ui->NumTrainingButtons++;
-		ui->TrainingButtons=realloc(ui->TrainingButtons,
-		    ui->NumTrainingButtons * sizeof(*ui->TrainingButtons));
-		b=&ui->TrainingButtons[ui->NumTrainingButtons - 1];
-		while (!gh_null_p(sslist)) {
-		    value = gh_car(sslist);
-		    sslist = gh_cdr(sslist);
-		    if (gh_eq_p(value, gh_symbol2scm("pos"))) {
-			value = gh_car(sslist);
-			sslist = gh_cdr(sslist);
-			b->X = gh_scm2int(gh_car(value));
-			b->Y = gh_scm2int(gh_car(gh_cdr(value)));
-		    } else if (gh_eq_p(value, gh_symbol2scm("size"))) {
-			value = gh_car(sslist);
-			sslist = gh_cdr(sslist);
-			b->Width = gh_scm2int(gh_car(value));
-			b->Height = gh_scm2int(gh_car(gh_cdr(value)));
-		    } else {
-			errl("Unsupported tag", value);
-		    }
-
-		}
-	    }
 	} else if (gh_eq_p(value, gh_symbol2scm("button-buttons"))) {
 	    SCM slist;
 	    SCM sslist;
diff --git a/src/ui/ui.cpp b/src/ui/ui.cpp
index fe0f77ce5..8a2676d31 100644
--- a/src/ui/ui.cpp
+++ b/src/ui/ui.cpp
@@ -154,8 +154,8 @@ global void InitUserInterface(const char* race_name)
 	SetViewportMode(VIEWPORT_SINGLE);
     }
 
-    TheUI.CompleteBarColor = VideoMapRGB(TheUI.CompleteBarColorRGB.D24.a,
-	TheUI.CompleteBarColorRGB.D24.b, TheUI.CompleteBarColorRGB.D24.c);
+    TheUI.CompletedBarColor = VideoMapRGB(TheUI.CompletedBarColorRGB.D24.a,
+	TheUI.CompletedBarColorRGB.D24.b, TheUI.CompletedBarColorRGB.D24.c);
     TheUI.ViewportCursorColor = ColorWhite;
 }
 
@@ -343,23 +343,152 @@ local void SaveUi(CLFile* file, const UI* ui)
     }
     CLprintf(file, ")\n");
 
-    CLprintf(file, "  'info-panel (list \"%s\" %d %d %d %d)\n",
-	ui->InfoPanel.File,
-	ui->InfoPanelX, ui->InfoPanelY,
-	ui->InfoPanelW, ui->InfoPanelH);
+    CLprintf(file, "\n  'info-panel (list");
+    CLprintf(file, "\n    'panel (list");
+    CLprintf(file, "\n      'file \"%s\"", ui->InfoPanel.File);
+    CLprintf(file, "\n      'pos '(%d %d)", ui->InfoPanelX, ui->InfoPanelY);
+    CLprintf(file, "\n      'size '(%d %d))", ui->InfoPanelW, ui->InfoPanelH);
 
-    CLprintf(file, "\n  'completed-bar '(");
-    CLprintf(file, "\n    color (%d %d %d)", ui->CompleteBarColorRGB.D24.a,
-	ui->CompleteBarColorRGB.D24.b, ui->CompleteBarColorRGB.D24.c);
-    CLprintf(file, "\n    pos (%3d %3d)", ui->CompleteBarX, ui->CompleteBarY);
-    CLprintf(file, "\n    size (%d %d)", ui->CompleteBarW, ui->CompleteBarH);
-    CLprintf(file, "\n    text \"%s\"", ui->CompleteBarText);
-    CLprintf(file, "\n    font %s", FontNames[ui->CompleteBarFont]);
-    CLprintf(file, "\n    text-pos (%3d %3d)",
-	ui->CompleteTextX, ui->CompleteTextY);
-    CLprintf(file, ")\n\n");
+    CLprintf(file, "\n    'selected (list");
+    CLprintf(file, "\n      'single (list");
+    if (ui->SingleSelectedText) {
+	CLprintf(file, "\n        'text (list");
+	CLprintf(file, "\n          'text \"%s\"", ui->SingleSelectedText);
+	CLprintf(file, "\n          'font '%s", FontNames[ui->SingleSelectedFont]);
+	CLprintf(file, "\n          'pos '(%d %d))",
+	    ui->SingleSelectedTextX, ui->SingleSelectedTextY);
+    }
+    if (ui->SingleSelectedButton) {
+	CLprintf(file, "\n        'icon (list");
+	CLprintf(file, "\n          'pos '(%d %d) 'size '(%d %d))",
+	    ui->SingleSelectedButton->X, ui->SingleSelectedButton->Y,
+	    ui->SingleSelectedButton->Width, ui->SingleSelectedButton->Height);
+    }
+    CLprintf(file, ")");
+    CLprintf(file, "\n      'multiple (list");
+    if (ui->SelectedText) {
+	CLprintf(file, "\n        'text (list");
+	CLprintf(file, "\n          'text \"%s\"", ui->SelectedText);
+	CLprintf(file, "\n          'font '%s", FontNames[ui->SelectedFont]);
+	CLprintf(file, "\n          'pos '(%d %d))",
+	    ui->SelectedTextX, ui->SelectedTextY);
+    }
+    if (ui->SelectedButtons) {
+	CLprintf(file, "\n        'icons (list");
+	for (i = 0; i < ui->NumSelectedButtons; ++i) {
+	    CLprintf(file, "\n          (list 'pos '(%d %d) 'size '(%d %d))",
+		ui->SelectedButtons[i].X, ui->SelectedButtons[i].Y,
+		ui->SelectedButtons[i].Width, ui->SelectedButtons[i].Height);
+	}
+	CLprintf(file, ")");
+    }
+    CLprintf(file, ")");
+    CLprintf(file, ")");
 
-    CLprintf(file, "  'button-panel (list \"%s\" %d %d)\n",
+    CLprintf(file, "\n    'training (list");
+    CLprintf(file, "\n      'single (list");
+    if (ui->SingleTrainingText) {
+	CLprintf(file, "\n        'text (list");
+	CLprintf(file, "\n          'text \"%s\"", ui->SingleTrainingText);
+	CLprintf(file, "\n          'font '%s", FontNames[ui->SingleTrainingFont]);
+	CLprintf(file, "\n          'pos '(%d %d))",
+	    ui->SingleTrainingTextX, ui->SingleTrainingTextY);
+    }
+    if (ui->SingleTrainingButton) {
+	CLprintf(file, "\n        'icon (list");
+	CLprintf(file, "\n          'pos '(%d %d) 'size '(%d %d))",
+	    ui->SingleTrainingButton->X, ui->SingleTrainingButton->Y,
+	    ui->SingleTrainingButton->Width, ui->SingleTrainingButton->Height);
+    }
+    CLprintf(file, ")");
+    CLprintf(file, "\n      'multiple (list");
+    if (ui->TrainingText) {
+	CLprintf(file, "\n        'text (list");
+	CLprintf(file, "\n          'text \"%s\"", ui->TrainingText);
+	CLprintf(file, "\n          'font '%s", FontNames[ui->TrainingFont]);
+	CLprintf(file, "\n          'pos '(%d %d))",
+	    ui->TrainingTextX, ui->TrainingTextY);
+    }
+    if (ui->TrainingButtons) {
+	CLprintf(file, "\n        'icons (list");
+	for (i = 0; i < ui->NumTrainingButtons; ++i) {
+	    CLprintf(file, "\n          (list 'pos '(%d %d) 'size '(%d %d))",
+		ui->TrainingButtons[i].X, ui->TrainingButtons[i].Y,
+		ui->TrainingButtons[i].Width, ui->TrainingButtons[i].Height);
+	}
+	CLprintf(file, ")");
+    }
+    CLprintf(file, ")");
+    CLprintf(file, ")");
+
+    CLprintf(file, "\n    'upgrading (list");
+    if (ui->UpgradingText) {
+	CLprintf(file, "\n      'text (list");
+	CLprintf(file, "\n        'text \"%s\"", ui->UpgradingText);
+	CLprintf(file, "\n        'font '%s", FontNames[ui->UpgradingFont]);
+	CLprintf(file, "\n        'pos '(%d %d))",
+	    ui->UpgradingTextX, ui->UpgradingTextY);
+    }
+    if (ui->UpgradingButton) {
+	CLprintf(file, "\n      'icon (list");
+	CLprintf(file, "\n        'pos '(%d %d)",
+	    ui->UpgradingButton->X, ui->UpgradingButton->Y);
+	CLprintf(file, "\n        'size '(%d %d))",
+	    ui->UpgradingButton->Width, ui->UpgradingButton->Height);
+    }
+    CLprintf(file, ")");
+
+    CLprintf(file, "\n    'researching (list");
+    if (ui->ResearchingText) {
+	CLprintf(file, "\n      'text (list");
+	CLprintf(file, "\n        'text \"%s\"", ui->ResearchingText);
+	CLprintf(file, "\n        'font '%s", FontNames[ui->ResearchingFont]);
+	CLprintf(file, "\n        'pos '(%d %d))",
+	    ui->ResearchingTextX, ui->ResearchingTextY);
+    }
+    if (ui->ResearchingButton) {
+	CLprintf(file, "\n      'icon (list");
+	CLprintf(file, "\n        'pos '(%d %d)",
+	    ui->ResearchingButton->X, ui->ResearchingButton->Y);
+	CLprintf(file, "\n        'size '(%d %d))",
+	    ui->ResearchingButton->Width, ui->ResearchingButton->Height);
+    }
+    CLprintf(file, ")");
+
+    CLprintf(file, "\n    'transporting (list");
+    if (ui->TransportingText) {
+	CLprintf(file, "\n      'text (list");
+	CLprintf(file, "\n        'text \"%s\"", ui->TransportingText);
+	CLprintf(file, "\n        'font '%s", FontNames[ui->TransportingFont]);
+	CLprintf(file, "\n        'pos '(%d %d))",
+	    ui->TransportingTextX, ui->TransportingTextY);
+    }
+    if (ui->TransportingButtons) {
+	CLprintf(file, "\n      'icons (list");
+	for (i = 0; i < ui->NumTransportingButtons; ++i) {
+	    CLprintf(file, "\n        (list 'pos '(%d %d) 'size '(%d %d))",
+		ui->TransportingButtons[i].X, ui->TransportingButtons[i].Y,
+		ui->TransportingButtons[i].Width, ui->TransportingButtons[i].Height);
+	}
+	CLprintf(file, ")");
+    }
+    CLprintf(file, ")");
+
+    CLprintf(file, "\n    'completed-bar (list");
+    CLprintf(file, "\n      'color '(%d %d %d)", ui->CompletedBarColorRGB.D24.a,
+	ui->CompletedBarColorRGB.D24.b, ui->CompletedBarColorRGB.D24.c);
+    CLprintf(file, "\n      'pos '(%d %d)", ui->CompletedBarX, ui->CompletedBarY);
+    CLprintf(file, "\n      'size '(%d %d)", ui->CompletedBarW, ui->CompletedBarH);
+    CLprintf(file, "\n      'text (list", ui->CompletedBarText);
+    CLprintf(file, "\n        'text \"%s\"", ui->CompletedBarText);
+    CLprintf(file, "\n        'font '%s", FontNames[ui->CompletedBarFont]);
+    CLprintf(file, "\n        'pos '(%d %d))",
+	ui->CompletedBarTextX, ui->CompletedBarTextY);
+    CLprintf(file, ")");
+
+    CLprintf(file, ")\n");    // 'info-panel
+
+    CLprintf(file, "\n  'button-panel (list \"%s\" %d %d)",
 	ui->ButtonPanel.File, ui->ButtonPanelX, ui->ButtonPanelY);
 
     CLprintf(file, "\n  'map-area (list");
@@ -426,22 +555,8 @@ local void SaveUi(CLFile* file, const UI* ui)
 	ui->NetworkDiplomacyButton.Text);
     CLprintf(file, "\n    style %s",
 	MenuButtonStyle(ui->NetworkDiplomacyButton.Button));
-    CLprintf(file, ")");
+    CLprintf(file, ")\n");
 
-    CLprintf(file, "\n\n  'info-buttons '(");
-    for (i = 0; i < ui->NumInfoButtons; ++i) {
-	CLprintf(file, "\n    (pos (%3d %3d) size (%d %d))",
-	    ui->InfoButtons[i].X, ui->InfoButtons[i].Y,
-	    ui->InfoButtons[i].Width, ui->InfoButtons[i].Height);
-    }
-    CLprintf(file, ")");
-    CLprintf(file, "\n  'training-buttons '(");
-    for (i = 0; i < ui->NumTrainingButtons; ++i) {
-	CLprintf(file, "\n    (pos (%3d %3d) size (%d %d))",
-	    ui->TrainingButtons[i].X, ui->TrainingButtons[i].Y,
-	    ui->TrainingButtons[i].Width, ui->TrainingButtons[i].Height);
-    }
-    CLprintf(file, ")");
     CLprintf(file, "\n  'button-buttons '(");
     for (i = 0; i < ui->NumButtonButtons; ++i) {
 	CLprintf(file, "\n    (pos (%3d %3d) size (%d %d))",
@@ -571,9 +686,21 @@ global void CleanUI(UI* ui)
 
     // Info Panel
     free(ui->InfoPanel.File);
-
-    // Completed Bar
-    free(ui->CompleteBarText);
+    free(ui->SingleSelectedButton);
+    free(ui->SingleSelectedText);
+    free(ui->SelectedButtons);
+    free(ui->SelectedText);
+    free(ui->SingleTrainingButton);
+    free(ui->SingleTrainingText);
+    free(ui->TrainingButtons);
+    free(ui->TrainingText);
+    free(ui->UpgradingButton);
+    free(ui->UpgradingText);
+    free(ui->ResearchingButton);
+    free(ui->ResearchingText);
+    free(ui->TransportingButtons);
+    free(ui->TransportingText);
+    free(ui->CompletedBarText);
 
     // Button Panel
     free(ui->ButtonPanel.File);
@@ -591,8 +718,6 @@ global void CleanUI(UI* ui)
     free(ui->MenuButton.Text);
     free(ui->NetworkMenuButton.Text);
     free(ui->NetworkDiplomacyButton.Text);
-    free(ui->InfoButtons);
-    free(ui->TrainingButtons);
     free(ui->ButtonButtons);
 
     // Cursors