(svn r9636) [cpp_gui] -Add: Table widget (another widget container besides Panel). cpp_gui
authorKUDr
Sun, 15 Apr 2007 14:20:35 +0000
branchcpp_gui
changeset 6305 aa0faea50ab5
parent 6304 38b7e46e2def
child 6306 036aa28ca80e
(svn r9636) [cpp_gui] -Add: Table widget (another widget container besides Panel).
projects/openttd.vcproj
projects/openttd_vs80.vcproj
source.list
src/intro_gui.cpp
src/widget/widget.h
src/widget/widget_table.cpp
--- a/projects/openttd.vcproj	Sun Apr 15 14:17:40 2007 +0000
+++ b/projects/openttd.vcproj	Sun Apr 15 14:20:35 2007 +0000
@@ -1103,6 +1103,9 @@
 				RelativePath=".\..\src\widget\widget_stickybox.cpp">
 			</File>
 			<File
+				RelativePath=".\..\src\widget\widget_table.cpp">
+			</File>
+			<File
 				RelativePath=".\..\src\widget\widget_types.h">
 			</File>
 			<File
--- a/projects/openttd_vs80.vcproj	Sun Apr 15 14:17:40 2007 +0000
+++ b/projects/openttd_vs80.vcproj	Sun Apr 15 14:20:35 2007 +0000
@@ -1688,6 +1688,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\widget\widget_table.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\widget\widget_types.h"
 				>
 			</File>
--- a/source.list	Sun Apr 15 14:17:40 2007 +0000
+++ b/source.list	Sun Apr 15 14:20:35 2007 +0000
@@ -340,6 +340,7 @@
 widget/widget_resizebox.cpp
 widget/widget_scrollbar.cpp
 widget/widget_stickybox.cpp
+widget/widget_table.cpp
 widget/widget_types.h
 widget/window_event.cpp
 widget/window_event_base.h
--- a/src/intro_gui.cpp	Sun Apr 15 14:17:40 2007 +0000
+++ b/src/intro_gui.cpp	Sun Apr 15 14:20:35 2007 +0000
@@ -167,6 +167,8 @@
 	CCountedPtr<gui::Panel> m_panel2;
 	CCountedPtr<gui::Panel> m_panel3;
 	CCountedPtr<gui::Panel> m_panel4;
+	CCountedPtr<gui::Table> m_table1;
+	CCountedPtr<gui::Table> m_table2;
 
 	gui::WidgetPtr m_label1;
 	gui::WidgetPtr m_button1;
@@ -178,7 +180,7 @@
 
 	WindowT()
 		: BaseWindow(WC_TEST1, STR_015B_OPENTTD, COLOUR_BROWN, gui::FF_NO_RESIZE_BOX)
-		, m_num_dyn_buttons(5)
+		, m_num_dyn_buttons(2)
 	{
 	}
 
@@ -207,6 +209,32 @@
 		AddWidget(m_panel1, gui::Panel::BOTTOM);
 
 
+
+
+		m_table2 = new gui::Table(this, gui::FF_NO_FRAME | gui::FF_IGNORE_PARENT_FRAME | gui::FF_SAME_SIZE, 0, COLOUR_TRANSPARENT);
+
+		m_button_add = new gui::TextButton(m_table2, STR_NULL, /*gui::FF_MIN_SIZE |*/ gui::FF_ALIGN_HCENTER | gui::FF_ALIGN_VCENTER, STR_0305_QUIT_OPENTTD, COLOUR_YELLOW);
+		m_button_add->m_text = "Add";
+		m_button_add->AddReflectHandlerT(&WindowT::ButtonAdd_OnClick);
+		m_table2->AddWidget(m_button_add, 0, 0);
+
+		m_button_remove = new gui::TextButton(m_table2, STR_NULL, /*gui::FF_MIN_SIZE |*/ gui::FF_ALIGN_HCENTER | gui::FF_ALIGN_VCENTER, STR_0305_QUIT_OPENTTD, COLOUR_YELLOW);
+		m_button_remove->m_text = "Del";
+		m_button_remove->AddReflectHandlerT(&WindowT::ButtonRemove_OnClick);
+		m_table2->AddWidget(m_button_remove, 1, 0);
+
+		m_button1 = new gui::TextButton(m_table2, STR_0304_QUIT, /*gui::FF_MIN_SIZE |*/ gui::FF_ALIGN_HCENTER | gui::FF_ALIGN_VCENTER, STR_0305_QUIT_OPENTTD, COLOUR_YELLOW);
+		m_button1->AddReflectHandlerT(&WindowT::Button1_OnClick);
+		m_table2->AddWidget(m_button1, 2, 0);
+
+		AddWidget(m_table2, gui::Panel::BOTTOM);
+
+
+
+
+
+
+
 		m_panel2 = new gui::Panel(this, gui::FF_NONE, 0, COLOUR_GREY);
 		for (int i = 0; i < m_num_dyn_buttons; i++) {
 			CCountedPtr<gui::TextButton> bu = new gui::TextButton(m_panel2, STR_000F_PASSENGERS + i, gui::FF_MIN_SIZE | gui::FF_ALIGN_HCENTER | gui::FF_ALIGN_VCENTER, STR_00D0_NOTHING + i, COLOUR_PARENT);
@@ -230,6 +258,59 @@
 		}
 		AddWidget(m_panel4, gui::Panel::RIGHT);
 
+		//m_table1 = new gui::Table(this, gui::FF_NONE, 0, COLOUR_GREY);
+		//for (int16 i = 1; i <= 9; i++) {
+		//	CCountedPtr<gui::TextButton> bu = new gui::TextButton(m_table1, STR_NULL, gui::FF_MIN_SIZE | gui::FF_ALIGN_LEFT | gui::FF_ALIGN_TOP, STR_00D0_NOTHING + i, COLOUR_PARENT);
+		//	char num_str[16];
+		//	sprintf(num_str, "%d", i);
+		//	bu->m_text = num_str;
+		//	int16 x_pos = (i - 1) % 3;
+		//	int16 y_pos = 2 - (i - 1) / 3;
+		//	m_table1->AddWidget(bu, x_pos, y_pos);
+		//}
+		static const char *but_text[] = {
+			"C", "/", "*", "-",
+			"7", "8", "9", "+",
+			"4", "5", "6",
+			"1", "2", "3", "=",
+			"0",      "."
+		};
+		static const int16 but_x_pos[] = {
+			0, 1, 2, 3,
+			0, 1, 2, 3,
+			0, 1, 2,
+			0, 1, 2, 3,
+			0,    2
+		};
+		static const int16 but_y_pos[] = {
+			0, 0, 0, 0,
+			1, 1, 1, 1,
+			2, 2, 2,
+			3, 3, 3, 3,
+			4,    4
+		};
+		static const int16 but_x_span[] = {
+			1, 1, 1, 1,
+			1, 1, 1, 1,
+			1, 1, 1,
+			1, 1, 1, 1,
+			2,    1
+		};
+		static const int16 but_y_span[] = {
+			1, 1, 1, 1,
+			1, 1, 1, 2,
+			1, 1, 1,
+			1, 1, 1, 2,
+			1,    1
+		};
+		m_table1 = new gui::Table(this, gui::FF_NONE | gui::FF_SAME_SIZE, 0, COLOUR_GREY);
+		for (int i = 0; i < (int)lengthof(but_text); i++) {
+			CCountedPtr<gui::TextButton> bu = new gui::TextButton(m_table1, STR_NULL, /*gui::FF_MIN_SIZE |*/ gui::FF_ALIGN_LEFT | gui::FF_ALIGN_TOP, STR_00D0_NOTHING + i, COLOUR_PARENT);
+			bu->m_text = but_text[i];
+			m_table1->AddWidget(bu, but_x_pos[i], but_y_pos[i], but_x_span[i], but_y_span[i]);
+		}
+		AddWidget(m_table1, gui::Panel::CENTER);
+
 
 		///* add controls */
 		//m_label1 = new gui::Label(this, 22, gui::FF_NONE, Rect16(104, 155, 231, 166), STR_0305_QUIT_OPENTTD, STR_0305_QUIT_OPENTTD, COLOUR_BROWN);
--- a/src/widget/widget.h	Sun Apr 15 14:17:40 2007 +0000
+++ b/src/widget/widget.h	Sun Apr 15 14:20:35 2007 +0000
@@ -290,6 +290,15 @@
 	/*virtual*/ void OnLeftButtonDown(EvtLeftButtonDown &ev);
 	/*virtual*/ void OnRightButtonDown(EvtRightButtonDown &ev);
 	/*virtual*/ void OnResize(EvtResize &ev);
+
+	template <class Tcls, void (Tcls::*Tenum_proc)(typename Tcls::Slot &, int) > void EnumSlotsT(Tcls *cls)
+	{
+		int slot_idx = 0;
+		for (WidgetIterator it = m_widgets.begin(); it != m_widgets.end(); slot_idx++) {
+			SlotPtr &slot = (*(it++));
+			(cls->*Tenum_proc)(*(typename Tcls::Slot*)(Slot*)slot, slot_idx);
+		}
+	}
 };
 
 struct Panel : CompositeWidget {
@@ -314,7 +323,25 @@
 		: CompositeWidget(container, feature_flags, tooltips, color)
 	{}
 
-	virtual CompositeWidget::WidgetIterator AddWidget(Widget *wd, Placement pp);
+	virtual CompositeWidget::WidgetIterator AddWidget(Widget *wi, Placement pp);
+
+	/*virtual*/ void QuerySizes();
+	/*virtual*/ void DoLayout();
+};
+
+struct Table : CompositeWidget {
+	typedef CompositeWidget super;
+
+	static const FeatureFlags DEFAULT_FEATURES = FF_NONE;
+
+	Rect16                           m_used_area;
+	CCountedPtr<SimpleCountedObject> m_size_data;
+
+	Table(CompositeWidget *container, FeatureFlags feature_flags = DEFAULT_FEATURES, StringID tooltips = 0, uint16 color = COLOUR_PARENT)
+		: CompositeWidget(container, feature_flags, tooltips, color)
+	{}
+
+	virtual CompositeWidget::WidgetIterator AddWidget(Widget *wi, int x_pos, int y_pos, int x_span = 1, int y_span = 1);
 
 	/*virtual*/ void QuerySizes();
 	/*virtual*/ void DoLayout();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/widget/widget_table.cpp	Sun Apr 15 14:20:35 2007 +0000
@@ -0,0 +1,650 @@
+/* $Id: widget_panel.cpp 9484 2007-03-26 21:00:16Z KUDr $ */
+
+#include "../stdafx.h"
+#include <stdarg.h>
+#include "../openttd.h"
+#include "../debug.h"
+#include "../functions.h"
+#include "../map.h"
+#include "../player.h"
+#include "../window.h"
+#include "../gfx.h"
+#include "../viewport.h"
+#include "../console.h"
+#include "../variables.h"
+#include "../table/sprites.h"
+#include "../genworld.h"
+#include "../helpers.hpp"
+#include "../misc/blob.hpp"
+#include "window_events.hpp"
+#include "widget_types.h"
+
+namespace gui {
+
+struct TableSizeData;
+struct DoLayoutData;
+
+struct TableSlot;
+typedef CCountedPtr<TableSlot> TableSlotPtr;
+
+/**
+ * Table Slot class. Each table child widget is assigned to the slot. Slot describes
+ * which cells are occupied by widget, what is the real slot position and size, etc.
+ */
+struct TableSlot : CompositeWidget::Slot {
+	typedef CompositeWidget::Slot super;
+
+	Rect16     m_placement;        ///< Slot row/col index and span
+	Rect16     m_rc_slot;          ///< Bounding rectangle of the widget slot
+	bool       m_edge_left   : 1;  ///< widget is on left edge
+	bool       m_edge_top    : 1;  ///< widget is on top edge
+	bool       m_edge_right  : 1;  ///< widget is on right edge
+	bool       m_edge_bottom : 1;  ///< widget is on bottom edge
+
+	TableSlot(Widget *wi, const Rect16 &placement)
+		: super(wi)
+		, m_placement(placement)
+		, m_edge_left(false)
+		, m_edge_top(false)
+		, m_edge_right(false)
+		, m_edge_bottom(false)
+	{}
+
+	/*virtual*/ ~TableSlot() {};
+};
+
+/**
+ * Tells if RowOrColPosT and RowOrColPosArrayT hold row or column sizes.
+ */
+enum RowOrCol {
+	COL,
+	ROW,
+};
+
+/**
+ * Row/column size info. Used to calculate cell layout on the Table widget
+ * (as item of ColPosArray or RowPosArray).
+ */
+template <RowOrCol Trow_or_col> struct RowOrColPosT {
+	int16 m_pos; ///< x/y position of the cell inside Table widget
+
+	RowOrColPosT()
+		: m_pos(0)
+	{}
+};
+
+typedef RowOrColPosT<COL> ColPos;
+typedef RowOrColPosT<ROW> RowPos;
+
+/**
+ * One dimensional array of row/column info. Used to calculate cell layout on the Table widget
+ * (as TableSizeData::m_rows/m_cols).
+ */
+template <RowOrCol Trow_or_col> struct RowOrColPosArrayT {
+	typedef RowOrColPosT<Trow_or_col> RowOrColPos;
+	typedef CBlobT<RowOrColPos> Array;
+
+	Array   m_array; ///< zero based array of ColPos/RowPos
+	int16   m_min;   ///< index base (for [] operator)
+
+	RowOrColPosArrayT()
+		: m_array()
+		, m_min(0)
+	{}
+
+	void Create(const Rect16 &area);
+
+	RowOrColPos& operator[] (int16 pos);
+	RowOrColPos& operator[] (Point16 pos);
+};
+
+typedef RowOrColPosArrayT<COL> ColPosArray;
+typedef RowOrColPosArrayT<ROW> RowPosArray;
+
+/**
+ * Allocate and initialize ColPos array to contain cell position data indexed from area.Left() to area.Right()+1.
+ */
+template <>
+void ColPosArray::Create(const Rect16 &area)
+{
+	m_min = area.Left();
+	m_array.Free();
+	/* Allocate one item more, then rows/cols to see whole table size */
+	m_array.GrowSizeC(area.Width() + 1);
+}
+
+/**
+ * Allocate and initialize RowPos array to contain cell position data indexed from area.Left() to area.Right()+1.
+ */
+template <>
+void RowPosArray::Create(const Rect16 &area)
+{
+	m_min = area.Top();
+	m_array.Free();
+	/* Allocate one item more, then rows/cols to see whole table size */
+	m_array.GrowSizeC(area.Height() + 1);
+}
+
+/**
+ * RowPos or ColPos indexing operator.
+ */
+template <RowOrCol Trow_or_col>
+RowOrColPosT<Trow_or_col>& RowOrColPosArrayT<Trow_or_col>::operator[] (int16 pos)
+{
+	return *m_array.Data(pos - m_min);
+}
+
+/**
+ * RowPos or ColPos indexing operator (specialization for column).
+ */
+template <>
+ColPos& ColPosArray::operator[] (Point16 pos)
+{
+	return *m_array.Data(pos.x - m_min);
+}
+
+/**
+ * RowPos or ColPos indexing operator (specialization for row).
+ */
+template <>
+RowPos& RowPosArray::operator[] (Point16 pos)
+{
+	return *m_array.Data(pos.y - m_min);
+}
+
+/**
+ * Two-dimensional array of table slots. Used as m_matrix member of TableSizeData.
+ */
+struct SlotMatrix {
+	typedef CBlobT<TableSlotPtr> Array;
+
+	Rect16      m_placement_area;
+	Array       m_slots;
+
+	/**
+	 * Create 2-dim array of slots so it can be indexed by col/row placement.
+	 */
+	void Create(const Rect16 &area)
+	{
+		m_placement_area = area;
+		m_slots.Free();
+		m_slots.GrowSizeC(m_placement_area.Height() * m_placement_area.Width());
+	}
+
+	/**
+	 * Get slot by its col/row indices.
+	 */
+	TableSlotPtr* operator[] (Point16 pos)
+	{
+		assert(m_placement_area.PtInRect(pos));
+		pos -= m_placement_area.TopLeft();
+		int idx = ((int)pos.y) * m_placement_area.Width() + pos.x;
+		TableSlotPtr *ret = (idx < m_slots.Size()) ? m_slots.Data(idx) : NULL;
+		return ret;
+	}
+};
+
+/**
+ * Table cell size calculator. Used by Table::QuerySizes() and Table::DoLayout() routines.
+ * Smart pointer to this class is stored in the Table widget (Table::m_size_data).
+ * Its life-time is between begin of Table::QuerySizes() and end of Table::DoLayout().
+ */
+struct TableSizeData : SimpleCountedObject {
+	typedef TableSlot Slot;
+	typedef TableSlotPtr SlotPtr;
+
+	Table       *m_table;         ///< table widget for which we are calculating cell pos/sizes
+	Rect16      m_placement_area; ///< cell index rectangle used by slots (min col/row to max col/row)
+	Size16      m_int_border;     ///< internal table frame border (reserved space at the table edges)
+	SlotMatrix  m_matrix;         ///< 2-dim array of slots
+	ColPosArray m_cols;           ///< 1-dim array of column position/width info
+	RowPosArray m_rows;           ///< 1-dim array of row position/height info
+
+	TableSizeData(Table *p)
+		: m_table(p)
+		, m_int_border(p->CalcInternalBorder())
+	{}
+
+	void UpdatePlacementArea();
+	void UpdatePlacementArea_EnumProc(TableSlot &slot, int slot_idx);
+
+	void UpdateEdges();
+	void UpdateEdges_EnumProc(TableSlot &slot, int slot_idx);
+
+	void FillMatrix();
+	void FillMatrix_EnumProc(TableSlot &slot, int slot_idx);
+
+	void UpdateColMinSizes_VarSizes();
+	void UpdateColMinSizes_SameSizes();
+
+	void UpdateRowMinSizes_VarSizes();
+	void UpdateRowMinSizes_SameSizes();
+
+	void DoLayout();
+	void DoLayout_EnumProc(TableSlot &slot, int slot_idx);
+
+	void EnlargeWidth_VarSizes(int16 add_size);
+	void EnlargeWidth_SameSizes(int16 add_size);
+	void EnlargeHeight_VarSizes(int16 add_size);
+	void EnlargeHeight_SameSizes(int16 add_size);
+};
+
+/**
+ * Enumerate all table slots and make union rectangle of the area occupied by table slots.
+ * For example if lowest column index is 3 and highest is 5 then table will have 3 columns.
+ * The same can happen to rows.
+ */
+void TableSizeData::UpdatePlacementArea()
+{
+	m_table->EnumSlotsT<TableSizeData, &TableSizeData::UpdatePlacementArea_EnumProc>(this);
+}
+
+/**
+ * Extend m_placement_area rectangle by placement rectangle of given slot. For first slot use just
+ * rectangle assignment (copy), for subsequent slots make rectangle union.
+ */
+void TableSizeData::UpdatePlacementArea_EnumProc(TableSlot &slot, int slot_idx)
+{
+	if (slot_idx > 0) {
+		m_placement_area.DoUnion(slot.m_placement);
+	} else {
+		m_placement_area = slot.m_placement;
+	}
+}
+
+/**
+ * Scan for all table slots and fill their TableSlot::m_edge_xxx flags.
+ */
+void TableSizeData::UpdateEdges()
+{
+	m_table->EnumSlotsT<TableSizeData, &TableSizeData::UpdateEdges_EnumProc>(this);
+}
+
+/**
+ * Fill one slot TableSlot::m_edge_xxx flags - callback of Table::EnumSlotsT.
+ */
+void TableSizeData::UpdateEdges_EnumProc(TableSlot &slot, int slot_idx)
+{
+	slot.m_edge_left   = slot.m_placement.Left()   == m_placement_area.Left();
+	slot.m_edge_top    = slot.m_placement.Top()    == m_placement_area.Top();
+	slot.m_edge_right  = slot.m_placement.Right()  == m_placement_area.Right();
+	slot.m_edge_bottom = slot.m_placement.Bottom() == m_placement_area.Bottom();
+}
+
+/**
+ * Fill table slot matrix with TableSlot.
+ */
+void TableSizeData::FillMatrix()
+{
+	m_matrix.Create(m_placement_area);
+	m_table->EnumSlotsT<TableSizeData, &TableSizeData::FillMatrix_EnumProc>(this);
+	m_cols.Create(m_placement_area);
+	m_rows.Create(m_placement_area);
+}
+
+/**
+ * Fill one matrix slot - callback of Table::EnumSlotsT method called from
+ * TableSizeData::FillMatrix.
+ */
+void TableSizeData::FillMatrix_EnumProc(TableSlot &slot, int slot_idx)
+{
+	TableSlotPtr *slot_ptr = m_matrix[slot.m_placement.TopLeft()];
+	assert(slot_ptr != NULL);
+	*slot_ptr = &slot;
+}
+
+/**
+ * Scan table slots by columns (left/right) and move column's horizontal positions down to
+ * satisfy min widths of widgets inside. Update TableSizeData::ColPosArray::m_pos.
+ */
+void TableSizeData::UpdateColMinSizes_VarSizes()
+{
+	/* Position the first cell. */
+	ColPos &pos_begin_first = m_cols[m_placement_area.Left()];
+	pos_begin_first.m_pos = m_int_border.x;
+
+	for (int16 y = m_placement_area.Top(); y <= m_placement_area.Bottom(); y++) {
+		for (int16 x = m_placement_area.Left(); x <= m_placement_area.Right(); x++) {
+			/* Locate slot at this position (if any) */
+			TableSlot *slot = *m_matrix[Point16(x, y)];
+			if (slot == NULL) continue;
+
+			/* Find appropriate begin/end position data for the slot. */
+			ColPos &pos_begin = m_cols[slot->m_placement.Left()];
+			ColPos &pos_end   = m_cols[slot->m_placement.Right() + 1];
+
+			/* Where this slot will begin. */
+			int16 min_begin_pos = pos_begin.m_pos;
+			int16 min_slot_width = slot->m_wi->m_min_size.x;
+
+			/* Slot can overlap the left edge. Move it to the left by border width. */
+			bool ignore_parent_frame = (slot->m_wi->m_feature_flags & FF_IGNORE_PARENT_FRAME) != 0;
+			if (ignore_parent_frame && slot->m_edge_left) min_begin_pos -= m_int_border.x;
+
+			/* Set minimal slot begin position. */
+			for (int16 sx = slot->m_placement.Left() + 1; sx <= slot->m_placement.Right(); sx++) {
+				ColPos &pos = m_cols[sx];
+				if (pos.m_pos < min_begin_pos) pos.m_pos = min_begin_pos;
+			}
+
+			/* Set slot end position. */
+			int16 min_end_pos = min_begin_pos + min_slot_width;
+
+			/* Slot can overlap the right edge. Move it to the left by border width. */
+			if (ignore_parent_frame && slot->m_edge_right) min_end_pos -= m_int_border.x;
+
+			/* Table cell can't end before it begins. */
+			if (min_end_pos < min_begin_pos) min_end_pos = min_begin_pos;
+
+			/* Adjust the slot end position to ensure that the slot will fit there. */
+			if (pos_end.m_pos < min_end_pos) pos_end.m_pos = min_end_pos;
+
+			/* Set minimal positions of remaining table cells. */
+			for (int16 sx = slot->m_placement.Right() + 2; sx <= m_placement_area.Right(); sx++) {
+				ColPos &pos = m_cols[sx];
+				if (pos.m_pos < min_end_pos) pos.m_pos = min_end_pos;
+			}
+		}
+	}
+}
+
+/**
+ * Scan table slots by columns (left/right) and move column's horizontal positions down to
+ * satisfy min widths of widgets inside. Update TableSizeData::ColPosArray::m_pos.
+ */
+void TableSizeData::UpdateColMinSizes_SameSizes()
+{
+	int16 accu_min_cell_width = 0;
+
+	for (int16 y = m_placement_area.Top(); y <= m_placement_area.Bottom(); y++) {
+		for (int16 x = m_placement_area.Left(); x <= m_placement_area.Right(); x++) {
+			/* Locate slot at this position (if any) */
+			TableSlot *slot = *m_matrix[Point16(x, y)];
+			if (slot == NULL) continue;
+
+			/* Fetch minimum slot width and span. */
+			int16 min_slot_width = slot->m_wi->m_min_size.x;
+			int16 span = slot->m_placement.Width();
+			assert(span > 0);
+
+			/* Slot can overlap the left/right edge. Move it to the left by border width. */
+			if ((slot->m_wi->m_feature_flags & FF_IGNORE_PARENT_FRAME) != 0) {
+				if (slot->m_edge_left)  min_slot_width -= m_int_border.x;
+				if (slot->m_edge_right) min_slot_width -= m_int_border.x;
+			}
+
+			/* Widget slot width -> table cell width. */
+			int16 min_cell_width = min_slot_width / span;
+
+			/* Accumulate minimal cell width. */
+			if (accu_min_cell_width < min_cell_width) accu_min_cell_width = min_slot_width;
+		}
+	}
+
+	/* Position the first cell. */
+	ColPos &pos_begin_first = m_cols[m_placement_area.Left()];
+	pos_begin_first.m_pos = m_int_border.x;
+
+	/* Position other cells. */
+	for (int16 x = m_placement_area.Left(); x <= m_placement_area.Right(); x++) {
+		ColPos &pos_cur  = m_cols[x];
+		ColPos &pos_next = m_cols[x + 1];
+		pos_next.m_pos = pos_cur.m_pos + accu_min_cell_width;
+	}
+}
+
+/**
+ * Scan table slots by rows (top/down) and move row vertical positions down to
+ * satisfy min heights of widgets inside. Update TableSizeData::RowPosArray::m_pos.
+ */
+void TableSizeData::UpdateRowMinSizes_VarSizes()
+{
+	/* Position the first cell. */
+	RowPos &pos_begin_first = m_rows[m_placement_area.Top()];
+	pos_begin_first.m_pos = m_int_border.y;
+
+	for (int16 x = m_placement_area.Left(); x <= m_placement_area.Right(); x++) {
+		for (int16 y = m_placement_area.Top(); y <= m_placement_area.Bottom(); y++) {
+			/* Locate slot at this position (if any) */
+			TableSlot *slot = *m_matrix[Point16(x, y)];
+			if (slot == NULL) continue;
+
+			/* Find appropriate begin/end position data for the slot. */
+			RowPos &pos_begin = m_rows[slot->m_placement.Top()];
+			RowPos &pos_end   = m_rows[slot->m_placement.Bottom() + 1];
+
+			/* Where this slot will begin. */
+			int16 min_begin_pos = pos_begin.m_pos;
+			int16 min_slot_height = slot->m_wi->m_min_size.y;
+
+			/* Slot can overlap the left edge. Move it to the left by border width. */
+			bool ignore_parent_frame = (slot->m_wi->m_feature_flags & FF_IGNORE_PARENT_FRAME) != 0;
+			if (ignore_parent_frame && slot->m_edge_top) min_begin_pos -= m_int_border.y;
+
+			/* Set minimal slot begin position. */
+			for (int16 sy = slot->m_placement.Top() + 1; sy <= slot->m_placement.Bottom(); sy++) {
+				RowPos &pos = m_rows[sy];
+				if (pos.m_pos < min_begin_pos) pos.m_pos = min_begin_pos;
+			}
+
+			/* Set slot end position. */
+			int16 min_end_pos = min_begin_pos + min_slot_height;
+
+			/* Slot can overlap the right edge. Move it to the left by border width. */
+			if (ignore_parent_frame && slot->m_edge_bottom) min_end_pos -= m_int_border.y;
+
+			/* Table cell can't end before it begins. */
+			if (min_end_pos < min_begin_pos) min_end_pos = min_begin_pos;
+
+			/* Adjust the slot end position to ensure that the slot will fit there. */
+			if (pos_end.m_pos < min_end_pos) pos_end.m_pos = min_end_pos;
+
+			/* Set minimal positions of remaining table cells. */
+			for (int16 sy = slot->m_placement.Bottom() + 2; sy <= m_placement_area.Bottom(); sy++) {
+				RowPos &pos = m_rows[sy];
+				if (pos.m_pos < min_end_pos) pos.m_pos = min_end_pos;
+			}
+		}
+	}
+}
+
+/**
+ * Scan table slots by rows (top/down) and move row vertical positions down to
+ * satisfy min heights of widgets inside. Update TableSizeData::RowPosArray::m_pos.
+ */
+void TableSizeData::UpdateRowMinSizes_SameSizes()
+{
+	int16 accu_min_cell_height = 0;
+
+	for (int16 x = m_placement_area.Left(); x <= m_placement_area.Right(); x++) {
+		for (int16 y = m_placement_area.Top(); y <= m_placement_area.Bottom(); y++) {
+			/* Locate slot at this position (if any) */
+			TableSlot *slot = *m_matrix[Point16(x, y)];
+			if (slot == NULL) continue;
+
+			/* Fetch minimum slot width and span. */
+			int16 min_slot_height = slot->m_wi->m_min_size.y;
+			int16 span = slot->m_placement.Height();
+			assert(span > 0);
+
+			/* Slot can overlap the left/right edge. Move it to the left by border width. */
+			if ((slot->m_wi->m_feature_flags & FF_IGNORE_PARENT_FRAME) != 0) {
+				if (slot->m_edge_left)  min_slot_height -= m_int_border.y;
+				if (slot->m_edge_right) min_slot_height -= m_int_border.y;
+			}
+
+			/* Widget slot width -> table cell width. */
+			int16 min_cell_height = min_slot_height / span;
+
+			/* Accumulate minimal cell width. */
+			if (accu_min_cell_height < min_cell_height) accu_min_cell_height = min_cell_height;
+		}
+	}
+
+	/* Position the first cell. */
+	RowPos &pos_begin_first = m_rows[m_placement_area.Top()];
+	pos_begin_first.m_pos = m_int_border.y;
+
+	/* Position other cells. */
+	for (int16 y = m_placement_area.Top(); y <= m_placement_area.Bottom(); y++) {
+		RowPos &pos_cur  = m_rows[y];
+		RowPos &pos_next = m_rows[y + 1];
+		pos_next.m_pos = pos_cur.m_pos + accu_min_cell_height;
+	}
+}
+
+/**
+ * Called from Table::DoLayout to assign each table slot its rectangle.
+ */
+void TableSizeData::DoLayout()
+{
+	// current placement area size is minimal one
+	int16 W = m_cols[m_placement_area.Right()  + 1].m_pos - m_cols[m_placement_area.Left()].m_pos + m_int_border.x * 2;
+	int16 H = m_rows[m_placement_area.Bottom() + 1].m_pos - m_rows[m_placement_area.Top() ].m_pos + m_int_border.y * 2;
+
+	// enlarge width if needed
+	if (W < m_table->Width()) {
+		if ((m_table->m_feature_flags & FF_SAME_WIDTH) == 0) {
+			EnlargeWidth_VarSizes(m_table->Width() - W);
+		} else {
+			EnlargeWidth_SameSizes(m_table->Width() - W);
+		}
+	}
+
+	// enlarge height if needed
+	if (H < m_table->Height()) {
+		if ((m_table->m_feature_flags & FF_SAME_HEIGHT) == 0) {
+			EnlargeHeight_VarSizes(m_table->Height() - H);
+		} else {
+			EnlargeHeight_SameSizes(m_table->Height() - H);
+		}
+	}
+
+	m_table->EnumSlotsT<TableSizeData, &TableSizeData::DoLayout_EnumProc>(this);
+}
+
+/**
+ * Called from TableSizeData::DoLayout as slot enumeration callback. Its purpose is to find
+ * the proper column width and row height of each table slot.
+ */
+void TableSizeData::DoLayout_EnumProc(TableSlot &slot, int slot_idx)
+{
+	int16 L = m_cols[slot.m_placement.Left()].m_pos;
+	int16 T = m_rows[slot.m_placement.Top()].m_pos;
+	int16 R = m_cols[slot.m_placement.Right() + 1].m_pos - 1;
+	int16 B = m_rows[slot.m_placement.Bottom() + 1].m_pos - 1;
+	Rect16 rc = Rect16(L, T, R, B);
+	slot.m_wi->SetSlotRect(rc);
+}
+
+/**
+ * Called from TableSizeData::DoLayout when 'variable cell widths' table needs to enlarge horizontally from
+ * its minimal width to the real width by incrementing width of its cells.
+ */
+void TableSizeData::EnlargeWidth_VarSizes(int16 add_size)
+{
+	// Do we need different distribution scheme for var-size tables? If yes, do it here...
+	EnlargeWidth_SameSizes(add_size);
+}
+
+/**
+ * Called from TableSizeData::DoLayout when 'variable cell widths' table needs to enlarge horizontally from
+ * its minimal width to the real width by incrementing width of its cells.
+ */
+void TableSizeData::EnlargeWidth_SameSizes(int16 add_size)
+{
+	int16 num_slots = m_placement_area.Width();
+	for (int32 i = m_placement_area.Left(); i <= m_placement_area.Right() + 1; i++) {
+		// note that 'i' is of type int32
+		m_cols[i].m_pos += (int16)(i * add_size / num_slots);
+	}
+}
+
+/**
+ * Called from TableSizeData::DoLayout when 'variable cell heights' table needs to enlarge vertically from
+ * its minimal size to the real size by incrementing height of its cells.
+ */
+void TableSizeData::EnlargeHeight_VarSizes(int16 add_size)
+{
+	// Do we need different distribution scheme for var-size tables? If yes, do it here...
+	EnlargeHeight_SameSizes(add_size);
+}
+
+/**
+ * Called from TableSizeData::DoLayout when 'same cell heights' table needs to enlarge vertically from
+ * its minimal size to the real size by incrementing height of its cells.
+ */
+void TableSizeData::EnlargeHeight_SameSizes(int16 add_size)
+{
+	int16 num_slots = m_placement_area.Height();
+	for (int32 i = m_placement_area.Top(); i <= m_placement_area.Bottom() + 1; i++) {
+		// note that 'i' is of type int32
+		m_rows[i].m_pos += (int16)(i * add_size / num_slots);
+	}
+}
+
+/**
+ * Add new table slot with widget. Each slot can span through multiple cells.
+ *  @param wi widget to add
+ *  @param x_pos zero based column index
+ *  @param y_pos zero based row index
+ *  @param x_span how many columns this slot will span through
+ *  @param y_span how many rows this slot will span through
+ */
+/*virtual*/ CompositeWidget::WidgetIterator Table::AddWidget(Widget *wi, int x_pos, int y_pos, int x_span, int y_span)
+{
+	assert(x_span >= 1);
+	assert(y_span >= 1);
+	Slot *slot = new TableSlot(wi, Rect16(Point16(x_pos, y_pos), Size16(x_span, y_span)));
+	WidgetIterator wit = super::AddSlot(slot);
+	return wit;
+}
+
+/**
+ * Query child widgets for their minimum sizes and calculate own minimum size from them.
+ */
+/*virtual*/ void Table::QuerySizes()
+{
+	/* query children */
+	super::QuerySizes();
+	if (m_widgets.empty()) return;
+
+	TableSizeData *size_data = new TableSizeData(this);
+	m_size_data = size_data;
+	size_data->UpdatePlacementArea();
+	size_data->UpdateEdges();
+	size_data->FillMatrix();
+
+	if ((m_feature_flags & FF_SAME_WIDTH) == 0) {
+		size_data->UpdateColMinSizes_VarSizes();
+	} else {
+		size_data->UpdateColMinSizes_SameSizes();
+	}
+
+	if ((m_feature_flags & FF_SAME_HEIGHT) == 0) {
+		size_data->UpdateRowMinSizes_VarSizes();
+	} else {
+		size_data->UpdateRowMinSizes_SameSizes();
+	}
+
+	int16 L = size_data->m_cols[size_data->m_placement_area.Left()].m_pos;
+	int16 T = size_data->m_rows[size_data->m_placement_area.Top()].m_pos;
+	int16 R = size_data->m_cols[size_data->m_placement_area.Right() + 1].m_pos;
+	int16 B = size_data->m_rows[size_data->m_placement_area.Bottom() + 1].m_pos;
+
+	m_min_size = Size16(R - L, B - T) + size_data->m_int_border * 2;
+}
+
+/**
+ * Layout widgets onto table.
+ */
+/*virtual*/ void Table::DoLayout()
+{
+	assert(!m_size_data.IsNull());
+	TableSizeData *size_data = (TableSizeData*)(SimpleCountedObject*)m_size_data;
+	size_data->DoLayout();
+	/* do layout children */
+	super::DoLayout();
+}
+
+}; // namespace gui