From cad7cf8c3f3d8a7623ff5e90e952ddc8934cd3d0 Mon Sep 17 00:00:00 2001
From: Jasmin Dizdarevic <jasmin.dizdarevic@gmail.com>
Date: Sat, 11 Jun 2011 19:05:16 +0200
Subject: [PATCH 1/2] From todo list: Ability to search a database for objects with a specific name

---
 pgadmin/frm/frmMain.cpp               |   38 +++--
 pgadmin/frm/frmSearchObject.cpp       |  382 +++++++++++++++++++++++++++++++++
 pgadmin/include/frm/frmMain.h         |    3 +-
 pgadmin/include/frm/frmSearchObject.h |   66 ++++++
 pgadmin/include/schema/pgDatabase.h   |    9 +
 pgadmin/schema/pgDatabase.cpp         |   25 +++
 pgadmin/ui/frmSearchObject.xrc        |   44 ++++
 7 files changed, 553 insertions(+), 14 deletions(-)
 create mode 100644 pgadmin/frm/frmSearchObject.cpp
 create mode 100644 pgadmin/include/frm/frmSearchObject.h
 create mode 100644 pgadmin/ui/frmSearchObject.xrc

diff --git a/pgadmin/frm/frmMain.cpp b/pgadmin/frm/frmMain.cpp
index 0bd7dd7..50580c6 100644
--- a/pgadmin/frm/frmMain.cpp
+++ b/pgadmin/frm/frmMain.cpp
@@ -353,6 +353,7 @@ void frmMain::CreateMenus()
 	new resetTableStatsFactory(menuFactories, editMenu, 0);
 	new resetFunctionStatsFactory(menuFactories, editMenu, 0);
 	new reassignDropOwnedFactory(menuFactories, editMenu, 0);
+	new searchObjectFactory(menuFactories, editMenu, 0);
 	editMenu->AppendSeparator();
 
 	new separatorFactory(menuFactories);
@@ -649,12 +650,13 @@ void frmMain::ExpandChildNodes(wxTreeItemId node, wxArrayString &expandedNodes)
 wxString frmMain::GetNodePath(wxTreeItemId node)
 {
 	wxString path;
-	path = browser->GetItemText(node).BeforeFirst('(');
-
+	path = browser->GetItemText(node).BeforeFirst('(').Trim().Trim(false);
+	
 	wxTreeItemId parent = browser->GetItemParent(node);
 	while (parent.IsOk())
 	{
-		path = browser->GetItemText(parent).BeforeFirst('(') + wxT("/") + path;
+		browser->GetItemData(parent);
+		path = browser->GetItemText(parent).BeforeFirst('(').Trim().Trim(false) + wxT("/") + path;		
 		parent = browser->GetItemParent(parent);
 	}
 
@@ -668,22 +670,32 @@ wxString frmMain::GetCurrentNodePath()
 }
 
 // Attempt to reselect the node with the given path
-bool frmMain::SetCurrentNode(wxTreeItemId node, const wxString &path)
+bool frmMain::SetCurrentNode(wxTreeItemId node, const wxString &origPath)
 {
+	wxString path = origPath.Lower();
 	wxTreeItemIdValue cookie;
 	wxTreeItemId child = browser->GetFirstChild(node, cookie);
-
+	
+		
 	while (child.IsOk())
 	{
-		if (GetNodePath(child) == path)
-		{
-			browser->SelectItem(child, true);
-			return true;
-		}
-		else if (SetCurrentNode(child, path))
-			return true;
+		wxString actNodePath = GetNodePath(child).Lower();
 
-		child = browser->GetNextChild(node, cookie);
+		if(path.StartsWith(actNodePath))
+		{	
+			if(!browser->IsExpanded(child))
+				browser->Expand(child);
+
+			if (actNodePath == path)
+			{
+				browser->SelectItem(child, true);
+				return true;
+			}		
+			else if (SetCurrentNode(child, path))
+				return true;
+		}	
+		child = browser->GetNextChild(node, cookie);			
+		
 	}
 
 	return false;
diff --git a/pgadmin/frm/frmSearchObject.cpp b/pgadmin/frm/frmSearchObject.cpp
new file mode 100644
index 0000000..4240a01
--- /dev/null
+++ b/pgadmin/frm/frmSearchObject.cpp
@@ -0,0 +1,382 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin III - PostgreSQL Tools
+//
+// Copyright (C) 2002 - 2011, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+// frmSearchObject.h - Search dialogue
+//
+//////////////////////////////////////////////////////////////////////////
+
+
+
+// App headers
+#include "pgAdmin3.h"
+
+#include "frm/frmMain.h"
+#include "frm/frmSearchObject.h"
+#include "utils/sysSettings.h"
+#include "utils/misc.h"
+#include "ctl/ctlListView.h"
+
+#define txtPattern        CTRL_TEXT("txtPattern")
+#define cbType          CTRL_COMBOBOX("cbType")
+#define lcResults         CTRL_LISTCTRL("lcResults")
+#define btnSearch             CTRL_BUTTON("btnSearch")
+
+
+
+BEGIN_EVENT_TABLE(frmSearchObject, pgDialog)	
+	EVT_BUTTON(wxID_HELP,                   frmSearchObject::OnHelp)
+	EVT_BUTTON(XRCID("btnSearch"),			frmSearchObject::OnSearch)
+	EVT_BUTTON(wxID_CANCEL,                 frmSearchObject::OnCancel)
+	EVT_TEXT(XRCID("txtPattern"),           frmSearchObject::OnChange)
+	EVT_LIST_ITEM_SELECTED(XRCID("lcResults"), frmSearchObject::OnSelSearchResult)
+
+END_EVENT_TABLE()
+
+frmSearchObject::frmSearchObject(frmMain *p, pgDatabase *db)
+{
+	parent = p;
+	header = wxT("");
+	currentdb = db;
+
+	wxWindowBase::SetFont(settings->GetSystemFont());
+	LoadResource(p, wxT("frmSearchObject"));
+
+	// Icon
+	appearanceFactory->SetIcons(this);
+	RestorePosition();
+	
+	btnSearch->Disable();
+
+	lcResults->InsertColumn(0, _("Type"));
+	lcResults->InsertColumn(1, _("Name"));
+	lcResults->InsertColumn(2, _("Path"));
+
+
+	/* Mapping table between local language and english, because in SQL we're using only english and translate it later 
+	/* to the local language.*/
+
+	aMap[_("All types")] = wxT("All types");
+	aMap[_("Schemas")] = wxT("Schemas");
+	aMap[_("Tables")] = wxT("Tables");
+	aMap[_("Columns")] = wxT("Columns");
+	aMap[_("Triggers")] = wxT("Triggers");
+	aMap[_("Views")] = wxT("Views");
+	aMap[_("Rules")] = wxT("Rules");	
+	aMap[_("Index")] = wxT("Index");
+	aMap[_("Functions")] = wxT("Functions");
+	aMap[_("Aggregates")] = wxT("Aggregates");
+	aMap[_("Trigger Functions")] = wxT("Trigger Functions");
+	aMap[_("Constraints")] = wxT("Constraints");	
+	aMap[_("Sequences")] = wxT("Sequences");
+	aMap[_("Types")] = wxT("Types");
+	aMap[_("Domains")] = wxT("Domains");
+	aMap[_("Languages")] = wxT("Languages");
+	aMap[_("Conversions")] = wxT("Conversions");
+	aMap[_("Casts")] = wxT("Casts");
+	aMap[_("FTS Configurations")] = wxT("FTS Configurations");
+	aMap[_("FTS Dictionarie")] = wxT("FTS Dictionaries");
+	aMap[_("FTS Parsers")] = wxT("FTS Parsers");
+	aMap[_("FTS Templates")] = wxT("FTS Templates");
+	aMap[_("Foreign Data Wrappers")] = wxT("Foreign Data Wrappers");
+	
+	/* Duplicate definition, if somebody knows hwo to sort wxHashTable, please make it better and use the iteration to fill the combobox:
+
+	LngMapping::iterator it;
+	for(it = aMap.begin(); it != aMap.end(); ++it) 
+	{
+		cbType->Append(it->first);
+	}
+	*/
+	
+	cbType->Clear();
+	cbType->Append(_("All types"));
+	cbType->Append(_("Schemas"));
+	cbType->Append(_("Tables"));
+	cbType->Append(_("Columns"));
+	cbType->Append(_("Triggers"));
+	cbType->Append(_("Views"));
+	cbType->Append(_("Rules"));	
+	cbType->Append(_("Indexs"));
+	cbType->Append(_("Functions"));
+	cbType->Append(_("Aggregates"));
+	cbType->Append(_("Trigger Functions"));
+	cbType->Append(_("Constraints"));	
+	cbType->Append(_("Sequences"));
+	cbType->Append(_("Types"));
+	cbType->Append(_("Languages"));
+	cbType->Append(_("Domains"));
+	cbType->Append(_("Conversions"));
+	cbType->Append(_("Casts"));
+	cbType->Append(_("FTS Configurations"));
+	cbType->Append(_("FTS Dictionaries"));
+	cbType->Append(_("FTS Parsers"));
+	cbType->Append(_("FTS Templates"));
+	cbType->Append(_("Foreign Data Wrappers"));
+	cbType->SetSelection(0);
+		
+
+}
+
+
+frmSearchObject::~frmSearchObject()
+{
+	//Deconstructor
+	
+}
+
+void frmSearchObject::OnHelp(wxCommandEvent &ev) {
+	DisplayHelp(wxT("searchobject"), HELP_PGADMIN);
+}
+
+void frmSearchObject::OnSelSearchResult(wxListEvent &ev) 
+{
+	
+	long row_number = ev.GetIndex();
+	
+	if(lcResults->GetItemTextColour(row_number) == wxColour(128,128,128)) 
+	{
+		/* Result type is not enabled in settings, so we don't search for it in the tree */
+		return;
+	}
+		
+	//Taken from: http://wiki.wxwidgets.org/WxListCtrl#Get_the_String_Contents_of_a_.22cell.22_in_a_LC_REPORT_wxListCtrl
+	wxListItem     row_info;  
+	wxString       path;
+ 
+	// Set what row it is (m_itemId is a member of the regular wxListCtrl class)
+	row_info.m_itemId = row_number;
+	// Set what column of that row we want to query for information.
+	row_info.m_col = 2;
+	// Set text mask
+	row_info.m_mask = wxLIST_MASK_TEXT;
+ 
+	// Get the info and store it in row_info variable.   
+	lcResults->GetItem(row_info);
+ 
+	// Extract the text out that cell
+	path = row_info.m_text;
+
+	
+	if(!parent->SetCurrentNode(parent->GetBrowser()->GetRootItem(), path))
+	{
+		wxMessageBox(_("The specified object couldn't be found in the tree"));
+	}
+		
+}
+
+void frmSearchObject::OnChange(wxCommandEvent &ev) {
+	if(txtPattern->GetValue().Length() >= 3) 
+	{
+		btnSearch->Enable();
+	}
+	else 
+	{
+		btnSearch->Disable();
+	}
+}
+
+void frmSearchObject::OnSearch(wxCommandEvent &ev)
+{
+
+	/*
+	Adding objects:
+
+	Create a sql statement which lists all objects of the specified type and add it to the inner statement with an union. 
+	We need three columns: Type, objectname and path.
+	Parts of the path which has to be translated to the local langauge (because of tree path) must begin with a colon.
+	Append the type to the combobox and the mapping table in the constructor. */
+
+	wxString databasePath = parent->GetNodePath(currentdb->GetDatabase()->GetId());	
+	wxString searchSQL = wxT("select * from (  ")
+							wxT("	SELECT  ")
+							wxT("	case   ")
+							wxT("		when c.relkind = 'r' then 'Tables'   ")
+							wxT("		when c.relkind = 'S' then 'Sequences'   ")
+							wxT("		when c.relkind = 'v' then 'Views'   ")
+							wxT("		else 'should not happen'   ")
+							wxT("	end as type, c.relname AS objectname,  ")
+							wxT("	':Schemas/' || n.nspname || '/' ||  ")
+							wxT("	case   ")
+							wxT("		when c.relkind = 'r' then ':Tables'   ")
+							wxT("		when c.relkind = 'S' then ':Sequences'   ")
+							wxT("		when c.relkind = 'v' then ':Views'   ")
+							wxT("		else 'should not happen'   ")
+							wxT("	end || '/' || c.relname AS path  ")
+							wxT("	FROM pg_class c  ")
+							wxT("	LEFT JOIN pg_namespace n ON n.oid = c.relnamespace     ")
+							wxT("	WHERE c.relkind in ('r','S','v')  ")
+							wxT("	union  ")
+							wxT("	SELECT 'Index', i.relname AS objectname, ':Schemas/' || n.nspname ||'/:Tables/'|| c.relname || '/' || i.relname as path  ")
+							wxT("	FROM pg_index x  ")
+							wxT("	JOIN pg_class c ON c.oid = x.indrelid  ")
+							wxT("	JOIN pg_class i ON i.oid = x.indexrelid  ")
+							wxT("	LEFT JOIN pg_namespace n ON n.oid = c.relnamespace  ")
+							wxT("	LEFT JOIN pg_tablespace t ON t.oid = i.reltablespace  ")
+							wxT("	WHERE c.relkind = 'r' AND i.relkind = 'i'  ")
+							wxT("	union  ")
+							wxT("	select case when t.typname = 'Triggers' then 'Trigger Functions' else 'Functions' end as type, p.proname,  ")
+							wxT("	':Schemas/' || n.nspname || '/' || case when t.typname = 'trigger' then ':Trigger Functions' else ':Functions' end || '/' || p.proname ")
+							wxT("	from pg_proc p  ")
+							wxT("	left join pg_namespace n on p.pronamespace = n.oid  ")
+							wxT("	left join pg_type t on p.prorettype = t.oid  ")
+							wxT("	union  ")
+							wxT("	select 'Schemas', nspname, ':Schemas/' || nspname from pg_namespace  ")
+							wxT("	union  ")
+							wxT("	select 'Columns', a.attname,  ")
+							wxT("	':Schemas/' || n.nspname || '/' ||  ")
+							wxT("	case   ")
+							wxT("		when t.relkind = 'r' then ':Tables'   ")
+							wxT("		when t.relkind = 'S' then ':Sequences'   ")
+							wxT("		when t.relkind = 'v' then ':Views'   ")
+							wxT("		else 'should not happen'   ")
+							wxT("	end || '/' || t.relname || '/:Columns/' || a.attname AS path  ")
+							wxT("	from pg_attribute a  ")
+							wxT("	inner join pg_class t on a.attrelid = t.oid and t.relkind in ('r','v')  ")
+							wxT("	left join pg_namespace n on t.relnamespace = n.oid where a.attnum > 0  ")
+							wxT("	union  ")
+							wxT("	select 'Constraints', c.conname, ':Schemas/' || n.nspname||'/:Tables/'||t.relname||'/:Constraints/'||c.conname from pg_constraint c  ")
+							wxT("	left join pg_class t on c.conrelid = t.oid  ")
+							wxT("	left join pg_namespace n on t.relnamespace = n.oid 						 ")
+							wxT("	union  ")
+							wxT("	select 'Rules', r.rulename, ':Schemas/' || n.nspname||'/:Views/'||t.relname||'/:Rules/'|| r.rulename from pg_rewrite r  ")
+							wxT("	left join pg_class t on r.ev_class = t.oid  ")
+							wxT("	left join pg_namespace n on t.relnamespace = n.oid 						 ")
+							wxT("	union  ")
+							wxT("	select 'Triggers', tr.tgname, ':Schemas/' || n.nspname||'/:Tables/'||t.relname || '/:Triggers/' || tr.tgname from pg_trigger tr  ")
+							wxT("	left join pg_class t on tr.tgrelid = t.oid  ")
+							wxT("	left join pg_namespace n on t.relnamespace = n.oid  ")
+							wxT("	union ")
+							wxT("	SELECT 'Types', t.typname, ':Schemas/' || n.nspname || '/:Types' || t.typname ")
+							wxT("	FROM pg_type t ")
+							wxT("	LEFT OUTER JOIN pg_type e ON e.oid=t.typelem ")
+							wxT("	LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c' ")
+							wxT("	LEFT OUTER JOIN pg_namespace n on t.typnamespace = n.oid ")
+							wxT("	WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' 	 ")
+							wxT("	union ")
+							wxT("	SELECT 'Conversions', co.conname, ':Schemas/' || n.nspname || '/:Conversions/' || co.conname ")
+							wxT("	FROM pg_conversion co ")
+							wxT("	JOIN pg_namespace n ON n.oid=co.connamespace ")
+							wxT("	LEFT OUTER JOIN pg_description des ON des.objoid=co.oid AND des.objsubid=0	 ")
+							wxT("	union ")
+							wxT("	SELECT 'Casts', format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod), ':Casts/' || format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod) ")
+							wxT("	FROM pg_cast ca ")
+							wxT("	JOIN pg_type st ON st.oid=castsource ")
+							wxT("	JOIN pg_type tt ON tt.oid=casttarget ")
+							wxT("	union ")
+							wxT("	SELECT 'Languages', lanname, ':Languages/' || lanname ")
+							wxT("	FROM pg_language lan ")
+							wxT("	WHERE lanispl IS TRUE ")
+							wxT("	union ")
+							wxT("	SELECT 'FTS Configurations', cfg.cfgname, ':Schemas/' || n.nspname || '/:FTS Configurations/' || cfg.cfgname ")
+							wxT("	FROM pg_ts_config cfg ")
+							wxT("	left join pg_namespace n on cfg.cfgnamespace = n.oid	 ")
+							wxT("	union ")
+							wxT("	SELECT 'FTS Dictionaries', dict.dictname, ':Schemas/' || ns.nspname || '/:FTS Dictionaries/' || dict.dictname ")
+							wxT("	FROM pg_ts_dict dict ")
+							wxT("	left join pg_namespace ns on dict.dictnamespace = ns.oid ")
+							wxT("	union ")
+							wxT("	SELECT 'FTS Parsers', prs.prsname, ':Schemas/' || ns.nspname || '/:FTS Parsers/' || prs.prsname ")
+							wxT("	FROM pg_ts_parser prs ")
+							wxT("	left join pg_namespace ns on prs.prsnamespace = ns.oid ")
+							wxT("	union ")
+							wxT("	SELECT 'FTS Templates', tmpl.tmplname, ':Schemas/' || ns.nspname || '/:FTS Templates/' || tmpl.tmplname ")
+							wxT("	FROM pg_ts_template tmpl ")
+							wxT("	left join pg_namespace ns on tmpl.tmplnamespace = ns.oid ")
+							wxT("	union ")
+							wxT("	select 'Domains', t.typname, ':Schemas/' || n.nspname || '/:Domains/' || t.typname from pg_type t  ")
+							wxT("	inner join pg_namespace n on t.typnamespace = n.oid ")
+							wxT("	where t.typtype = 'd' ")
+							wxT("	union ")
+							wxT("	select 'Aggregates', pr.proname, ':Schemas/' || ns.nspname || '/:Aggregates/' || pr.proname from pg_catalog.pg_aggregate ag ")
+							wxT("	inner join pg_proc pr on ag.aggfnoid = pr.oid ")
+							wxT("	left join pg_namespace ns on  pr.pronamespace = ns.oid ")
+							wxT("	union ")
+							wxT("	select 'Foreign Data Wrappers', fdwname, ':Foreign Data Wrappers/' || fdwname from pg_foreign_data_wrapper ")
+							wxT(") i ") 
+						wxT("where lower(i.objectname) like '%") + txtPattern->GetValue().Lower() + wxT("%' ");
+						if(cbType->GetValue() != _("All types")) 
+						{
+							searchSQL += wxT("AND i.type = '") + aMap[cbType->GetValue()] + wxT("' ");
+						}
+						searchSQL += wxT("order by 1,2;");
+		
+							
+	
+
+	pgSet *set = currentdb->GetConnection()->ExecuteSet(searchSQL);
+	int i = 0;
+	if(set) 
+	{
+		lcResults->DeleteAllItems();
+		while(!set->Eof()) 
+		{
+			wxListItem item;	
+			item.SetId(i);
+			lcResults->InsertItem(item);
+
+			wxString ItemPath = databasePath + wxT("/") + set->GetVal(wxT("path"));
+			if(ItemPath.Contains(wxT("Schemas/information_schema")))
+			{
+				ItemPath.Replace(wxT("Schemas/information_schema"), wxT("Catalogs/ANSI"));
+			}
+			if(ItemPath.Contains(wxT("Schemas/pg_catalog")))
+			{
+				ItemPath.Replace(wxT("Schemas/pg_catalog"), wxT("Catalogs/PostgreSQL"));
+			}
+
+			wxString locTypeStr = wxGetTranslation(set->GetVal(wxT("type")));
+
+			if(!settings->GetDisplayOption(locTypeStr)) 
+			{				
+				lcResults->SetItemTextColour(i, wxColour(128,128,128));
+			}
+
+			lcResults->SetItem(i, 0, locTypeStr);
+			lcResults->SetItem(i, 1, set->GetVal(wxT("objectname")));
+			lcResults->SetItem(i, 2, TranslatePath(ItemPath));
+			set->MoveNext();
+			i++;
+		}
+		delete set;
+	}
+	lcResults->SetColumnWidth(0, wxLIST_AUTOSIZE);
+	lcResults->SetColumnWidth(1, wxLIST_AUTOSIZE);
+	lcResults->SetColumnWidth(2, wxLIST_AUTOSIZE);
+
+	
+
+}
+
+wxString frmSearchObject::TranslatePath(wxString &path) 
+{
+	
+	wxStringTokenizer tkz(path, wxT("/"));
+	wxString newPath;
+	while(tkz.HasMoreTokens()) 
+	{
+		wxString token = tkz.GetNextToken();
+		if(token.StartsWith(wxT(":"))) 
+		{
+			token = wxGetTranslation(token.AfterFirst(':'));
+		}
+		newPath = newPath + token.Trim() + wxT("/"); 
+	}
+	return newPath.BeforeLast('/');
+
+}
+
+void frmSearchObject::OnCancel(wxCommandEvent &ev)
+{
+	if (IsModal())
+		EndModal(wxID_CANCEL);
+	else
+		Destroy();
+}
+
+
+
+
diff --git a/pgadmin/include/frm/frmMain.h b/pgadmin/include/frm/frmMain.h
index 455f253..3783b9c 100644
--- a/pgadmin/include/frm/frmMain.h
+++ b/pgadmin/include/frm/frmMain.h
@@ -154,6 +154,7 @@ public:
 	void UpdateAllMacrosList();
 
 	void SetItemBackgroundColour(wxTreeItemId item, wxColour colour);
+	wxString GetNodePath(wxTreeItemId node);
 
 private:
 	wxAuiManager manager;
@@ -233,7 +234,7 @@ private:
 
 	void GetExpandedChildNodes(wxTreeItemId node, wxArrayString &expandedNodes);
 	void ExpandChildNodes(wxTreeItemId node, wxArrayString &expandedNodes);
-	wxString GetNodePath(wxTreeItemId node);
+	
 
 	void PopulatePluginButtonMenu(wxCommandEvent &event);
 
diff --git a/pgadmin/include/frm/frmSearchObject.h b/pgadmin/include/frm/frmSearchObject.h
new file mode 100644
index 0000000..51b4b9e
--- /dev/null
+++ b/pgadmin/include/frm/frmSearchObject.h
@@ -0,0 +1,66 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin III - PostgreSQL Tools
+//
+// Copyright (C) 2002 - 2011, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+// frmSearchObject.h - Search dialogue
+//
+//////////////////////////////////////////////////////////////////////////
+
+#ifndef FRMSEARCHOBJECT_H
+#define FRMSEARCHOBJECT_H
+
+#include "dlg/dlgClasses.h"
+#include "ctl/ctlListView.h"
+#include "schema/pgDatabase.h"
+#include "utils/sysSettings.h"
+
+// Class declarations
+class frmSearchObject : public pgDialog
+{
+public:
+	frmSearchObject(frmMain *p, pgDatabase *db);
+	~frmSearchObject();
+	
+private:
+	void OnHelp(wxCommandEvent &ev);
+	void OnSearch(wxCommandEvent &ev);
+	void OnCancel(wxCommandEvent &ev);
+	void OnChange(wxCommandEvent &ev);
+	void OnSelSearchResult(wxListEvent &ev); 
+	wxString TranslatePath(wxString &path);
+	WX_DECLARE_STRING_HASH_MAP(wxString, LngMapping);
+	LngMapping aMap;
+	
+	pgDatabase *currentdb;
+	frmMain *parent;
+	wxString header;
+	wxArrayString sectionName, sectionData, sectionTableHeader, sectionTableRows, sectionTableInfo, sectionSql;
+
+	DECLARE_EVENT_TABLE()
+};
+
+///////////////////////////////////////////////////////
+// Search Object Factory base class
+///////////////////////////////////////////////////////
+class searchObjectBaseFactory : public actionFactory
+{
+private:
+	searchObjectBaseFactory(menuFactoryList *list) : actionFactory(list) {}	
+	frmMain *GetFrmMain()
+	{
+		return parent;
+	};
+	
+	frmMain *parent;
+public:
+	bool CheckEnable(pgObject *obj)
+	{
+		return false;
+	};
+};
+
+
+#endif
diff --git a/pgadmin/include/schema/pgDatabase.h b/pgadmin/include/schema/pgDatabase.h
index bef2e3b..90e819d 100644
--- a/pgadmin/include/schema/pgDatabase.h
+++ b/pgadmin/include/schema/pgDatabase.h
@@ -351,6 +351,15 @@ public:
 	bool CanCreate();
 };
 
+class searchObjectFactory : public contextActionFactory
+{
+public:
+	searchObjectFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar);
+	wxWindow *StartDialog(frmMain *form, pgObject *obj);
+	bool CheckEnable(pgObject *obj);
+
+};
+
 
 
 #endif
diff --git a/pgadmin/schema/pgDatabase.cpp b/pgadmin/schema/pgDatabase.cpp
index 3657274..51dd300 100644
--- a/pgadmin/schema/pgDatabase.cpp
+++ b/pgadmin/schema/pgDatabase.cpp
@@ -26,6 +26,7 @@
 #include "slony/slCluster.h"
 #include "frm/frmHint.h"
 #include "frm/frmReport.h"
+#include "frm/frmSearchObject.h"
 
 pgDatabase::pgDatabase(const wxString &newName)
 	: pgServerObject(databaseFactory, newName)
@@ -1242,3 +1243,27 @@ bool disconnectDatabaseFactory::CheckEnable(pgObject *obj)
 
 	return false;
 }
+
+searchObjectFactory::searchObjectFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
+{
+	mnu->Append(id, _("Search Object"), _("Search database for specific object"));
+}
+
+
+wxWindow *searchObjectFactory::StartDialog(frmMain *form, pgObject *obj)
+{
+	
+	frmSearchObject *so = new frmSearchObject(form, (pgDatabase *) obj);
+	so->ShowModal();
+
+	
+
+	return 0;
+}
+bool searchObjectFactory::CheckEnable(pgObject *obj) 
+{
+	if (obj && obj->IsCreatedBy(databaseFactory))
+		return ((pgDatabase *)obj)->GetConnected() && (((pgDatabase *)obj)->GetName() != ((pgDatabase *)obj)->GetServer()->GetDatabaseName());
+
+	return false;
+}
diff --git a/pgadmin/ui/frmSearchObject.xrc b/pgadmin/ui/frmSearchObject.xrc
new file mode 100644
index 0000000..13b7909
--- /dev/null
+++ b/pgadmin/ui/frmSearchObject.xrc
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<resource>
+  <object class="wxDialog" name="frmSearchObject">
+    <title>Search Objects</title>
+    <size>353,40d</size>
+    <style>wxDEFAULT_DIALOG_STYLE</style>
+    <object class="wxStaticText" name="stTitle">
+      <label>Search pattern</label>
+      <pos>5,5d</pos>
+    </object>
+    <object class="wxTextCtrl" name="txtPattern">
+      <pos>5,15d</pos>
+      <size>290,-1d</size>
+      <tooltip>Enter part of the object's name you're looking for</tooltip>
+    </object>        
+    <object class="wxButton" name="btnSearch">
+      <label>Find</label>
+      <default>1</default>
+      <pos>300,14d</pos>      
+      <style></style>
+    </object>
+    <object class="wxComboBox" name="cbType">
+      <pos>5,30d</pos>
+      <size>196,-1d</size>
+      <content/>
+      <style>wxCB_READONLY|wxCB_DROPDOWN</style>
+    </object>
+    <object class="wxListCtrl" name="lcResults">
+      <pos>10,80</pos>
+      <size>340,150d</size>
+      <style>wxLC_REPORT</style>
+    </object>
+    <object class="wxButton" name="wxID_HELP">
+      <label>&amp;Help</label>
+      <pos>2,200d</pos>
+      <style></style>
+    </object>    
+    <object class="wxButton" name="wxID_CANCEL">
+      <label>&amp;Close</label>
+      <default>0</default>
+      <pos>300,200d</pos>
+    </object>
+  </object>
+</resource>
\ No newline at end of file
-- 
1.7.3.1.msysgit.0
