 pgadmin/ctl/ctlSQLGrid.cpp         | 203 ++++++++++++++++++++++++++++++++-----
 pgadmin/ctl/ctlSQLResult.cpp       |  36 +------
 pgadmin/frm/frmEditGrid.cpp        |  19 +---
 pgadmin/include/ctl/ctlSQLGrid.h   |   9 ++
 pgadmin/include/ctl/ctlSQLResult.h |   3 -
 5 files changed, 195 insertions(+), 75 deletions(-)

diff --git a/pgadmin/ctl/ctlSQLGrid.cpp b/pgadmin/ctl/ctlSQLGrid.cpp
index a217283..f29a08d 100644
--- a/pgadmin/ctl/ctlSQLGrid.cpp
+++ b/pgadmin/ctl/ctlSQLGrid.cpp
@@ -26,6 +26,7 @@
 
 BEGIN_EVENT_TABLE(ctlSQLGrid, wxGrid)
 	EVT_MOUSEWHEEL(ctlSQLGrid::OnMouseWheel)
+	EVT_GRID_COL_SIZE(ctlSQLGrid::OnGridColSize)
 END_EVENT_TABLE()
 
 IMPLEMENT_DYNAMIC_CLASS(ctlSQLGrid, wxGrid)
@@ -53,6 +54,13 @@ ctlSQLGrid::ctlSQLGrid(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons
 	Connect(wxID_ANY, wxEVT_GRID_LABEL_LEFT_DCLICK, wxGridEventHandler(ctlSQLGrid::OnLabelDoubleClick));
 }
 
+void ctlSQLGrid::OnGridColSize(wxGridSizeEvent &event)
+{
+	// Save key="index:label", value=size
+	int col = event.GetRowOrCol();
+	colSizes[GetColKeyValue(col)] = GetColSize(col);
+}
+
 void ctlSQLGrid::OnCopy(wxCommandEvent &ev)
 {
 	Copy();
@@ -324,42 +332,185 @@ void ctlSQLGrid::OnLabelDoubleClick(wxGridEvent &event)
 	}
 	else if (col >= 0)
 	{
-		for (row = 0 ; row < GetNumberRows() ; row++)
-		{
-			if (CheckRowPresent(row))
-			{
-				extent = GetBestSize(row, col).GetWidth();
-				if (extent > extentWant)
-					extentWant = extent;
-			}
-		}
-
-		extentWant += EXTRAEXTENT_WIDTH;
-		extentWant = wxMax(extentWant, GetColMinimalAcceptableWidth());
-		extentWant = wxMin(extentWant, maxWidth * 3 / 4);
-		int currentWidth = GetColumnWidth(col);
-
-		if (currentWidth >= maxWidth * 3 / 4 || currentWidth == extentWant)
-			extentWant = GetColMinimalAcceptableWidth();
-		else if (currentWidth < maxWidth / 4)
-			extentWant = wxMin(maxWidth / 4, extentWant);
-		else if (currentWidth < maxWidth / 2)
-			extentWant = wxMin(maxWidth / 2, extentWant);
-		else if (currentWidth < maxWidth * 3 / 4)
-			extentWant = wxMin(maxWidth * 3 / 4, extentWant);
-
-		if (extentWant != currentWidth)
+		// Holding Ctrl switches back to automatic column's sizing
+		if (event.ControlDown()) 
 		{
+			colSizes.erase(GetColKeyValue(col));
 			BeginBatch();
 			if(IsCellEditControlShown())
 			{
 				HideCellEditControl();
 				SaveEditControlValue();
 			}
-			SetColumnWidth(col, extentWant);
+			AutoSizeColumn(col, false);
 			EndBatch();
 		}
+		else // toggle between some predefined sizes
+		{
+
+			for (row = 0 ; row < GetNumberRows() ; row++)
+			{
+				if (CheckRowPresent(row))
+				{
+					extent = GetBestSize(row, col).GetWidth();
+					if (extent > extentWant)
+						extentWant = extent;
+				}
+			}
+
+			extentWant += EXTRAEXTENT_WIDTH;
+			extentWant = wxMax(extentWant, GetColMinimalAcceptableWidth());
+			extentWant = wxMin(extentWant, maxWidth * 3 / 4);
+			int currentWidth = GetColumnWidth(col);
+
+			if (currentWidth >= maxWidth * 3 / 4 || currentWidth == extentWant)
+				extentWant = GetColMinimalAcceptableWidth();
+			else if (currentWidth < maxWidth / 4)
+				extentWant = wxMin(maxWidth / 4, extentWant);
+			else if (currentWidth < maxWidth / 2)
+				extentWant = wxMin(maxWidth / 2, extentWant);
+			else if (currentWidth < maxWidth * 3 / 4)
+				extentWant = wxMin(maxWidth * 3 / 4, extentWant);
+
+			if (extentWant != currentWidth)
+			{
+				BeginBatch();
+				if(IsCellEditControlShown())
+				{
+					HideCellEditControl();
+					SaveEditControlValue();
+				}
+				SetColumnWidth(col, extentWant);
+				EndBatch();
+				colSizes[GetColKeyValue(col)] = extentWant;
+			}
+		}
+	}
+}
+
+void ctlSQLGrid::AutoSizeColumn(int col, bool setAsMin, bool doLimit)
+{
+	wxGrid::AutoSizeColumn(col, setAsMin);
+	
+	if (doLimit)
+	{
+		int newSize, oldSize;
+		int maxSize, totalSize = 0, availSize;
+
+		oldSize = GetColSize(col);
+		availSize = GetClientSize().GetWidth() - GetRowLabelSize();
+		maxSize = availSize / 2;
+		for (int i = 0 ; i < GetNumberCols() ; i++)
+			totalSize += GetColSize(i);
+
+		if (oldSize > maxSize && totalSize > availSize)
+		{
+			totalSize -= oldSize;
+			/* Shrink wide column to maxSize.
+			 * If the rest of the columns are short, make sure to use all the remaining space,
+			 *   but no more than oldSize (which is enough according to AutoSizeColumns())
+			 */
+			newSize = wxMin(oldSize, wxMax(maxSize, availSize - totalSize));
+			SetColSize(col, newSize);
+		}
+	}
+}
+
+void ctlSQLGrid::AutoSizeColumns(bool setAsMin)
+{
+	wxCoord newSize, oldSize;
+	wxCoord maxSize, totalSize = 0, availSize;
+	int col, nCols = GetNumberCols();
+	int row, nRows = GetNumberRows();
+
+	/* We need to check each cell's width to choose best. wxGrid::AutoSizeColumns()
+	 * is good, but looping through long result sets gives a noticeable slowdown.
+	 * So for each column we'll check every first 50 cells and then increase the
+	 * loop step skipping some cells to finish the loop in about 50+500 cycles.
+	 * That's a good compromise for usability and speed.
+	 */
+	float dr, dr_skip = (nRows <= 50+500) ? 1 : (nRows / 500.0);
+
+	// First pass: auto-size columns
+	for (col = 0 ; col < nCols; col++)
+	{
+		ColKeySizeHashMap::iterator it = colSizes.find(GetColKeyValue(col));
+		if (it != colSizes.end()) // Restore manually specified size
+			newSize = it->second;
+		else
+		{
+			wxClientDC dc(GetGridWindow());
+			newSize = 0;
+			dr = 1;
+			// get cells's width
+			for (float r = 0 ; round(r) < nRows ; r += dr)
+			{
+				row = round(r);
+				wxGridCellAttr *attr = GetCellAttr(row, col);
+				wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col);
+				if ( renderer )
+				{
+					wxSize size = renderer->GetBestSize(*this, *attr, dc, row, col);
+					if ( size.x > newSize )
+						newSize = size.x;
+					renderer->DecRef();
+				}
+				attr->DecRef();
+				
+				if (row > 50)
+					dr = dr_skip;
+			}
+			// get column's label width
+			wxCoord w, h;
+			dc.SetFont( GetLabelFont() );
+			dc.GetMultiLineTextExtent( GetColLabelValue(col), &w, &h );
+			if ( GetColLabelTextOrientation() == wxVERTICAL )
+				w = h;
+
+			if ( w > newSize )
+				newSize = w;
+
+			if (!newSize)
+				newSize = GetRowLabelSize();
+			else
+				// leave some space around text
+				newSize += 6;
+		}
+		SetColSize(col, newSize);
+		totalSize += newSize;
 	}
+
+	availSize = GetClientSize().GetWidth() - GetRowLabelSize();
+
+	// Second pass: shrink wide columns if exceeded available width
+	if (totalSize > availSize)
+	{
+		// A wide column shouldn't take up more than 50% of the visible space
+		maxSize = availSize / 2;
+		for (col = 0 ; col < nCols ; col++)
+		{
+			oldSize = GetColSize(col);
+			ColKeySizeHashMap::iterator it = colSizes.find(GetColKeyValue(col));
+			// No user-specified size and is too wide
+			if (it == colSizes.end() && oldSize > maxSize)
+			{
+				totalSize -= oldSize;
+				/* Shrink wide column to maxSize.
+				 * If the rest of the columns are short, make sure to use all the remaining space,
+				 *   but no more than oldSize (which is enough according to first pass)
+				 */
+				newSize = wxMin(oldSize, wxMax(maxSize, availSize - totalSize));
+				SetColSize(col, newSize);
+				totalSize += newSize;
+			}
+		}
+	}
+}
+
+wxString ctlSQLGrid::GetColKeyValue(int col)
+{
+	wxString colKey = wxString::Format(wxT("%d:"), col) + GetColLabelValue(col);
+	return colKey;
 }
 
 wxSize ctlSQLGrid::GetBestSize(int row, int col)
diff --git a/pgadmin/ctl/ctlSQLResult.cpp b/pgadmin/ctl/ctlSQLResult.cpp
index c0d2e10..5d7ce87 100644
--- a/pgadmin/ctl/ctlSQLResult.cpp
+++ b/pgadmin/ctl/ctlSQLResult.cpp
@@ -109,14 +109,6 @@ bool ctlSQLResult::IsColText(int col)
 
 int ctlSQLResult::Execute(const wxString &query, int resultToRetrieve, wxWindow *caller, long eventId, void *data)
 {
-	colSizes.Empty();
-	colHeaders.Empty();
-	for (int col = 0 ; col < GetNumberCols() ; col++)
-	{
-		colSizes.Add(GetColSize(col));
-		colHeaders.Add(this->GetColLabelValue(col));
-	}
-
 	wxGridTableMessage *msg;
 	sqlResultTable *table = (sqlResultTable *)GetTable();
 	msg = new wxGridTableMessage(table, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, GetNumberRows());
@@ -200,42 +192,24 @@ void ctlSQLResult::DisplayData(bool single)
 
 	if (single)
 	{
-		int w, h;
-		if (colSizes.GetCount() == 1)
-			w = colSizes.Item(0);
-		else
-			GetSize(&w, &h);
-
 		colNames.Add(thread->DataSet()->ColName(0));
 		colTypes.Add(wxT(""));
 		colTypClasses.Add(0L);
 
-		SetColSize(0, w);
+		AutoSizeColumn(0, false, false);
 	}
 	else
 	{
-		wxString colName, colType;
-		int w;
-
-		size_t hdrIndex = 0;
 		long col, nCols = thread->DataSet()->NumCols();
 
+		AutoSizeColumns(false);
+
 		for (col = 0 ; col < nCols ; col++)
 		{
-			colName = thread->DataSet()->ColName(col);
-			colType = thread->DataSet()->ColFullType(col);
-			colNames.Add(colName);
-			colTypes.Add(colType);
+			colNames.Add(thread->DataSet()->ColName(col));
+			colTypes.Add(thread->DataSet()->ColFullType(col));
 			colTypClasses.Add(thread->DataSet()->ColTypClass(col));
 
-			wxString colHeader = colName + wxT("\n") + colType;
-
-			if (hdrIndex < colHeaders.GetCount() && colHeaders.Item(hdrIndex) == colHeader)
-				w = colSizes.Item(hdrIndex++);
-			else
-				w = -1;
-
-			SetColSize(col, w);
 			if (thread->DataSet()->ColTypClass(col) == PGTYPCLASS_NUMERIC)
 			{
 				/*
diff --git a/pgadmin/frm/frmEditGrid.cpp b/pgadmin/frm/frmEditGrid.cpp
index 2cd9327..1509b16 100644
--- a/pgadmin/frm/frmEditGrid.cpp
+++ b/pgadmin/frm/frmEditGrid.cpp
@@ -1380,13 +1380,6 @@ void frmEditGrid::Go()
 	toolsMenu->Enable(MNU_DESCSORT, false);
 	toolsMenu->Enable(MNU_REMOVESORT, false);
 
-	// Stash the column sizes so we can reset them
-	wxArrayInt colWidths;
-	for (col = 0 ; col < sqlGrid->GetNumberCols() ; col++)
-	{
-		colWidths.Add(sqlGrid->GetColumnWidth(col));
-	}
-
 	wxString qry = wxT("SELECT ");
 	if (hasOids)
 		qry += wxT("oid, ");
@@ -1470,16 +1463,12 @@ void frmEditGrid::Go()
 	// to force the grid to create scrollbars, we make sure the size  so small that scrollbars are needed
 	// later, we will resize the grid's parent to force the correct size (now including scrollbars, even if
 	// they are suppressed initially. Win32 won't need this.
-	sqlGrid->SetSize(10, 10);
+	// !!! This hack breaks columns auto-sizing ( GetClientSize().GetWidth() is used in ctlSQLGrid::AutoSizeColumns() )
+	// !!! Is it still required?
+	//sqlGrid->SetSize(10, 10); 
 
 	sqlGrid->SetTable(new sqlTable(connection, thread, tableName, relid, hasOids, primaryKeyColNumbers, relkind), true);
-
-	// Reset the column widths
-	for (col = 0 ; col < sqlGrid->GetNumberCols() ; col++)
-	{
-		if ((col + 1) <= (int)colWidths.Count())
-			sqlGrid->SetColumnWidth(col, colWidths[col]);
-	}
+	sqlGrid->AutoSizeColumns(false);
 
 	sqlGrid->EndBatch();
 
diff --git a/pgadmin/include/ctl/ctlSQLGrid.h b/pgadmin/include/ctl/ctlSQLGrid.h
index 173ec31..a2ab601 100644
--- a/pgadmin/include/ctl/ctlSQLGrid.h
+++ b/pgadmin/include/ctl/ctlSQLGrid.h
@@ -38,16 +38,25 @@ public:
 	wxSize GetBestSize(int row, int col);
 	void OnLabelDoubleClick(wxGridEvent &event);
 
+	void AutoSizeColumn(int col, bool setAsMin = false, bool doLimit = true);
+	void AutoSizeColumns(bool setAsMin);
+
+	WX_DECLARE_STRING_HASH_MAP( int, ColKeySizeHashMap );
+
 	DECLARE_DYNAMIC_CLASS(ctlSQLGrid)
 	DECLARE_EVENT_TABLE()
 
 private:
 	void OnCopy(wxCommandEvent &event);
 	void OnMouseWheel(wxMouseEvent &event);
+	void OnGridColSize(wxGridSizeEvent &event);
 	wxString GetColumnName(int colNum);
+	wxString GetColKeyValue(int col);
 	void AppendColumnHeader(wxString &str, int start, int end);
 	void AppendColumnHeader(wxString &str, wxArrayInt columns);
 
+	// Stores sizes of colums explicitly resized by user
+	ColKeySizeHashMap colSizes;
 };
 
 #endif
diff --git a/pgadmin/include/ctl/ctlSQLResult.h b/pgadmin/include/ctl/ctlSQLResult.h
index 087da13..b0a9579 100644
--- a/pgadmin/include/ctl/ctlSQLResult.h
+++ b/pgadmin/include/ctl/ctlSQLResult.h
@@ -72,9 +72,6 @@ public:
 	wxArrayString colTypes;
 	wxArrayLong colTypClasses;
 
-	wxArrayInt  colSizes;
-	wxArrayString colHeaders;
-
 private:
 	pgQueryThread *thread;
 	pgConn *conn;
