text: //////////////////////////////////////////////////////////////////////////
  //
  // pgAdmin III - PostgreSQL Tools
  //
  // Copyright (C) 2002 - 2016, The pgAdmin Development Team
  // This software is released under the PostgreSQL Licence
  //
  // dlgDirectDbg.cpp - debugger
  //
  //////////////////////////////////////////////////////////////////////////
  
  #include "pgAdmin3.h"
  
  // wxWindows headers
  #include <wx/wx.h>
  #include <wx/grid.h>
  
  // App headers
  #include "ctl/ctlAuiNotebook.h"
  #include "db/pgConn.h"
  #include "db/pgQueryThread.h"
  #include "db/pgQueryResultEvent.h"
  #include "schema/pgObject.h"
  #include "debugger/dbgConst.h"
  #include "debugger/dbgBreakPoint.h"
  #include "debugger/dbgController.h"
  #include "debugger/dbgModel.h"
  #include "debugger/ctlStackWindow.h"
  #include "debugger/ctlMessageWindow.h"
  #include "debugger/ctlTabWindow.h"
  #include "debugger/frmDebugger.h"
  #include "debugger/dlgDirectDbg.h"
  
  #include "images/debugger.pngc"
  
  #define lblMessage                  CTRL_STATIC("lblMessage")
  #define grdParams                   CTRL("grdParams", wxGrid)
  #define chkPkgInit                  CTRL_CHECKBOX("chkPkgInit")
  #define btnDebug                    CTRL_BUTTON("wxID_OK")
  
  IMPLEMENT_CLASS(dlgDirectDbg, pgDialog)
  
  BEGIN_EVENT_TABLE(dlgDirectDbg, pgDialog)
  	EVT_BUTTON(wxID_OK,                               dlgDirectDbg::OnOk)
  	EVT_BUTTON(wxID_CANCEL,                           dlgDirectDbg::OnCancel)
  	EVT_GRID_CMD_LABEL_LEFT_CLICK(XRCID("grdParams"), dlgDirectDbg::OnClickGridLabel)
  	EVT_MENU(RESULT_ID_ARGS_UPDATED,                  dlgDirectDbg::ResultArgsUpdated)
  	EVT_MENU(RESULT_ID_ARGS_UPDATE_ERROR,             dlgDirectDbg::ResultArgsUpdateError)
  END_EVENT_TABLE()
  
  
  ////////////////////////////////////////////////////////////////////////////////
  // dlgDirectDbg constructor
  //
  //    This class implements 'direct-debugging'. In direct-debugging, the user
  //  provides a function signature, procedure signature, or OID on the command
  //  line (this identifies the debug target).  We query the server for the
  //  names, types, and in/out modes for each target parameter and then prompt
  //    the user to enter a value for each of the IN (and IN/OUT) parameters.
  //
  //  When the user fills in the parameter values and clicks OK, we set a
  //  breakpoint at the target and then execute a SELECT statement or an
  //  EXEC statement that invokes the target (with the parameter values
  //  provided by the user).
  
  dlgDirectDbg::dlgDirectDbg(frmDebugger *_parent, dbgController *_controller,
                             pgConn *_conn)
  	: m_controller(_controller), m_thread(NULL), m_conn(_conn)
  {
  	int width, height, totalWidth = 0;
  
  	SetFont(settings->GetSystemFont());
  	LoadResource(_parent, wxT("dlgDirectDbg"));
  
  	// Icon
  	SetIcon(*debugger_png_ico);
  	RestorePosition();
  
  	grdParams->SetRowLabelSize(wxGRID_AUTOSIZE);
  	grdParams->SetColLabelSize(20);
  	grdParams->AutoSizeColumns(true);
  
  	chkPkgInit->SetValue(false);
  
  	PopulateParamGrid();
  
  	LoadSettings();
  
  	for(int i = 0; i < grdParams->GetNumberCols(); i++)
  		grdParams->AutoSizeColumn(i, true);
  
  	if (grdParams->GetNumberCols() > 1)
  	{
  		// Extend grid to it's parent width
  		grdParams->GetClientSize(&width, &height);
  		for (int i = 0; i < grdParams->GetNumberCols(); i++)
  		{
  			totalWidth += grdParams->GetColSize(i);
  		}
  		// Total client width - total six column widths - the first (an empty) column
  		// width
  		grdParams->SetColSize(COL_DEF_VAL, width - totalWidth - 100);
  	}
  }
  
  
  ////////////////////////////////////////////////////////////////////////////////
  // PopulateParamGrid()
  //
  //    This function reads parameter descriptions from m_targetInfo and adds a
  //    new row to the grid control for each IN (or IN/OUT, VARAIADIC) parameter.
  //    Each row displays the parameter name, the data type, and an entry box
  //    where the user can type in a value for that parameter
  //
  void dlgDirectDbg::PopulateParamGrid()
  {
  	pgDbgArgs *args = m_controller->GetTargetInfo()->GetArgs();
  
  	// If the target is defined within package, offer the user
  	// a chance to debug the initializer (there may or may not
  	// be an initializer, we don't really know at this point)
  	if(m_controller->GetTargetInfo()->GetPkgOid() == 0)
  		chkPkgInit->Disable();
  	else
  		chkPkgInit->Enable();
  
  	if (!args)
  	{
  		grdParams->CreateGrid(0, 1);
  		grdParams->SetColLabelValue(COL_NAME, _("No arguments required"));
  		grdParams->SetColSize(0, 350);
  
  		return;
  	}
  
  	// Add seven columns to the grid control:
  	// - Name, Type, NULL?, EXPR?, Value, Default?, And Default Value
  	grdParams->CreateGrid(0, 7);
  
  	grdParams->SetColLabelValue(COL_NAME, _("Name"));
  	grdParams->SetColLabelValue(COL_TYPE, _("Type"));
  	grdParams->SetColLabelValue(COL_NULL, _("Null?"));
  	grdParams->SetColLabelValue(COL_EXPR, _("Expression?"));
  	grdParams->SetColLabelValue(COL_VALUE, _("Value"));
  	grdParams->SetColLabelValue(COL_USE_DEF, _("Use default?"));
  	grdParams->SetColLabelValue(COL_DEF_VAL, _("Default Value"));
  
  	size_t idx = 0;
  
  	for(size_t pIdx = 0; pIdx < args->Count(); pIdx++)
  	{
  		dbgArgInfo *arg = (*args)[pIdx];
  
  		// If this is an IN parameter (or an IN/OUT parameter), add
  		// a new row to the grid
  		if(arg->GetMode() != pgParam::PG_PARAM_OUT)
  		{
  			grdParams->AppendRows(1);
  			grdParams->SetCellValue(idx, COL_NAME,  arg->GetName());
  
  			// Make it obvious which are variadics
  			if (arg->GetMode() != pgParam::PG_PARAM_VARIADIC)
  			{
  				grdParams->SetRowLabelValue(idx, wxEmptyString);
  				grdParams->SetCellValue(idx, COL_TYPE, arg->GetTypeName());
  
  				if (!arg->IsArray())
  				{
  					// Is the value an expression?
  					grdParams->SetCellEditor(idx, COL_EXPR, new ctlGridCellBoolEditor());
  					grdParams->SetCellRenderer(idx, COL_EXPR, new wxGridCellBoolRenderer());
  					grdParams->SetCellValue(idx, COL_EXPR, wxT(""));
  				}
  				else
  				{
  					grdParams->SetCellBackgroundColour(idx, COL_EXPR, wxColour(235, 235, 235, 90));
  					grdParams->SetCellBackgroundColour(idx, COL_VALUE, wxColour(235, 235, 235, 90));
  				}
  
  				// Use the default value?
  				grdParams->SetCellRenderer(idx, COL_USE_DEF, new wxGridCellBoolRenderer());
  				grdParams->SetCellEditor(idx, COL_USE_DEF, new ctlGridCellBoolEditor());
  			}
  			else
  			{
  				grdParams->SetCellValue(idx, COL_TYPE, wxT("VARIADIC ") + arg->GetTypeName());
  				grdParams->SetRowLabelValue(idx, wxEmptyString);
  
  				grdParams->AppendRows(1);
  				grdParams->SetRowLabelValue(idx + 1, wxT("+"));
  				grdParams->SetCellValue(idx + 1, COL_NAME, _("Click '+/-' to add/remove value to variadic"));
  
  				grdParams->SetCellBackgroundColour(idx + 1, COL_EXPR,    wxColour(235, 235, 235, 90));
  				grdParams->SetCellBackgroundColour(idx + 1, COL_VALUE,   wxColour(235, 235, 235, 90));
  				grdParams->SetCellBackgroundColour(idx + 1, COL_USE_DEF, wxColour(235, 235, 235, 90));
  			}
  
  			// Set value to NULL?
  			grdParams->SetCellEditor(idx, COL_NULL, new ctlGridCellBoolEditor(arg));
  			grdParams->SetCellRenderer(idx, COL_NULL, new wxGridCellBoolRenderer());
  			grdParams->SetCellValue(idx, COL_NULL, wxT(""));
  
  			if (arg->HasDefault())
  			{
  				// Whenever the default value is available, use that by default
  				grdParams->SetCellValue(idx, COL_USE_DEF, wxT("1"));
  				grdParams->SetCellValue(idx, COL_DEF_VAL, arg->Default());
  
  				// When "Use Defalut?" is enabled, disable VALUE, NULL? and
  				// EXPR? columns
  				grdParams->SetReadOnly(idx, COL_USE_DEF, false);
  				grdParams->SetReadOnly(idx, COL_VALUE, false);
  				grdParams->SetReadOnly(idx, COL_EXPR, false);
  			}
  			else
  			{
  				grdParams->SetCellValue(idx, COL_USE_DEF, wxT(""));
  				grdParams->SetCellValue(idx, COL_DEF_VAL, _("<No default value>"));
  
  				// When default value is not available, we should ask the user
  				// to enter them
  				grdParams->SetReadOnly(idx, COL_USE_DEF, true);
  				grdParams->SetReadOnly(idx, COL_VALUE, arg->IsArray());
  				grdParams->SetReadOnly(idx, COL_EXPR, arg->IsArray());
  
  				grdParams->SetCellBackgroundColour(idx, COL_USE_DEF, wxColour(235, 235, 235, 90));
  				if (arg->IsArray())
  				{
  					grdParams->SetCellBackgroundColour(idx, COL_VALUE, wxColour(235, 235, 235, 90));
  					grdParams->SetCellBackgroundColour(idx, COL_EXPR, wxColour(235, 235, 235, 90));
  				}
  			}
  
  			grdParams->SetReadOnly(idx, COL_NULL, false);
  			grdParams->SetReadOnly(idx, COL_NAME, true);
  			grdParams->SetReadOnly(idx, COL_TYPE, true);
  			grdParams->SetReadOnly(idx, COL_DEF_VAL, true);
  			grdParams->SetCellBackgroundColour(idx, COL_NAME,    wxColour(235, 235, 235, 90));
  			grdParams->SetCellBackgroundColour(idx, COL_TYPE,    wxColour(235, 235, 235, 90));
  			grdParams->SetCellBackgroundColour(idx, COL_DEF_VAL, wxColour(235, 235, 235, 90));
  
  			idx++;
  
  			if (arg->GetMode() != pgParam::PG_PARAM_VARIADIC &&
  			        arg->IsArray())
  			{
  				grdParams->AppendRows(1);
  
  				grdParams->SetRowLabelValue(idx, wxT("+"));
  				grdParams->SetCellValue(idx, COL_NAME, _("Click '+/-' to add/remove value to the array"));
  
  				grdParams->SetReadOnly(idx, COL_NAME,    true);
  				grdParams->SetReadOnly(idx, COL_TYPE,    true);
  				grdParams->SetReadOnly(idx, COL_NULL,    true);
  				grdParams->SetReadOnly(idx, COL_EXPR,    true);
  				grdParams->SetReadOnly(idx, COL_VALUE,   true);
  				grdParams->SetReadOnly(idx, COL_USE_DEF, true);
  				grdParams->SetReadOnly(idx, COL_DEF_VAL, true);
  
  				grdParams->SetCellBackgroundColour(idx, COL_NAME,    wxColour(235, 235, 235, 90));
  				grdParams->SetCellBackgroundColour(idx, COL_TYPE,    wxColour(235, 235, 235, 90));
  				grdParams->SetCellBackgroundColour(idx, COL_NULL,    wxColour(235, 235, 235, 90));
  				grdParams->SetCellBackgroundColour(idx, COL_EXPR,    wxColour(235, 235, 235, 90));
  				grdParams->SetCellBackgroundColour(idx, COL_VALUE,   wxColour(235, 235, 235, 90));
  				grdParams->SetCellBackgroundColour(idx, COL_USE_DEF, wxColour(235, 235, 235, 90));
  				grdParams->SetCellBackgroundColour(idx, COL_DEF_VAL, wxColour(235, 235, 235, 90));
  
  				idx++;
  			}
  		}
  	}
  
  	// Move the cursor to the first value (so that the user
  	// can just start typing)
  
  	grdParams->SetGridCursor(0, COL_VALUE);
  	grdParams->SetFocus();
  }
  
  void dlgDirectDbg::LoadLastCellSetting(int row_number, int parameter_index,
                                         int index_array_bound, bool isArray)
  {
  	wxString lastValue, valKey;
  	dbgTargetInfo *target = m_controller->GetTargetInfo();
  	long num;
  
  	settings->Read(wxT("Debugger/Proc/OID"), &num, -1L);
  	if(num != target->GetOid())
  	{
  		return;
  	}
  	else
  	{
  		if(!isArray)
  		{
  			// Non array elements cell index is 0
  			valKey = wxString::Format(wxT("Debugger/Proc/%d/0/"), parameter_index);
  			settings->Read(valKey + wxT("VAL"), &lastValue, wxEmptyString);
  			grdParams->SetCellValue(row_number, COL_VALUE, lastValue);
  		}
  		else
  		{
  			long arrCnt;
  
  			settings->Read(
  			    wxString::Format(
  			        wxT("Debugger/Proc/%d/"), parameter_index)
  			    + wxT("ArrCnt"), &arrCnt, 0L);
  
  			// Refresh an entire array element
  			for( int i = 0; i <= index_array_bound && i <= arrCnt - 1; i++ )
  			{
  				valKey = wxString::Format(wxT("Debugger/Proc/%d/%d/"), parameter_index, i);
  				settings->Read(valKey + wxT("VAL"), &lastValue, wxEmptyString);
  				grdParams->SetCellValue(row_number - index_array_bound + i, COL_VALUE,
  				                        lastValue);
  			}
  		}
  	}
  
  }
  
  ////////////////////////////////////////////////////////////////////////////////
  // LoadSettings()
  //
  //    Loads default values from our .ini file. We save the OID of the most
  //    recent direct-debugging target when close a session. If we're direct-
  //    debugging the same target this time around, we load the argument values
  //    from the .ini file.
  void dlgDirectDbg::LoadSettings()
  {
  	long     num, arrCnt;
  	wxString tmp, key;
  	bool     initPkgCon = false;
  
  	settings->Read(wxT("Debugger/Proc/OID"), &num, -1L);
  
  	dbgTargetInfo *target = m_controller->GetTargetInfo();
  	pgDbgArgs *args = m_controller->GetTargetInfo()->GetArgs();
  
  	if (num != target->GetOid())
  	{
  		chkPkgInit->SetValue(target->GetPkgInitOid() != 0L);
  		chkPkgInit->Enable(target->GetPkgInitOid() != 0L);
  
  		return;
  	}
  	else
  	{
  		settings->Read(wxT("Debugger/Proc/initialize_package_constructor"), &initPkgCon, true);
  
  		chkPkgInit->SetValue(&initPkgCon && target->GetPkgInitOid() != 0L);
  		chkPkgInit->Enable(target->GetPkgInitOid() != 0L);
  	}
  
  	settings->Read(wxT("Debugger/Proc/args"), &num, 0L);
  
  	if (num <= 0L)
  		return;
  
  	if (!args)
  		return;
  
  	for (int cnt = 0, row = 0; cnt < num && cnt < (int)args->Count();)
  	{
  		ctlGridCellBoolEditor *editor =
  		    dynamic_cast<ctlGridCellBoolEditor *>(
  		        grdParams->GetCellEditor(row, COL_NULL));
  		dbgArgInfo *arg
  		    = editor != NULL ? editor->GetArg() : NULL;
  
  		if (arg == NULL)
  		{
  			row++;
  
  			continue;
  		}
  
  		key = wxString::Format(wxT("Debugger/Proc/%d/"), cnt);
  
  		// Use NULL?
  		settings->Read(key + wxT("NULL"), &tmp, wxEmptyString);
  		grdParams->SetCellValue(row, COL_NULL, tmp);
  
  		// Use Default (if available)
  		settings->Read(key + wxT("USE_DEF"), &tmp, wxEmptyString);
  		grdParams->SetCellValue(row, COL_USE_DEF, tmp);
  
  		// Is Array/VARIADIC?
  		settings->Read(key + wxT("ArrCnt"), &arrCnt, 0L);
  
  		if (arrCnt > 0L)
  		{
  			// An individual variable can be an expression or can be the 'NULL' value
  			wxString valKey, strVal, strExpr, strNull;
  
  			for (int idx = 0; idx < arrCnt; idx++, row++)
  			{
  				valKey = wxString::Format(wxT("Debugger/Proc/%d/%d/"), cnt, idx);
  
  				// Use 'NULL'?
  				settings->Read(valKey + wxT("NULL"), &strNull, wxEmptyString);
  
  				// Use EXPR?
  				settings->Read(valKey + wxT("EXPR"), &strExpr, wxEmptyString);
  
  				// Value
  				settings->Read(valKey + wxT("VAL"), &strVal, wxEmptyString);
  
  				if (arg->IsArray())
  				{
  					int arrRow = row + 1;
  
  					if (idx == 0)
  					{
  						grdParams->SetReadOnly(row, COL_EXPR, true);
  						grdParams->SetReadOnly(row, COL_VALUE, true);
  
  						grdParams->SetCellBackgroundColour(row, COL_EXPR,  wxColour(235, 235, 235, 90));
  						grdParams->SetCellBackgroundColour(row, COL_VALUE, wxColour(235, 235, 235, 90));
  
  						grdParams->SetRowLabelValue(
  						    row, wxString::Format(wxT("%d"), cnt + 1));
  					}
  					grdParams->InsertRows(arrRow, 1, false);
  					grdParams->SetRowLabelValue(arrRow, wxT("-"));
  
  					grdParams->SetCellValue(arrRow, COL_TYPE, arg->GetBaseType());
  
  					// Is the value an expression?
  					grdParams->SetCellEditor(arrRow, COL_EXPR, new ctlGridCellBoolEditor());
  					grdParams->SetCellRenderer(arrRow, COL_EXPR,
  					                           new wxGridCellBoolRenderer());
  					grdParams->SetCellValue(arrRow, COL_EXPR, strExpr);
  
  					// Set value to NULL?
  					grdParams->SetCellEditor(arrRow, COL_NULL,
  					                         new ctlGridCellBoolEditor(arg));
  					grdParams->SetCellRenderer(arrRow, COL_NULL,
  					                           new wxGridCellBoolRenderer());
  					grdParams->SetCellValue(arrRow, COL_NULL, strNull);
  
  					// Set value
  					grdParams->SetCellValue(arrRow, COL_VALUE, strVal);
  
  					grdParams->SetReadOnly(arrRow, COL_NULL, false);
  					grdParams->SetReadOnly(arrRow, COL_EXPR, false);
  					grdParams->SetReadOnly(arrRow, COL_VALUE, false);
  
  					grdParams->SetCellBackgroundColour(arrRow, COL_NAME,    wxColour(235, 235, 235, 90));
  					grdParams->SetCellBackgroundColour(arrRow, COL_TYPE,    wxColour(235, 235, 235, 90));
  					grdParams->SetCellBackgroundColour(arrRow, COL_USE_DEF, wxColour(235, 235, 235, 90));
  					grdParams->SetCellBackgroundColour(arrRow, COL_DEF_VAL, wxColour(235, 235, 235, 90));
  
  					grdParams->SetReadOnly(arrRow + 1, COL_NAME,    true);
  					grdParams->SetReadOnly(arrRow + 1, COL_TYPE,    true);
  					grdParams->SetReadOnly(arrRow + 1, COL_NULL,    true);
  					grdParams->SetReadOnly(arrRow + 1, COL_EXPR,    true);
  					grdParams->SetReadOnly(arrRow + 1, COL_VALUE,   true);
  					grdParams->SetReadOnly(arrRow + 1, COL_USE_DEF, true);
  					grdParams->SetReadOnly(arrRow + 1, COL_DEF_VAL, true);
  
  					grdParams->SetCellBackgroundColour(arrRow + 1, COL_NAME, wxColour(235, 235, 235, 90));
  					grdParams->SetCellBackgroundColour(arrRow + 1, COL_TYPE, wxColour(235, 235, 235, 90));
  					grdParams->SetCellBackgroundColour(arrRow + 1, COL_NULL, wxColour(235, 235, 235, 90));
  					grdParams->SetCellBackgroundColour(arrRow + 1, COL_EXPR, wxColour(235, 235, 235, 90));
  					grdParams->SetCellBackgroundColour(arrRow + 1, COL_VALUE, wxColour(235, 235, 235, 90));
  					grdParams->SetCellBackgroundColour(arrRow + 1, COL_USE_DEF,
  					                                   wxColour(235, 235, 235, 90));
  					grdParams->SetCellBackgroundColour(arrRow + 1, COL_DEF_VAL,
  					                                   wxColour(235, 235, 235, 90));
  
  					grdParams->SetRowLabelValue(arrRow + 1, wxT("+"));
  				}
  				else
  				{
  					grdParams->SetCellValue(row, COL_VALUE, strVal);
  					grdParams->SetCellValue(row, COL_EXPR, strExpr);
  					grdParams->SetCellValue(row, COL_NULL, strNull);
  
  					grdParams->SetReadOnly(row, COL_NULL, false);
  					grdParams->SetReadOnly(row, COL_EXPR, false);
  					grdParams->SetReadOnly(row, COL_VALUE, false);
  
  					grdParams->SetRowLabelValue(
  					    row, wxString::Format(wxT("%d"), cnt + 1));
  				}
  			}
  			if (arg->IsArray())
  				row++;
  		}
  		cnt++;
  	}
  }
  
  
  void dlgDirectDbg::OnOk(wxCommandEvent &_ev)
  {
  	if (m_thread)
  		return;
  
  	grdParams->Enable(false);
  	btnDebug->Enable(false);
  
  	m_thread = new dbgArgValueEvaluator(m_conn, this);
  
  	if (m_thread->Create() != wxTHREAD_NO_ERROR)
  	{
  		delete m_thread;
  		m_thread = NULL;
  
  		wxLogError(_("Failed to create a debugging thread."));
  		EndModal(wxID_CANCEL);
  
  		return;
  	}
  
  	m_thread->Run();
  }
  
  
  void dlgDirectDbg::OnCancel(wxCommandEvent &_ev)
  {
  	if (m_thread)
  	{
  		if (m_thread->IsRunning())
  		{
  			m_thread->CancelEval();
  			m_thread->Wait();
  		}
  
  		delete m_thread;
  		m_thread = NULL;
  	}
  	_ev.Skip();
  }
  
  void dlgDirectDbg::OnClickGridLabel(wxGridEvent &_ev)
  {
  	if (_ev.AltDown() || _ev.ControlDown() || _ev.ShiftDown())
  	{
  		_ev.Skip();
  
  		return;
  	}
  
  	int row = _ev.GetRow();
  	int col = _ev.GetCol();
  
  	if (row < 0 || col > 0)
  	{
  		_ev.Skip();
  
  		return;
  	}
  
  	wxString strLabel = grdParams->GetRowLabelValue(row);
  
  	if (strLabel == wxT("+"))
  	{
  		wxASSERT(row != 0);
  
  		ctlGridCellBoolEditor *editor =
  		    dynamic_cast<ctlGridCellBoolEditor *>(
  		        grdParams->GetCellEditor(row - 1, COL_NULL));
  		dbgArgInfo *arg
  		    = editor != NULL ? editor->GetArg() : NULL;
  
  		grdParams->InsertRows(row, 1, false);
  		grdParams->SetRowLabelValue(row, wxT("-"));
  
  		grdParams->SetCellValue(row, COL_TYPE, arg->GetBaseType());
  		grdParams->SetCellBackgroundColour(row, COL_TYPE, wxColour(235, 235, 235, 90));
  		grdParams->SetCellBackgroundColour(row, COL_NAME, wxColour(235, 235, 235, 90));
  		grdParams->SetCellBackgroundColour(row, COL_USE_DEF, wxColour(235, 235, 235, 90));
  		grdParams->SetCellBackgroundColour(row, COL_DEF_VAL, wxColour(235, 235, 235, 90));
  
  		// Is the value an expression?
  		grdParams->SetCellEditor(row, COL_EXPR, new ctlGridCellBoolEditor());
  		grdParams->SetCellRenderer(row, COL_EXPR, new wxGridCellBoolRenderer());
  		grdParams->SetCellValue(row, COL_EXPR, wxT(""));
  
  		// Set value to NULL?
  		grdParams->SetCellEditor(row, COL_NULL, new ctlGridCellBoolEditor(arg));
  		grdParams->SetCellRenderer(row, COL_NULL, new wxGridCellBoolRenderer());
  		grdParams->SetCellValue(row, COL_NULL, wxT("1"));
  
  		row++;
  		grdParams->SetRowLabelValue(row, wxT("+"));
  	}
  	else if (strLabel == wxT("-"))
  	{
  		dbgArgInfo *arg = NULL;
  		grdParams->DeleteRows(row, 1, false);
  	}
  	else
  		return;
  
  	// Update the row labels
  	ctlGridCellBoolEditor *editor = NULL;
  	dbgArgInfo *arg = NULL,
  	            *prev = NULL;
  	wxString strName;
  
  	int totalRows = grdParams->GetNumberRows(),
  	    idx = 0;
  	row = 0;
  
  	while (row < totalRows)
  	{
  		editor =
  		    dynamic_cast<ctlGridCellBoolEditor *>(
  		        grdParams->GetCellEditor(row, COL_NULL));
  		arg	= editor != NULL ? editor->GetArg() : NULL;
  		strName = grdParams->GetCellValue(row, COL_NAME);
  
  		if (strName.IsEmpty() && arg)
  			grdParams->SetRowLabelValue(row, wxT("-"));
  		else if (!strName.IsEmpty() && !arg)
  			grdParams->SetRowLabelValue(row, wxT("+"));
  		else
  		{
  			idx++;
  			grdParams->SetRowLabelValue(row, wxString::Format(wxT("%d"), idx));
  		}
  
  		row++;
  	}
  }
  
  ////////////////////////////////////////////////////////////////////////////////
  // SaveSettings()
  //
  //    Save default values to our .ini file. We save the OID of the most
  //    recent direct-debugging target when close a session. We also save the
  //    value of each argument - if you debug the same target again next time,
  //    loadSettings() will initialize the parameter-values window with the
  //    same parameter values that you entered in this session.
  //
  void dlgDirectDbg::SaveSettings()
  {
  	wxString    strName, strExpr, strVal, strNull, strDef, strKey, strValExpr;
  	dbgArgInfo *prev = NULL,
  	            *arg = NULL;
  	int         row = 0,
  	            idx = -1,
  	            arrCnt = 1;
  
  	// Save the current function/procedure/trigger OID
  	settings->WriteLong(wxT("Debugger/Proc/OID"),
  	                    m_controller->GetTargetInfo()->GetOid());
  
  	// Save - whether we need to debug the package constructor
  	settings->WriteBool(wxT("Debugger/Proc/initialize_package_constructor"),
  	                    (chkPkgInit->IsEnabled() && chkPkgInit->GetValue()));
  
  	for (; row < grdParams->GetNumberRows(); row++)
  	{
  		ctlGridCellBoolEditor *editor =
  		    dynamic_cast<ctlGridCellBoolEditor *>(
  		        grdParams->GetCellEditor(row, COL_NULL));
  		arg	= editor != NULL ? editor->GetArg() : NULL;
  
  		// This should be the information for add/remove values for an array
  		if (arg == NULL)
  		{
  			continue;
  		}
  		else if (prev != arg)
  		{
  			idx++;
  			prev = arg;
  
  			if (arg->IsArray())
  			{
  				arrCnt = 0;
  			}
  			else
  			{
  				arrCnt = 1;
  			}
  		}
  		else
  		{
  			arrCnt++;
  		}
  
  		settings->WriteInt(wxString::Format(wxT("Debugger/Proc/%d/ArrCnt"), idx), arrCnt);
  
  		if ((arrCnt == 0 && arg->IsArray()) ||
  		        (arrCnt == 1 && !arg->IsArray()))
  		{
  			// Use Default (if available)
  			settings->Write(wxString::Format(wxT("Debugger/Proc/%d/USE_DEF"), idx),
  			                grdParams->GetCellValue(row, COL_USE_DEF));
  
  			// Use NULL?
  			settings->Write(wxString::Format(wxT("Debugger/Proc/%d/NULL"), idx),
  			                grdParams->GetCellValue(row, COL_NULL));
  
  			if (arrCnt == 0 && arg->IsArray())
  				continue;
  		}
  
  		strKey = wxString::Format(wxT("Debugger/Proc/%d/%d/"), idx, arrCnt - 1);
  
  		// Use NULL?
  		settings->Write(strKey + wxT("NULL"),
  		                grdParams->GetCellValue(row, COL_NULL));
  
  		// Is value EXPR?
  		settings->Write(strKey + wxT("EXPR"),
  		                grdParams->GetCellValue(row, COL_EXPR));
  
  		// Value
  		settings->Write(strKey + wxT("VAL"),
  		                grdParams->GetCellValue(row, COL_VALUE));
  	}
  
  	// Save the number of arguments
  	settings->WriteLong(wxT("Debugger/Proc/args"), (long)(idx + 1));
  }
  
  
  ctlGridCellBoolEditor::ctlGridCellBoolEditor(dbgArgInfo *_arg)
  	: m_arg(_arg)
  {}
  
  
  void ctlGridCellBoolEditor::BeginEdit(int _row, int _col, wxGrid *_grid)
  {
  	wxGridCellBoolEditor::BeginEdit(_row, _col, _grid);
  
  	wxFocusEvent event (wxEVT_KILL_FOCUS);
  	if (m_control)
  	{
  		m_control->GetEventHandler()->AddPendingEvent(event);
  	}
  }
  
  
  wxGrid *dlgDirectDbg::GetParamsGrid()
  {
  	return grdParams;
  }
  
  bool dlgDirectDbg::DebugPkgConstructor()
  {
  	return chkPkgInit->IsEnabled() && chkPkgInit->GetValue();
  }
  
  
  wxGridCellEditor *ctlGridCellBoolEditor::Clone() const
  {
  	return new ctlGridCellBoolEditor(m_arg);
  }
  
  
  void dlgDirectDbg::ResultArgsUpdated(wxCommandEvent &_ev)
  {
  	SaveSettings();
  	SavePosition();
  
  	if (m_thread)
  	{
  		if (m_thread->IsRunning())
  		{
  			m_thread->CancelEval();
  			m_thread->Wait();
  		}
  
  		delete m_thread;
  		m_thread = NULL;
  	}
  
  	if (IsModal())
  		EndModal(wxID_OK);
  	else
  		Destroy();
  }
  
  
  void dlgDirectDbg::ResultArgsUpdateError(wxCommandEvent &_ev)
  {
  	if (_ev.GetInt() == pgQueryResultEvent::PGQ_CONN_LOST)
  	{
  		if(wxMessageBox(
  		            _("Connection to the database server lost!\nDo you want to try to reconnect to the server?"),
  		            _("Connection Lost"), wxICON_ERROR | wxICON_QUESTION | wxYES_NO) == wxID_YES)
  		{
  			if (m_thread)
  			{
  				if (m_thread->IsRunning())
  				{
  					m_thread->CancelEval();
  					m_thread->Wait();
  				}
  
  				delete m_thread;
  				m_thread = NULL;
  			}
  			m_conn->Reconnect();
  
  			return;
  		}
  		EndModal(wxID_CANCEL);
  
  		return;
  	}
  
  	if (m_thread)
  	{
  		if (m_thread->IsRunning())
  		{
  			m_thread->CancelEval();
  			m_thread->Wait();
  		}
  
  		delete m_thread;
  		m_thread = NULL;
  	}
  
  	wxLogError(_ev.GetString());
  
  	grdParams->Enable(true);
  	btnDebug->Enable(true);
  }
  
  
  void dbgArgValueEvaluator::NoticeHandler(void *, const char *)
  {
  	// Ignore the notices
  }
  
  
  void *dbgArgValueEvaluator::Entry()
  {
  	bool argNull, nullVal, useDef;
  	wxString strVal, strValExpr;
  	dbgArgInfo *prev = NULL,
  	            *arg = NULL;
  
  	wxGrid *grd = m_dlg->GetParamsGrid();
  
  	m_dlg->m_controller->GetTargetInfo()->DebugPackageConstructor()
  	    = m_dlg->DebugPkgConstructor();
  
  	m_conn->RegisterNoticeProcessor(dbgArgValueEvaluator::NoticeHandler, NULL);
  
  	for (int row = 0, idx = 0, arrCnt = 1, arr_idx_bound = 0;
  	        row < grd->GetNumberRows() && !m_cancelled; row++)
  	{
  		ctlGridCellBoolEditor *editor =
  		    dynamic_cast<ctlGridCellBoolEditor *>(
  		        grd->GetCellEditor(row, dlgDirectDbg::COL_NULL));
  		arg	= editor != NULL ? editor->GetArg() : NULL;
  
  		// This should be the information for add/remove values for an array
  		if (arg == NULL)
  		{
  			// prev was an array, can we fetch value for the same
  			if (prev && !prev->Null())
  			{
  				wxString res =
  				    m_conn->ExecuteScalar(
  				        wxT("SELECT ARRAY[") + strValExpr + wxT("]::") + prev->GetTypeName(),
  				        false);
  
  				if (m_cancelled)
  				{
  					m_conn->RegisterNoticeProcessor(NULL, NULL);
  
  					return (void *)NULL;
  				}
  				if (m_conn->GetStatus() == PGCONN_BAD)
  				{
  					wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATE_ERROR);
  
  					ev.SetInt(pgQueryResultEvent::PGQ_CONN_LOST);
  
  					m_dlg->GetEventHandler()->AddPendingEvent(ev);
  
  					m_conn->RegisterNoticeProcessor(NULL, NULL);
  
  					return (void *)NULL;
  				}
  				if (m_conn->GetLastResultStatus() == PGRES_TUPLES_OK)
  				{
  					prev->Value() = res;
  				}
  				else
  				{
  					wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATE_ERROR);
  					wxString strName = prev->GetName();
  					wxString strMsg;
  
  					if (strName.IsEmpty())
  					{
  						ev.SetString(
  						    wxString::Format(
  						        _("The specified value for argument #%d is not valid.\nPlease re-enter the value for it."),
  						        idx));
  					}
  					else
  					{
  						ev.SetString(
  						    wxString::Format(
  						        _("Specified value for argument '%s' is not valid.\nPlease re-enter the value for it."),
  						        strName));
  					}
  					ev.SetInt(pgQueryResultEvent::PGQ_RESULT_ERROR);
  					m_dlg->LoadLastCellSetting(row - 1, idx - 1, arr_idx_bound - 1, true);
  					m_dlg->GetEventHandler()->AddPendingEvent(ev);
  
  					m_conn->RegisterNoticeProcessor(NULL, NULL);
  
  					return (void *)NULL;
  
  				}
  				prev = NULL;
  				arr_idx_bound = 0;
  			}
  
  			continue;
  		}
  		else if (prev != arg)
  		{
  			idx++;
  			argNull = false;
  			nullVal = false;
  			useDef  = false;
  
  			strValExpr = wxEmptyString;
  		}
  
  		if (prev != arg)
  		{
  			prev = arg;
  
  			// Use Default (if available)
  			arg->UseDefault() = wxGridCellBoolEditor::IsTrueValue(
  			                        grd->GetCellValue(row, dlgDirectDbg::COL_USE_DEF));
  			// NULL?
  			arg->Null() = wxGridCellBoolEditor::IsTrueValue(
  			                  grd->GetCellValue(row, dlgDirectDbg::COL_NULL));
  
  			if (arg->UseDefault())
  				strValExpr = arg->Default();
  
  			if (arg->IsArray())
  				continue;
  		}
  
  		// Use NULL?
  		//
  		//   Keep this just before 'Use Default' column as - we may have non-empty
  		//   value in the 'VAL' column
  
  		if (!arg->UseDefault() || !arg->Null())
  		{
  			if (!strValExpr.IsEmpty())
  			{
  				strValExpr += wxT(", ");
  			}
  
  			if (wxGridCellBoolEditor::IsTrueValue(
  			            grd->GetCellValue(row, dlgDirectDbg::COL_NULL)))
  			{
  				strValExpr += wxT("NULL");
  			}
  			else if (wxGridCellBoolEditor::IsTrueValue(
  			             grd->GetCellValue(row, dlgDirectDbg::COL_EXPR)))
  			{
  				strValExpr += wxT("(") + grd->GetCellValue(row, dlgDirectDbg::COL_VALUE) + wxT(")");
  			}
  			else
  			{
  				strValExpr += m_conn->qtDbString(
  				                  grd->GetCellValue(row, dlgDirectDbg::COL_VALUE));
  			}
  			strValExpr.Append(wxT("::"))
  			.Append(arg->IsArray() ?
  			        arg->GetBaseType() : arg->GetTypeName());
  
  			if(arg->IsArray())
  			{
  				arr_idx_bound ++;
  			}
  		}
  
  		if (!arg->IsArray() && !arg->Null())
  		{
  			pgSet *set =
  			    m_conn->ExecuteSet(wxT("SELECT ") + strValExpr, false);
  
  			if (m_cancelled)
  			{
  				return (void *)NULL;
  			}
  			if (m_conn->GetStatus() == PGCONN_BAD)
  			{
  				wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATE_ERROR);
  
  				ev.SetString(_("Connection to the database server lost!"));
  				ev.SetInt(pgQueryResultEvent::PGQ_CONN_LOST);
  
  				m_dlg->GetEventHandler()->AddPendingEvent(ev);
  
  				m_conn->RegisterNoticeProcessor(NULL, NULL);
  
  				if (set)
  					delete set;
  
  				return (void *)NULL;
  			}
  			if (m_conn->GetLastResultStatus() == PGRES_TUPLES_OK &&
  			        set && set->NumRows() > 0L)
  			{
  				if (set->IsNull(0))
  				{
  					arg->Null() = true;
  				}
  				else
  				{
  					arg->Null() = false;
  					arg->Value() = set->GetVal(0);
  				}
  			}
  			else
  			{
  				wxString strName = arg->GetName();
  
  				if (strName.IsEmpty())
  					strName = wxString::Format(_("Param#%d"), idx);
  
  				wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATE_ERROR);
  
  				ev.SetString(
  				    wxString::Format(_("Please re-enter the value for argument '%s'."),
  				                     strName));
  				ev.SetInt(pgQueryResultEvent::PGQ_RESULT_ERROR);
  
  				m_dlg->GetEventHandler()->AddPendingEvent(ev);
  				m_dlg->LoadSettings();
  				// For scalar variable, always index of array bound is 0.
  				m_dlg->LoadLastCellSetting(row, idx - 1, 0, false);
  				m_conn->RegisterNoticeProcessor(NULL, NULL);
  
  				return (void *)NULL;
  			}
  		}
  	}
  
  	wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_ARGS_UPDATED);
  	m_dlg->GetEventHandler()->AddPendingEvent(ev);
  
  	m_conn->RegisterNoticeProcessor(NULL, NULL);
  
  	return (void *)NULL;
  }
  
  
  void dbgArgValueEvaluator::CancelEval()
  {
  	m_cancelled = true;
  
  	if (m_conn->GetTxStatus() == PGCONN_TXSTATUS_ACTIVE)
  		m_conn->CancelExecution();
  }
  
  dbgArgValueEvaluator::dbgArgValueEvaluator(pgConn *_conn, dlgDirectDbg *_dlg)
  	: wxThread(wxTHREAD_JOINABLE), m_conn(_conn), m_dlg(_dlg), m_cancelled(false)
  {}
  