1 //////////////////////////////////////////////////////////////////////////
2 //
3 // pgAdmin III - PostgreSQL Tools
4 //
5 // Copyright (C) 2002 - 2016, The pgAdmin Development Team
6 // This software is released under the PostgreSQL Licence
7 //
8 // frmMainConfig.cpp - Backend configuration tool
9 //
10 //////////////////////////////////////////////////////////////////////////
11 
12 #include "pgAdmin3.h"
13 
14 #ifdef __WXMSW__
15 #include <io.h>
16 #include <fcntl.h>
17 #endif
18 
19 #include <wx/imaglist.h>
20 
21 #include "ctl/ctlMenuToolbar.h"
22 #include "frm/frmMainConfig.h"
23 #include "frm/frmMain.h"
24 #include "dlg/dlgMainConfig.h"
25 #include "utils/utffile.h"
26 #include "schema/pgServer.h"
27 #include "frm/menu.h"
28 #include "utils/pgfeatures.h"
29 
30 #include <wx/arrimpl.cpp>
31 WX_DEFINE_OBJARRAY(pgConfigOrgLineArray);
32 
33 #define CTL_CFGVIEW 345
34 
35 
BEGIN_EVENT_TABLE(frmMainConfig,frmConfig)36 BEGIN_EVENT_TABLE(frmMainConfig, frmConfig)
37 	EVT_MENU(MNU_UNDO,                      frmMainConfig::OnUndo)
38 	EVT_MENU(MNU_CONTENTS,                  frmMainConfig::OnContents)
39 	EVT_LIST_ITEM_ACTIVATED(CTL_CFGVIEW,    frmMainConfig::OnEditSetting)
40 	EVT_LIST_ITEM_SELECTED(CTL_CFGVIEW,     frmMainConfig::OnSelectSetting)
41 END_EVENT_TABLE()
42 
43 
44 #define BCE_TITLE _("Backend Configuration Editor")
45 
46 
47 frmMainConfig::frmMainConfig(frmMain *parent, pgServer *server)
48 	: frmConfig(parent, BCE_TITLE, 0)
49 {
50 	wxString applicationname = appearanceFactory->GetLongAppName() + _(" - Configuration Editor");
51 	if (server)
52 	{
53 		conn = server->CreateConn(wxEmptyString, 0, applicationname);
54 		serverVersionNumber = server->GetVersionNumber();
55 	}
56 	else
57 		serverVersionNumber = wxEmptyString;
58 
59 	InitForm();
60 	Init();
61 
62 	if (conn)
63 	{
64 		if (serverFileName.IsEmpty())
65 			serverFileName = wxT("postgresql.conf");
66 
67 
68 		wxString txt;
69 		txt.Printf(_(" - %s on %s (%s:%d)"),
70 		           serverFileName.c_str(), server->GetDescription().c_str(),
71 		           server->GetName().c_str(), server->GetPort());
72 		SetTitle(BCE_TITLE + txt);
73 
74 		wxString str;
75 		str = conn->ExecuteScalar(wxT("SELECT pg_file_read('") + serverFileName + wxT("', 0, ")
76 		                          wxT("pg_file_length('") + serverFileName + wxT("'))"));
77 
78 		DisplayFile(str);
79 
80 		statusBar->SetStatusText(wxString::Format(_(" Configuration read from %s"), conn->GetHost().c_str()));
81 	}
82 }
83 
84 
frmMainConfig(const wxString & title,const wxString & configFile)85 frmMainConfig::frmMainConfig(const wxString &title, const wxString &configFile)
86 	: frmConfig(title + wxT(" - ") + _("Backend Configuration Editor"), configFile)
87 {
88 	InitForm();
89 	Init();
90 	OpenLastFile();
91 }
92 
93 
~frmMainConfig()94 frmMainConfig::~frmMainConfig()
95 {
96 	options.clear();
97 	categories.clear();
98 	lines.Clear();
99 }
100 
101 
Init(pgSettingReader * reader)102 void frmMainConfig::Init(pgSettingReader *reader)
103 {
104 	pgSettingItem *item;
105 	do
106 	{
107 		item = reader->GetNextItem();
108 		if (item)
109 		{
110 			if (item->context == pgSettingItem::PGC_INTERNAL)
111 				delete item;
112 			else
113 			{
114 				if (options[item->name])
115 					delete item;
116 				else
117 				{
118 					options[item->name] = item;
119 
120 					wxArrayString *category = categories[item->category];
121 					if (!category)
122 					{
123 						category = new wxArrayString;
124 						categories[item->category] = category;
125 					}
126 					category->Add(item->name);
127 				}
128 			}
129 		}
130 	}
131 	while (item);
132 
133 	editMenu->Enable(MNU_DELETE, false);
134 	toolBar->EnableTool(MNU_DELETE, false);
135 }
136 
137 
Init()138 void frmMainConfig::Init()
139 {
140 	// Cleanup first, in case we're re-initing
141 	options.clear();
142 	categories.clear();
143 	lines.Clear();
144 
145 	pgSettingReader *reader;
146 	if (conn)
147 	{
148 		// read settings from server
149 		reader = new pgSettingDbReader(conn);
150 	}
151 	else
152 	{
153 		// read settings from file. First, use localized file...
154 		reader = new pgSettingFileReader(true);
155 
156 		if (reader->IsValid())
157 			Init(reader);
158 		delete reader;
159 
160 		// ... then add default file
161 		reader = new pgSettingFileReader(false);
162 	}
163 
164 	if (reader->IsValid())
165 		Init(reader);
166 
167 	delete reader;
168 }
169 
InitForm()170 void frmMainConfig::InitForm()
171 {
172 	appearanceFactory->SetIcons(this);
173 
174 	InitFrame(wxT("frmMainConfig"));
175 	RestorePosition(50, 50, 600, 600, 300, 200);
176 
177 	cfgList = new ctlListView(this, CTL_CFGVIEW, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER);
178 
179 	cfgList->SetImageList(configImageList, wxIMAGE_LIST_SMALL);
180 
181 	cfgList->AddColumn(_("Setting name"), 120);
182 	cfgList->AddColumn(_("Value"), 80);
183 	if (conn)
184 		cfgList->AddColumn(_("Current value"), 80);
185 	cfgList->AddColumn(_("Comment"), 400);
186 }
187 
OnSelectSetting(wxListEvent & event)188 void frmMainConfig::OnSelectSetting(wxListEvent &event)
189 {
190 
191 	// Disable undo because we don't want to undo the wrong line.
192 	editMenu->Enable(MNU_UNDO, false);
193 	toolBar->EnableTool(MNU_UNDO, false);
194 }
195 
OnEditSetting(wxListEvent & event)196 void frmMainConfig::OnEditSetting(wxListEvent &event)
197 {
198 	wxString name = cfgList->GetText(event.GetIndex());
199 	if (!name.IsEmpty())
200 	{
201 		pgSettingItem *item = options[name];
202 		wxASSERT(item);
203 		dlgMainConfig dlg(this, item);
204 		dlg.Go();
205 
206 		if (item->orgLine && !item->newLine->Differs(item->orgLine))
207 		{
208 			delete item->newLine;
209 			item->newLine = 0;
210 		}
211 		else
212 		{
213 			changed = true;
214 			fileMenu->Enable(MNU_SAVE, true);
215 			editMenu->Enable(MNU_UNDO, true);
216 			toolBar->EnableTool(MNU_SAVE, true);
217 			toolBar->EnableTool(MNU_UNDO, true);
218 
219 
220 		}
221 		UpdateLine(event.GetIndex());
222 	}
223 }
224 
225 
OnContents(wxCommandEvent & event)226 void frmMainConfig::OnContents(wxCommandEvent &event)
227 {
228 	DisplayHelp(wxT("index"), HELP_PGADMIN);
229 }
230 
231 
GetHelpPage() const232 wxString frmMainConfig::GetHelpPage() const
233 {
234 	wxString page;
235 	if (page.IsEmpty())
236 		page = wxT("runtime-config");
237 
238 	return page;
239 }
240 
241 
OnUndo(wxCommandEvent & ev)242 void frmMainConfig::OnUndo(wxCommandEvent &ev)
243 {
244 	wxString name = cfgList->GetText(cfgList->GetSelection());
245 	if (!name.IsEmpty())
246 	{
247 		pgSettingItem *item = options[name];
248 		if (item->newLine)
249 		{
250 			delete item->newLine;
251 			item->newLine = 0;
252 			UpdateLine(cfgList->GetSelection());
253 		}
254 	}
255 }
256 
257 
258 
UpdateLine(int pos)259 void frmMainConfig::UpdateLine(int pos)
260 {
261 	wxString name = cfgList->GetText(pos);
262 	if (!name.IsEmpty())
263 	{
264 		pgSettingItem *item = options[name];
265 
266 		pgConfigLine *line = item->newLine;
267 		if (!line)
268 			line = item->orgLine;
269 
270 		wxString value, comment;
271 		int imgIndex = 0;
272 		if (line)
273 		{
274 			value = line->value;
275 			comment = line->comment;
276 			if (!line->isComment)
277 				imgIndex = 1;
278 		}
279 		cfgList->SetItem(pos, 1, value);
280 
281 		if (conn)
282 			cfgList->SetItem(pos, 3, comment);
283 		else
284 			cfgList->SetItem(pos, 2, comment);
285 
286 		cfgList->SetItemImage(pos, imgIndex, imgIndex);
287 	}
288 }
289 
290 
WriteFile(pgConn * conn)291 void frmMainConfig::WriteFile(pgConn *conn)
292 {
293 
294 	size_t i;
295 
296 	wxString str;
297 	for (i = 0 ; i < lines.GetCount() ; i++)
298 		str.Append(lines.Item(i).GetNewText() + wxT("\n"));
299 
300 	for (i = 0 ; i < (size_t)cfgList->GetItemCount() ; i++)
301 	{
302 		pgSettingItem *item = options[cfgList->GetText(i)];
303 
304 		if (item && item->newLine && item->newLine->item && !item->orgLine)
305 			str.Append(item->newLine->GetNewText() + wxT("\n"));
306 	}
307 
308 
309 	if (DoWriteFile(str, conn))
310 	{
311 		changed = false;
312 		fileMenu->Enable(MNU_SAVE, false);
313 		editMenu->Enable(MNU_UNDO, false);
314 		toolBar->EnableTool(MNU_SAVE, false);
315 		toolBar->EnableTool(MNU_UNDO, false);
316 
317 		size_t i;
318 		for (i = 0 ; i < (size_t)cfgList->GetItemCount() ; i++)
319 		{
320 			pgSettingItem *item = options[cfgList->GetText(i)];
321 
322 			if (item && item->newLine)
323 			{
324 				if (!item->orgLine)
325 				{
326 					item->orgLine = new pgConfigOrgLine(item->newLine);
327 					lines.Add(item->orgLine);
328 					item->orgLine->item = item;
329 				}
330 				else
331 				{
332 					item->orgLine->comment = item->newLine->comment;
333 					item->orgLine->isComment = item->newLine->isComment;
334 					item->orgLine->value = item->newLine->value;
335 					item->orgLine->text = item->orgLine->GetNewText();
336 				}
337 			}
338 		}
339 
340 
341 		for (i = 0 ; i < lines.GetCount() ; i++)
342 		{
343 			pgConfigOrgLine &line = lines.Item(i);
344 			if (line.item && line.item->newLine)
345 			{
346 				line.text = line.GetNewText();
347 				delete line.item->newLine;
348 				line.item->newLine = 0;
349 			}
350 		}
351 	}
352 }
353 
354 
355 
DisplayFile(const wxString & str)356 void frmMainConfig::DisplayFile(const wxString &str)
357 {
358 	Init();
359 
360 	filetype = wxTextFileType_Unix;
361 	wxStringTokenizer strtok;
362 	wxArrayString *unknownCategory = 0;
363 
364 	if (str.Find('\r') >= 0)
365 	{
366 		if (str.Find(wxT("\n\r")) >= 0 || str.Find(wxT("\r\n")))
367 			filetype = wxTextFileType_Dos;
368 		else
369 			filetype = wxTextFileType_Mac;
370 
371 		strtok.SetString(wxTextBuffer::Translate(str, wxTextFileType_Unix), wxT("\n"), wxTOKEN_RET_EMPTY);
372 	}
373 	else
374 		strtok.SetString(str, wxT("\n"), wxTOKEN_RET_EMPTY);
375 
376 	while (strtok.HasMoreTokens())
377 	{
378 		pgConfigOrgLine *line = new pgConfigOrgLine(strtok.GetNextToken());
379 		lines.Add(line);
380 
381 		// identify option
382 		bool isComment = false;
383 		const wxChar *p = line->text.c_str();
384 
385 		// identify keywords
386 		while (*p && wxStrchr(wxT("# \n"), *p))
387 		{
388 			if (*p == '#')
389 				isComment = true;
390 			p++;
391 		}
392 
393 		if (!*p)
394 			isComment = true;
395 
396 		line->isComment = isComment;
397 
398 		const wxChar *p2 = p;
399 		while (*p2 && *p2 != '#' && *p2 != ' ' && *p2 != '\t' && *p2 != '=')
400 			p2++;
401 
402 		if (p2 != p)
403 		{
404 			wxString keyword = line->text.Mid(p - line->text.c_str(), p2 - p);
405 
406 			pgSettingItemHashmap::iterator it = options.find(keyword);
407 
408 			pgSettingItem *item;
409 			if (it == options.end())
410 			{
411 				if (isComment)
412 					continue;
413 
414 				item = new pgSettingItem;
415 				item->name = keyword;
416 				item->category = _("Unknown");
417 				item->short_desc = _("Unknown option");
418 				item->extra_desc = _("This option is present in the configuration file, but not known to the configuration tool.");
419 				item->SetType(wxT("string"));
420 
421 				options[item->name] = item;
422 
423 				if (!unknownCategory)
424 				{
425 					unknownCategory = new wxArrayString;
426 					categories[item->category] = unknownCategory;
427 				}
428 				unknownCategory->Add(item->name);
429 			}
430 			else
431 				item = it->second;
432 
433 
434 			if (!isComment || !item->orgLine || item->orgLine->isComment)
435 			{
436 				line->item = item;
437 				if (item->orgLine)
438 					item->orgLine->item = 0;
439 				item->orgLine = line;
440 			}
441 			while (*p2 && *p2 != '=')
442 				p2++;
443 
444 			p2++;   // skip =
445 			while (*p2 && wxStrchr(wxT(" \t"), *p2))
446 				p2++;
447 
448 			wxChar quoteChar = 0;
449 			if (wxStrchr(wxT("'\""), *p2))
450 				quoteChar = *p2++;
451 
452 			const wxChar *p3 = p2;
453 			while (*p3)
454 			{
455 				if (*p3 == quoteChar || (!quoteChar && wxStrchr(wxT(" \t#"), *p3)))
456 					break;
457 				p3++;
458 			}
459 			if (p2 != p3)
460 			{
461 				line->value = line->text.Mid(p2 - line->text.c_str(), p3 - p2);
462 				if (quoteChar)
463 					p3++;
464 
465 				const wxChar *p4 = p3;
466 				while (*p4 && wxStrchr(wxT(" \t#"), *p4))
467 					p4++;
468 
469 				line->commentIndent = line->text.Mid(p3 - line->text.c_str(), p4 - p3);
470 				line->comment = p4;
471 			}
472 		}
473 	}
474 
475 	cfgList->DeleteAllItems();
476 
477 	double versionNum;
478 	bool versionRetVal = serverVersionNumber.ToDouble(&versionNum);
479 
480 	// we want to show options ordered by category/name
481 	// category might be localized, and we want a distinct category ordering
482 
483 	FillList(wxT("listen_addresses"));          // Connections and Authentication / Connection Settings
484 	FillList(wxT("authentication_timeout"));    // Connections and Authentication / Security and Authentication
485 	FillList(wxT("check_function_bodies"));     // Client Connection Defaults / Statement Behaviour
486 	FillList(wxT("lc_messages"));               // Client Connection Defaults / Locale and Formatting
487 	if (versionRetVal && versionNum < 8.4)
488 		FillList(wxT("explain_pretty_print"));      // Client Connection Defaults / Other Defaults
489 	FillList(wxT("enable_hashjoin"));           // Query Tuning / Planner Method Configuration
490 	FillList(wxT("cpu_operator_cost"));         // Query Tuning / Planner Cost Constants
491 	if (!conn || !conn->GetIsGreenplum())       // Greenplum doesn't have the Genetic Query Optimizer
492 		FillList(wxT("geqo"));                  // Query Tuning / Genetic Query Optimizer
493 	FillList(wxT("default_statistics_target")); // Query Tuning / Other Planner Options
494 	FillList(wxT("deadlock_timeout"));          // Lock Management
495 	FillList(wxT("shared_buffers"));            // Resource Usage / Memory
496 	if (versionRetVal && versionNum < 8.4)
497 		FillList(wxT("max_fsm_pages"));             // Resource Usage / Free Space Map
498 	FillList(wxT("bgwriter_delay"));            // Resource Usage
499 	FillList(wxT("max_files_per_process"));     // Resource Usage / Kernel Resources
500 	FillList(wxT("log_connections"));           // Reporting and Logging / What to Log
501 	FillList(wxT("client_min_messages"));       // Reporting and Logging / When to Log
502 	FillList(wxT("log_destination"));           // Reporting and Logging / Where to Log
503 	FillList(wxT("stats_command_string"), wxT("track_activities"));      // Statistics / Query and Index Statistics Collector
504 	FillList(wxT("log_executor_stats"));        // Statistics / Monitoring
505 	FillList(wxT("fsync"));                     // Write-Ahead Log / Settings
506 	FillList(wxT("checkpoint_segments"));       // Write-Ahead Log / Checkpoints
507 	if (versionRetVal && versionNum <= 8.4)
508 		FillList(wxT("add_missing_from"));          // Version and Platform Compatibility / Previous PostgreSQL Version
509 	FillList(wxT("transform_null_equals"));     // Version and Platform Compatibility / Other Platforms and Clients
510 	if (!conn || !conn->GetIsGreenplum())       // Greenplum doesn't have trace_notify visible
511 		FillList(wxT("trace_notify"));          // Developer Options
512 	FillList(wxT("hba_file"));                  // Ungrouped
513 
514 
515 	// for all we didn't get
516 	while (!categories.empty())
517 	{
518 		pgCategoryHashmap::iterator it = categories.begin();
519 		wxString missed = it->first;
520 		FillList(it->second);
521 		categories.erase(it);
522 	}
523 }
524 
525 
FillList(const wxString & categoryMember,const wxString & altCategoryMember)526 void frmMainConfig::FillList(const wxString &categoryMember, const wxString &altCategoryMember)
527 {
528 	pgSettingItem *categoryItem = options[categoryMember];
529 
530 	if (!categoryItem && !altCategoryMember.IsEmpty())
531 		categoryItem = options[altCategoryMember];
532 
533 	wxASSERT_MSG(categoryItem, wxString::Format(wxT("%s/%s"), categoryMember.c_str(), altCategoryMember.c_str()));
534 
535 	if (categoryItem)
536 	{
537 		FillList(categories[categoryItem->category]);
538 		categories.erase(categoryItem->category);
539 	}
540 }
541 
542 
FillList(wxArrayString * category)543 void frmMainConfig::FillList(wxArrayString *category)
544 {
545 	if (category)
546 	{
547 		size_t i;
548 		for (i = 0 ; i < category->GetCount() ; i++)
549 		{
550 			pgSettingItem *item = options[category->Item(i)];
551 			wxASSERT(item);
552 
553 			wxString value;
554 			wxString comment;
555 			int imgIndex = 0;
556 			if (item->orgLine)
557 			{
558 				if (!item->orgLine->isComment)
559 					imgIndex = 1;
560 				value = item->orgLine->value;
561 				comment = item->orgLine->comment;
562 				comment.Replace(wxT("\t"), wxT(" "));
563 			}
564 			long pos = cfgList->AppendItem(imgIndex, item->name, value);
565 			if (conn)
566 			{
567 				cfgList->SetItem(pos, 2, item->value);
568 				cfgList->SetItem(pos, 3, comment);
569 			}
570 			else
571 				cfgList->SetItem(pos, 2, comment);
572 		}
573 		delete category;
574 	}
575 }
576 
577 
578 
579 enum
580 {
581 	HINT_LISTEN_ADDRESSES,
582 	HINT_AUTOVACUUM_CFG
583 };
584 
585 
586 const wxChar *hintString[] =
587 {
588 	_("The PostgreSQL server engine is currently configured to listen for local connections only.\nYou might want to check \"listen_addresses\" to enable accessing the server over the network too."),
589 	_("The autovacuum backend process is not running.\nIt is recommended to enable it by setting 'track_counts' and 'autovacuum' to 'on' in PostgreSQL 8.3 and above or 'stats_start_collector', 'stats_row_level' and 'autovacuum' to 'on' in earlier versions.")
590 };
591 
592 
GetHintString()593 wxString frmMainConfig::GetHintString()
594 {
595 	wxArrayInt hints;
596 	size_t i;
597 	int autovacuum = 0;
598 	bool autovacuumPresent = false;
599 
600 	for (i = 0 ; i < (size_t)cfgList->GetItemCount() ; i++)
601 	{
602 		pgSettingItem *item = options[cfgList->GetText(i)];
603 
604 		if (item)
605 		{
606 			wxString name = item->name;
607 			wxString value = item->GetActiveValue();
608 			if (name == wxT("listen_addresses"))
609 			{
610 				if (value.IsEmpty() || value == wxT("localhost"))
611 					hints.Add(HINT_LISTEN_ADDRESSES);
612 			}
613 			else if (name == wxT("autovacuum"))
614 			{
615 				autovacuumPresent = true;
616 				if (StrToBool(value))
617 					autovacuum++;
618 			}
619 			else if (name == wxT("stats_start_collector") || name == wxT("stats_row_level"))
620 			{
621 				if (StrToBool(value))
622 					autovacuum++;
623 			}
624 			else if (name == wxT("track_counts"))
625 			{
626 				// Double increment, because track_counts in 8.3 is worth both previous options.
627 				if (StrToBool(value))
628 					autovacuum = autovacuum + 2;
629 			}
630 		}
631 	}
632 
633 	if (autovacuumPresent && autovacuum < 3)
634 		hints.Add(HINT_AUTOVACUUM_CFG);
635 
636 	wxString str;
637 	for (i = 0 ; i < hints.GetCount() ; i++)
638 	{
639 		if (i)
640 			str.Append(wxT("\n\n"));
641 		str.Append(hintString[hints.Item(i)]);
642 	}
643 
644 	return str;
645 }
646 
OnOpen(wxCommandEvent & event)647 void frmMainConfig::OnOpen(wxCommandEvent &event)
648 {
649 	if (CheckChanged(true))
650 		return;
651 
652 #ifdef __WXMSW__
653 	wxFileDialog dlg(this, _("Open configuration file"), lastDir, wxT(""),
654 	                 _("Configuration files (*.conf)|*.conf|All files (*.*)|*.*"), wxFD_OPEN);
655 #else
656 	wxFileDialog dlg(this, _("Open configuration file"), lastDir, wxT(""),
657 	                 _("Configuration files (*.conf)|*.conf|All files (*)|*"), wxFD_OPEN);
658 #endif
659 	if (dlg.ShowModal() == wxID_OK)
660 	{
661 		Init();
662 
663 		lastFilename = dlg.GetFilename();
664 		lastDir = dlg.GetDirectory();
665 		lastPath = dlg.GetPath();
666 		OpenLastFile();
667 		UpdateRecentFiles();
668 	}
669 }
670 
mainConfigFactory(menuFactoryList * list,wxMenu * mnu,ctlMenuToolbar * toolbar)671 mainConfigFactory::mainConfigFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : actionFactory(list)
672 {
673 	mnu->Append(id, wxT("postgresql.conf"), _("Edit general server configuration file."));
674 }
675 
676 
StartDialog(frmMain * form,pgObject * obj)677 wxWindow *mainConfigFactory::StartDialog(frmMain *form, pgObject *obj)
678 {
679 	pgServer *server = obj->GetServer();
680 	if (server)
681 	{
682 		frmConfig *frm = new frmMainConfig(form, server);
683 		frm->Go();
684 		return frm;
685 	}
686 	return 0;
687 }
688 
689 
CheckEnable(pgObject * obj)690 bool mainConfigFactory::CheckEnable(pgObject *obj)
691 {
692 	if (obj)
693 	{
694 		pgServer *server = obj->GetServer();
695 		if (server)
696 		{
697 			pgConn *conn = server->GetConnection();
698 			return conn && server->GetSuperUser() &&  conn->HasFeature(FEATURE_FILEREAD);
699 		}
700 	}
701 	return false;
702 }
703 
704 
mainConfigFileFactory(menuFactoryList * list,wxMenu * mnu,ctlMenuToolbar * toolbar)705 mainConfigFileFactory::mainConfigFileFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : actionFactory(list)
706 {
707 	mnu->Append(id, _("Open postgresql.conf..."), _("Open configuration editor with postgresql.conf."));
708 }
709 
710 
StartDialog(frmMain * form,pgObject * obj)711 wxWindow *mainConfigFileFactory::StartDialog(frmMain *form, pgObject *obj)
712 {
713 	frmConfig *dlg = new frmMainConfig(form);
714 	dlg->Go();
715 	dlg->DoOpen();
716 	return dlg;
717 }
718