text: //////////////////////////////////////////////////////////////////////////
  //
  // pgAdmin III - PostgreSQL Tools
  //
  // Copyright (C) 2002 - 2016, The pgAdmin Development Team
  // This software is released under the PostgreSQL Licence
  //
  // dlgTextSearchConfiguration.cpp - PostgreSQL Text Search Configuration Property
  //
  //////////////////////////////////////////////////////////////////////////
  
  // wxWindows headers
  #include <wx/wx.h>
  
  // App headers
  #include "pgAdmin3.h"
  #include "utils/misc.h"
  #include "utils/pgDefs.h"
  
  #include "dlg/dlgTextSearchConfiguration.h"
  #include "schema/pgSchema.h"
  #include "schema/pgTextSearchConfiguration.h"
  #include "schema/pgDatatype.h"
  
  
  // pointer to controls
  #define cbParser            CTRL_COMBOBOX2("cbParser")
  #define cbCopy              CTRL_COMBOBOX2("cbCopy")
  #define lstTokens           CTRL_LISTVIEW("lstTokens")
  #define cbToken             CTRL_COMBOBOX2("cbToken")
  #define txtDictionary       CTRL_TEXT("txtDictionary")
  #define cbDictionary        CTRL_CHOICE("cbDictionary")
  #define btnAdd              CTRL_BUTTON("wxID_ADD")
  #define btnRemove           CTRL_BUTTON("wxID_REMOVE")
  
  
  BEGIN_EVENT_TABLE(dlgTextSearchConfiguration, dlgTypeProperty)
  	EVT_TEXT(XRCID("cbParser"),                 dlgTextSearchConfiguration::OnChange)
  	EVT_COMBOBOX(XRCID("cbParser"),             dlgTextSearchConfiguration::OnChange)
  	EVT_TEXT(XRCID("cbCopy"),                   dlgTextSearchConfiguration::OnChange)
  	EVT_COMBOBOX(XRCID("cbCopy"),               dlgTextSearchConfiguration::OnChange)
  	EVT_LIST_ITEM_SELECTED(XRCID("lstTokens"),  dlgTextSearchConfiguration::OnSelChangeToken)
  	EVT_TEXT(XRCID("cbToken"),                  dlgTextSearchConfiguration::OnChangeCbToken)
  	EVT_COMBOBOX(XRCID("cbToken"),              dlgTextSearchConfiguration::OnChangeCbToken)
  	EVT_TEXT(XRCID("txtDictionary"),            dlgTextSearchConfiguration::OnChangeTxtDictionary)
  	EVT_CHOICE(XRCID("cbDictionary"),           dlgTextSearchConfiguration::OnChangeCbDictionary)
  	EVT_BUTTON(wxID_ADD,                        dlgTextSearchConfiguration::OnAddToken)
  	EVT_BUTTON(wxID_REMOVE,                     dlgTextSearchConfiguration::OnRemoveToken)
  #ifdef __WXMAC__
  	EVT_SIZE(                                   dlgTextSearchConfiguration::OnChangeSize)
  #endif
  END_EVENT_TABLE();
  
  
  
  dlgProperty *pgTextSearchConfigurationFactory::CreateDialog(frmMain *frame, pgObject *node, pgObject *parent)
  {
  	return new dlgTextSearchConfiguration(this, frame, (pgTextSearchConfiguration *)node, (pgSchema *)parent);
  }
  
  dlgTextSearchConfiguration::dlgTextSearchConfiguration(pgaFactory *f, frmMain *frame, pgTextSearchConfiguration *node, pgSchema *sch)
  	: dlgTypeProperty(f, frame, wxT("dlgTextSearchConfiguration"))
  {
  	schema = sch;
  	config = node;
  	dirtyTokens = false;
  
  	lstTokens->CreateColumns(0, _("Token"), _("Dictionaries"));
  
  	cbCopy->Disable();
  }
  
  
  pgObject *dlgTextSearchConfiguration::GetObject()
  {
  	return config;
  }
  
  
  int dlgTextSearchConfiguration::Go(bool modal)
  {
  	wxString qry;
  	pgSet *set;
  
  	cbParser->Append(wxT(""));
  
  	qry = wxT("SELECT prsname, nspname\n")
  	      wxT("  FROM pg_ts_parser\n")
  	      wxT("  JOIN pg_namespace n ON n.oid=prsnamespace\n")
  	      wxT("  ORDER BY prsname\n");
  
  	set = connection->ExecuteSet(qry);
  	if (set)
  	{
  		while (!set->Eof())
  		{
  			wxString procname = database->GetSchemaPrefix(set->GetVal(wxT("nspname"))) + set->GetVal(wxT("prsname"));
  			cbParser->Append(procname);
  			set->MoveNext();
  		}
  		delete set;
  	}
  
  	cbCopy->Append(wxT(""));
  
  	qry = wxT("SELECT cfgname, nspname\n")
  	      wxT("  FROM pg_ts_config\n")
  	      wxT("  JOIN pg_namespace n ON n.oid=cfgnamespace\n")
  	      wxT("  ORDER BY nspname, cfgname\n");
  
  	set = connection->ExecuteSet(qry);
  	if (set)
  	{
  		cbCopy->Enable();
  		while (!set->Eof())
  		{
  			wxString configname = database->GetSchemaPrefix(set->GetVal(wxT("nspname"))) + set->GetVal(wxT("cfgname"));
  			cbCopy->Append(configname);
  			set->MoveNext();
  		}
  		delete set;
  	}
  
  	if (config)
  	{
  		// edit mode
  		cbSchema->Enable(connection->BackendMinimumVersion(9, 1));
  		cbParser->SetValue(config->GetParser());
  		cbCopy->Disable();
  
  		// second tab handling
  		size_t i;
  		for (i = 0 ; i < config->GetTokens().GetCount() ; i++)
  		{
  			wxString token = config->GetTokens().Item(i);
  			lstTokens->AppendItem(token.BeforeFirst('/'), token.AfterFirst('/'));
  		}
  
  		pgSet *tokens;
  		tokens = connection->ExecuteSet(
  		             wxT("SELECT alias FROM ts_token_type(")
  		             + config->GetParserOidStr()
  		             + wxT(") ORDER BY alias"));
  
  		if (tokens)
  		{
  			while (!tokens->Eof())
  			{
  				cbToken->Append(tokens->GetVal(wxT("alias")));
  				tokens->MoveNext();
  			}
  			delete tokens;
  		}
  
  		pgSet *dictionaries;
  		dictionaries = connection->ExecuteSet(
  		                   wxT("SELECT dictname FROM pg_ts_dict ORDER BY dictname"));
  
  		if (dictionaries)
  		{
  			while (!dictionaries->Eof())
  			{
  				cbDictionary->Append(dictionaries->GetVal(wxT("dictname")));
  				dictionaries->MoveNext();
  			}
  			delete dictionaries;
  		}
  
  		if (!connection->BackendMinimumVersion(8, 0))
  			cbOwner->Disable();
  	}
  	else
  	{
  		// create mode
  	}
  
  	btnAdd->Disable();
  	btnRemove->Disable();
  
  	return dlgProperty::Go(modal);
  }
  
  
  pgObject *dlgTextSearchConfiguration::CreateObject(pgCollection *collection)
  {
  	pgObject *obj = textSearchConfigurationFactory.CreateObjects(collection, 0,
  	                wxT("\n   AND cfg.cfgname=") + qtDbString(GetName()) +
  	                wxT("\n   AND cfg.cfgnamespace=") + schema->GetOidStr());
  
  	return obj;
  }
  
  
  #ifdef __WXMAC__
  void dlgTextSearchConfiguration::OnChangeSize(wxSizeEvent &ev)
  {
  	lstTokens->SetSize(wxDefaultCoord, wxDefaultCoord,
  	                   ev.GetSize().GetWidth(), ev.GetSize().GetHeight() - 350);
  	if (GetAutoLayout())
  	{
  		Layout();
  	}
  }
  #endif
  
  
  void dlgTextSearchConfiguration::CheckChange()
  {
  	if (config)
  	{
  		EnableOK(txtName->GetValue() != config->GetName()
  		         || cbSchema->GetValue() != config->GetSchema()->GetName()
  		         || txtComment->GetValue() != config->GetComment()
  		         || cbOwner->GetValue() != config->GetOwner()
  		         || dirtyTokens);
  	}
  	else
  	{
  		wxString name = GetName();
  		bool enable = true;
  		CheckValid(enable, !name.IsEmpty(), _("Please specify name."));
  		CheckValid(enable, cbParser->GetGuessedSelection() > 0 || cbCopy->GetGuessedSelection() > 0 , _("Please select a parser or a configuration to copy."));
  
  		EnableOK(enable);
  	}
  }
  
  
  void dlgTextSearchConfiguration::OnChange(wxCommandEvent &ev)
  {
  	cbParser->Enable(cbCopy->GetValue().Length() == 0);
  	cbCopy->Enable(cbParser->GetValue().Length() == 0);
  
  	CheckChange();
  }
  
  void dlgTextSearchConfiguration::OnChangeCbToken(wxCommandEvent &ev)
  {
  	bool found = false;
  
  	for (int pos = 0 ; pos < lstTokens->GetItemCount() ; pos++)
  	{
  		if (lstTokens->GetText(pos).IsSameAs(cbToken->GetValue(), false))
  		{
  			lstTokens->Select(pos);
  			found = true;
  			break;
  		}
  	}
  
  	btnAdd->Enable(cbToken->GetValue().Length() > 0);
  }
  
  
  void dlgTextSearchConfiguration::OnChangeCbDictionary(wxCommandEvent &ev)
  {
  	if (!txtDictionary->GetValue().Matches(wxT("*") + cbDictionary->GetStringSelection() + wxT("*")))
  	{
  		wxString dicts = txtDictionary->GetValue();
  		if (dicts.Length() > 0)
  			dicts += wxT(",");
  		dicts += cbDictionary->GetStringSelection();
  
  		txtDictionary->SetValue(dicts);
  	}
  	btnAdd->Enable(cbToken->GetValue().Length() > 0);
  }
  
  
  void dlgTextSearchConfiguration::OnChangeTxtDictionary(wxCommandEvent &ev)
  {
  	btnAdd->Enable(cbToken->GetValue().Length() > 0);
  }
  
  
  void dlgTextSearchConfiguration::OnSelChangeToken(wxListEvent &ev)
  {
  	int row = lstTokens->GetSelection();
  	if (row >= 0)
  	{
  		cbToken->SetValue(lstTokens->GetText(row, 0));
  		txtDictionary->SetValue(lstTokens->GetText(row, 1));
  	}
  
  	btnAdd->Enable(cbToken->GetValue().Length() > 0);
  	btnRemove->Enable(row >= 0);
  }
  
  
  void dlgTextSearchConfiguration::OnAddToken(wxCommandEvent &ev)
  {
  	bool found = false;
  
  	for (int pos = 0 ; pos < lstTokens->GetItemCount() ; pos++)
  	{
  		if (lstTokens->GetText(pos).IsSameAs(cbToken->GetValue(), false))
  		{
  			lstTokens->SetItem(pos, 1, txtDictionary->GetValue());
  			found = true;
  			break;
  		}
  	}
  
  	if (!found)
  	{
  		lstTokens->AppendItem(cbToken->GetValue(), txtDictionary->GetValue());
  	}
  
  	btnAdd->Disable();
  
  	dirtyTokens = true;
  
  	CheckChange();
  }
  
  
  void dlgTextSearchConfiguration::OnRemoveToken(wxCommandEvent &ev)
  {
  	for (int pos = 0 ; pos < lstTokens->GetItemCount() ; pos++)
  	{
  		if (lstTokens->GetText(pos).IsSameAs(cbToken->GetValue(), false))
  		{
  			lstTokens->DeleteItem(pos);
  			break;
  		}
  	}
  
  	cbToken->SetValue(wxT(""));
  	txtDictionary->SetValue(wxT(""));
  
  	btnRemove->Disable();
  
  	dirtyTokens = true;
  
  	CheckChange();
  }
  
  
  wxString dlgTextSearchConfiguration::GetSql()
  {
  	wxString sql;
  	wxString objname;
  
  	if (config)
  	{
  		objname = schema->GetQuotedPrefix() + qtIdent(config->GetName());
  	}
  	else
  	{
  		objname = schema->GetQuotedPrefix() + qtIdent(GetName());
  	}
  
  	if (cbParser->GetValue().Length() > 0)
  	{
  		wxArrayString toks;
  		size_t index;
  
  		if (config)
  		{
  			for (index = 0 ; index < config->GetTokens().GetCount() ; index++)
  				toks.Add(config->GetTokens().Item(index));
  		}
  
  		int cnt = lstTokens->GetItemCount();
  		int pos;
  
  		// check for changed or added tokens
  		for (pos = 0 ; pos < cnt ; pos++)
  		{
  			wxString newTok = lstTokens->GetText(pos);
  			wxString newVal = lstTokens->GetText(pos, 1);
  
  			wxString oldVal;
  
  			for (index = 0 ; index < toks.GetCount() ; index++)
  			{
  				wxString tok = toks.Item(index);
  				if (tok.BeforeFirst('/').IsSameAs(newTok, false))
  				{
  					oldVal = tok.Mid(newTok.Length() + 1);
  					toks.RemoveAt(index);
  					break;
  				}
  			}
  			if (oldVal != newVal)
  			{
  				if (oldVal.Length() == 0)
  				{
  					sql += wxT("ALTER TEXT SEARCH CONFIGURATION ") + objname
  					       +  wxT("\n  ADD MAPPING FOR ") + newTok
  					       +  wxT("\n  WITH ") + newVal
  					       +  wxT(";\n");
  				}
  				else
  				{
  					sql += wxT("ALTER TEXT SEARCH CONFIGURATION ") + objname
  					       +  wxT("\n  ALTER MAPPING FOR ") + newTok
  					       +  wxT("\n  WITH ") + newVal
  					       +  wxT(";\n");
  				}
  			}
  		}
  
  		// check for removed tokens
  		wxString oldTok;
  		for (pos = 0 ; pos < (int)toks.GetCount() ; pos++)
  		{
  			if (!toks.Item(pos).BeforeFirst('/').IsSameAs(oldTok, false))
  			{
  				oldTok = toks.Item(pos).BeforeFirst('/');
  				sql += wxT("ALTER TEXT SEARCH CONFIGURATION ") + objname
  				       +  wxT(" DROP MAPPING FOR ") + oldTok
  				       + wxT(";\n");
  			}
  		}
  	}
  
  	if (config)
  	{
  		// edit mode
  		objname = schema->GetQuotedPrefix() + qtIdent(GetName());
  		AppendNameChange(sql, wxT("TEXT SEARCH CONFIGURATION ") + config->GetQuotedFullIdentifier());
  		AppendOwnerChange(sql, wxT("TEXT SEARCH CONFIGURATION ") + objname);
  		AppendSchemaChange(sql, wxT("TEXT SEARCH CONFIGURATION ") + objname);
  	}
  	else
  	{
  		// create mode
  		objname = qtIdent(cbSchema->GetValue()) + wxT(".") + qtIdent(GetName());
  		sql = wxT("CREATE TEXT SEARCH CONFIGURATION ")
  		      + objname
  		      + wxT(" (");
  
  		AppendIfFilled(sql, wxT("\n   PARSER="), cbParser->GetValue());
  		AppendIfFilled(sql, wxT("\n   COPY="), cbCopy->GetValue());
  
  		sql += wxT("\n);\n");
  
  	}
  
  	AppendComment(sql, wxT("TEXT SEARCH CONFIGURATION ") + qtIdent(cbSchema->GetValue()) + wxT(".") + qtIdent(GetName()), config);
  
  	return sql;
  }
  