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 // events.cpp - Event handlers for frmMain
9 //
10 //
11 //////////////////////////////////////////////////////////////////////////
12 
13 #include "pgAdmin3.h"
14 
15 // wxWindows headers
16 #include <wx/wx.h>
17 #include <wx/settings.h>
18 #include <wx/treectrl.h>
19 #include <wx/listctrl.h>
20 #include <wx/imaglist.h>
21 #include <wx/stc/stc.h>
22 #include <wx/busyinfo.h>
23 #include <wx/aui/aui.h>
24 
25 // App headers
26 #include "utils/misc.h"
27 #include "frm/menu.h"
28 #include "frm/frmMain.h"
29 #include "frm/frmOptions.h"
30 #include "ctl/ctlSQLBox.h"
31 #include "ctl/ctlMenuToolbar.h"
32 #include "db/pgConn.h"
33 #include "schema/pgDatabase.h"
34 #include "db/pgSet.h"
35 #include "schema/pgServer.h"
36 #include "schema/pgObject.h"
37 #include "schema/pgCollection.h"
38 #include "schema/pgTable.h"
39 #include "schema/edbPrivateSynonym.h"
40 #include "dlg/dlgProperty.h"
41 
42 // Mutex to protect the "currentObject" from race conditions.
43 //
44 static wxMutex s_currentObjectMutex;
45 
46 // Event table
BEGIN_EVENT_TABLE(frmMain,pgFrame)47 BEGIN_EVENT_TABLE(frmMain, pgFrame)
48 	EVT_CHILD_FOCUS(			frmMain::OnChildFocus)
49 	EVT_ERASE_BACKGROUND(                   frmMain::OnEraseBackground)
50 	EVT_SIZE(                               frmMain::OnSize)
51 	EVT_MENU(MNU_ACTION,                    frmMain::OnAction)
52 
53 	EVT_MENU(MNU_COPY,			frmMain::OnCopy)
54 	EVT_MENU(MNU_DELETE,                    frmMain::OnDelete)
55 	EVT_MENU(MNU_SAVEDEFINITION,            frmMain::OnSaveDefinition)
56 	EVT_MENU(MNU_SQLPANE,                   frmMain::OnToggleSqlPane)
57 	EVT_MENU(MNU_OBJECTBROWSER,             frmMain::OnToggleObjectBrowser)
58 	EVT_MENU(MNU_TOOLBAR,                   frmMain::OnToggleToolBar)
59 	EVT_MENU(MNU_DEFAULTVIEW,               frmMain::OnDefaultView)
60 	EVT_MENU(MNU_CHECKALIVE,                frmMain::OnCheckAlive)
61 	EVT_MENU(MNU_CONTEXTMENU,               frmMain::OnContextMenu)
62 
63 	EVT_AUINOTEBOOK_PAGE_CHANGED(wxID_ANY,	frmMain::OnPageChange)
64 	EVT_LIST_ITEM_SELECTED(CTL_PROPVIEW,    frmMain::OnPropSelChanged)
65 	EVT_LIST_ITEM_ACTIVATED(CTL_PROPVIEW,   frmMain::OnPropSelActivated)
66 	EVT_LIST_ITEM_RIGHT_CLICK(CTL_PROPVIEW, frmMain::OnPropRightClick)
67 	EVT_LIST_ITEM_SELECTED(CTL_STATVIEW,    frmMain::OnSelectItem)
68 	EVT_LIST_ITEM_SELECTED(CTL_DEPVIEW,     frmMain::OnSelectItem)
69 	EVT_LIST_ITEM_SELECTED(CTL_REFVIEW,     frmMain::OnSelectItem)
70 	EVT_TREE_SEL_CHANGED(CTL_BROWSER,       frmMain::OnTreeSelChanged)
71 	EVT_TREE_ITEM_EXPANDING(CTL_BROWSER,    frmMain::OnExpand)
72 	EVT_TREE_ITEM_COLLAPSING(CTL_BROWSER,   frmMain::OnCollapse)
73 	EVT_TREE_ITEM_ACTIVATED(CTL_BROWSER,    frmMain::OnSelActivated)
74 	EVT_TREE_ITEM_RIGHT_CLICK(CTL_BROWSER,  frmMain::OnSelRightClick)
75 	EVT_STC_UPDATEUI(CTL_SQLPANE,           frmMain::OnPositionStc)
76 	EVT_CLOSE(                              frmMain::OnClose)
77 
78 	EVT_AUI_PANE_CLOSE(                     frmMain::OnAuiUpdate)
79 	EVT_AUINOTEBOOK_PAGE_CLOSE(wxID_ANY,    frmMain::OnAuiNotebookPageClose)
80 
81 #ifdef __WXGTK__
82 	EVT_TREE_KEY_DOWN(CTL_BROWSER,          frmMain::OnTreeKeyDown)
83 #endif
84 
85 #if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
86 	EVT_COMMAND (wxID_ANY, SSH_TUNNEL_ERROR_EVENT, frmMain::OnSSHTunnelEvent)
87 #endif
88 
89 END_EVENT_TABLE()
90 
91 void frmMain::OnChildFocus(wxChildFocusEvent &event)
92 {
93 	// Grab the focussed control and stash it away for later use
94 	currentControl = dynamic_cast<wxControl *>(event.GetEventObject());
95 
96 	// Setup the menu controls according to the control that's selected
97 	// and it's status.
98 
99 	// Defaults.
100 	editMenu->Enable(MNU_COPY, false);
101 
102 	// ctlSQLBox?
103 	ctlSQLBox *sb = dynamic_cast<ctlSQLBox *>(event.GetEventObject());
104 	if (sb)
105 	{
106 		// Copy
107 		editMenu->Enable(MNU_COPY, !sb->GetSelectedText().IsEmpty());
108 	}
109 
110 	// Listview?
111 	ctlListView *lv = dynamic_cast<ctlListView *>(event.GetEventObject());
112 	if (lv)
113 	{
114 		// Copy
115 		editMenu->Enable(MNU_COPY, lv->GetSelectedItemCount() > 0);
116 	}
117 }
118 
OnEraseBackground(wxEraseEvent & event)119 void frmMain::OnEraseBackground(wxEraseEvent &event)
120 {
121 	event.Skip();
122 }
123 
OnSize(wxSizeEvent & event)124 void frmMain::OnSize(wxSizeEvent &event)
125 {
126 	event.Skip();
127 }
128 
129 // unfortunately, under GTK we won't get the original wxKeyEvent
130 // to reset m_metaDown
OnTreeKeyDown(wxTreeEvent & event)131 void frmMain::OnTreeKeyDown(wxTreeEvent &event)
132 {
133 	int keyCode = event.GetKeyCode();
134 	switch (keyCode)
135 	{
136 		case WXK_F1:
137 			OnHelp(event);
138 			break;
139 		case WXK_F5:
140 			Refresh(currentObject);
141 			break;
142 		case WXK_DELETE:
143 			OnDelete(event);
144 			break;
145 		// Is tempting to write all cases(this handler) in tree control itself
146 		case WXK_LEFT:
147 		case WXK_RIGHT:
148 			browser->NavigateTree(keyCode);
149 			break;
150 		default:
151 			event.Skip();
152 			break;
153 	}
154 }
155 
OnExit(wxCommandEvent & event)156 void frmMain::OnExit(wxCommandEvent &event)
157 {
158 	Close(false);   // Allow sub windows to stop us
159 	event.Skip();
160 }
161 
162 
163 
OnClose(wxCloseEvent & event)164 void frmMain::OnClose(wxCloseEvent &event)
165 {
166 	wxWindow *fr;
167 	windowList::Node *node;
168 	while ((node = frames.GetFirst()) != NULL)
169 	{
170 		fr = node->GetData();
171 
172 		// if crashes occur here when closing the app,
173 		// some actionFactory::StartDialog returned a wxWindow* (which is registered in frames)
174 		// without code to handle OnClose (esp. removing itself with RemoveFrame)
175 
176 		if (!fr->Close(!event.CanVeto()))
177 		{
178 			if (event.CanVeto())
179 			{
180 				event.Veto();
181 				return;
182 			}
183 		}
184 		delete node;
185 		fr->Destroy();
186 	}
187 	Destroy();
188 }
189 
190 
UpdateAllRecentFiles()191 void frmMain::UpdateAllRecentFiles()
192 {
193 	wxWindow *fr;
194 	windowList::Node *node;
195 	node = frames.GetFirst();
196 	while (node)
197 	{
198 		fr = node->GetData();
199 		((frmQuery *)fr)->UpdateRecentFiles(false);
200 		node = node->GetNext();
201 	}
202 }
203 
204 
UpdateAllFavouritesList()205 void frmMain::UpdateAllFavouritesList()
206 {
207 	wxWindow *fr;
208 	windowList::Node *node;
209 	node = frames.GetFirst();
210 	while (node)
211 	{
212 		fr = node->GetData();
213 		((frmQuery *)fr)->UpdateFavouritesList();
214 		node = node->GetNext();
215 	}
216 }
217 
UpdateAllMacrosList()218 void frmMain::UpdateAllMacrosList()
219 {
220 	wxWindow *fr;
221 	windowList::Node *node;
222 	node = frames.GetFirst();
223 	while (node)
224 	{
225 		fr = node->GetData();
226 		((frmQuery *)fr)->UpdateMacrosList();
227 		node = node->GetNext();
228 	}
229 }
230 
231 
OnAction(wxCommandEvent & ev)232 void frmMain::OnAction(wxCommandEvent &ev)
233 {
234 	actionFactory *af = menuFactories->GetFactory(ev.GetId());
235 	if (af)
236 	{
237 		wxWindow *wnd = af->StartDialog(this, currentObject);
238 		if (wnd)
239 			AddFrame(wnd);
240 	}
241 }
242 
GetHelpPage() const243 wxString frmMain::GetHelpPage() const
244 {
245 	wxString page;
246 
247 	if (currentObject)
248 		page = currentObject->GetHelpPage(true);
249 
250 	if (page.IsEmpty())
251 		page = wxT("pg/sql-commands");
252 
253 	return page;
254 }
255 
OnCollapse(wxTreeEvent & event)256 void frmMain::OnCollapse(wxTreeEvent &event)
257 {
258 #ifdef WIN32
259 	// This is weird stuff, but somewhere comes a collapse after we have done
260 	// connecting the server and expanding the tree.
261 	// Possibly not necessary
262 	if (event.GetItem() == denyCollapseItem)
263 		event.Veto();
264 #endif
265 	denyCollapseItem = wxTreeItemId();
266 }
267 
268 
OnExpand(wxTreeEvent & event)269 void frmMain::OnExpand(wxTreeEvent &event)
270 {
271 	wxCookieType cookie;
272 	wxTreeItemId item = browser->GetFirstChild(event.GetItem(), cookie);
273 	if (item && !browser->GetItemData(item))
274 	{
275 		// the expanding node has a dummy item.
276 		// delete dummy item, and expand kids.
277 		execSelChange(event.GetItem(), browser->GetSelection() == item);
278 
279 		// we don't have any kids, so don't expand
280 		if (!browser->GetChildrenCount(event.GetItem()))
281 			event.Veto();
282 	}
283 }
284 
285 
OnCheckAlive(wxCommandEvent & event)286 void frmMain::OnCheckAlive(wxCommandEvent &event)
287 {
288 	CheckAlive();
289 }
290 
291 
292 
OnPropSelChanged(wxListEvent & event)293 void frmMain::OnPropSelChanged(wxListEvent &event)
294 {
295 	if (properties->GetSelectedItemCount() == 1)
296 	{
297 		wxTreeItemId item = browser->GetSelection();
298 		if (item)
299 		{
300 			pgObject *data = browser->GetObject(item);
301 			if (data && data->IsCollection())
302 			{
303 				currentObject = ((pgCollection *)data)->FindChild(browser, event.GetIndex());
304 				if (currentObject)
305 				{
306 					setDisplay(currentObject);
307 					sqlPane->SetReadOnly(false);
308 					sqlPane->SetText(currentObject->GetSql(browser));
309 					sqlPane->SetReadOnly(true);
310 				}
311 			}
312 		}
313 	}
314 
315 	editMenu->Enable(MNU_COPY, properties->GetSelectedItemCount() > 0);
316 
317 // The generic list view control on the Mac doesn't fire focus events
318 // as it should, so we set currentControl here instead of relying on
319 // the ChildFocusEvent. The native list view does fire the events, but
320 // does weird things with multi-select items so we currently disable
321 // it (see the creation of the listviews in frmMain.cpp).
322 #ifdef __WXMAC__
323 	currentControl = properties;
324 #endif
325 }
326 
327 
OnSelectItem(wxListEvent & event)328 void frmMain::OnSelectItem(wxListEvent &event)
329 {
330 	ctlListView *list;
331 
332 	switch(listViews->GetSelection())
333 	{
334 		case NBP_STATISTICS:
335 			list = statistics;
336 			break;
337 		case NBP_DEPENDENCIES:
338 			list = dependencies;
339 			break;
340 		case NBP_DEPENDENTS:
341 			list = dependents;
342 			break;
343 		default:
344 			// This shouldn't happen.
345 			// If it does, it's no big deal, we just need to get out.
346 			return;
347 			break;
348 	}
349 
350 	editMenu->Enable(MNU_COPY, list->GetSelectedItemCount() > 0);
351 
352 // The generic list view control on the Mac doesn't fire focus events
353 // as it should, so we set currentControl here instead of relying on
354 // the ChildFocusEvent. The native list view does fire the events, but
355 // does weird things with multi-select items so we currently disable
356 // it (see the creation of the listviews in frmMain.cpp).
357 #ifdef __WXMAC__
358 	currentControl = list;
359 #endif
360 }
361 
OnPropSelActivated(wxListEvent & event)362 void frmMain::OnPropSelActivated(wxListEvent &event)
363 {
364 	if (propFactory->CheckEnable(currentObject))
365 		propFactory->StartDialog(this, currentObject);
366 }
367 
368 
OnPropRightClick(wxListEvent & event)369 void frmMain::OnPropRightClick(wxListEvent &event)
370 {
371 	OnPropSelChanged(event);
372 
373 	if (currentObject)
374 		doPopup(properties, event.GetPoint(), currentObject);
375 }
376 
377 
OnTreeSelChanged(wxTreeEvent & event)378 void frmMain::OnTreeSelChanged(wxTreeEvent &event)
379 {
380 	/*
381 	    * Do not honour the tree selection change, while a property dialog is
382 	    * closed and refresh is in progress
383 	*/
384 	if (m_refreshing)
385 		return;
386 
387 	denyCollapseItem = wxTreeItemId();
388 	// Reset the listviews/SQL pane
389 	if (event.GetItem())
390 		execSelChange(event.GetItem(), true);
391 }
392 
393 
394 // Reset the list controls
ResetLists()395 void frmMain::ResetLists()
396 {
397 	properties->ClearAll();
398 	properties->AddColumn(_("Properties"), properties->GetSize().GetWidth() - 10);
399 	properties->InsertItem(0, _("No properties are available for the current selection"), PGICON_PROPERTY);
400 	statistics->ClearAll();
401 	statistics->AddColumn(_("Statistics"), properties->GetSize().GetWidth() - 10);
402 	statistics->InsertItem(0, _("No statistics are available for the current selection"), PGICON_PROPERTY);
403 	dependencies->ClearAll();
404 	dependencies->AddColumn(_("Dependencies"), properties->GetSize().GetWidth() - 10);
405 	dependencies->InsertItem(0, _("No dependency information is available for the current selection"), PGICON_PROPERTY);
406 	dependents->ClearAll();
407 	dependents->AddColumn(_("Dependents"), properties->GetSize().GetWidth() - 10);
408 	dependents->InsertItem(0, _("No dependent information is available for the current selection"), PGICON_PROPERTY);
409 }
410 
411 
execSelChange(wxTreeItemId item,bool currentNode)412 void frmMain::execSelChange(wxTreeItemId item, bool currentNode)
413 {
414 	static bool refresh = true;
415 
416 	if (currentNode)
417 	{
418 		ResetLists();
419 		sqlPane->Clear();
420 	}
421 
422 	// Get the item data, and feed it to the relevant handler,
423 	// cast as required.
424 	//
425 	// Lock the assignment to prevent the race conditions between onSelRightClick and execSelChange.
426 	//
427 	s_currentObjectMutex.Lock();
428 	currentObject = browser->GetObject(item);
429 	s_currentObjectMutex.Unlock();
430 
431 	// If we didn't get an object, then we may have a right click, or
432 	// invalid click, so ignore.
433 	if (!currentObject)
434 	{
435 		menuFactories->CheckMenu(currentObject, menuBar, toolBar);
436 	}
437 	else
438 	{
439 		int settingRefreshOnClick = settings->GetRefreshOnClick();
440 
441 		if (settingRefreshOnClick != REFRESH_OBJECT_NONE
442 		        && refresh
443 		        && currentObject->GetTypeName() != wxT("Server")
444 		        && currentObject->GetTypeName() != wxT("Servers")
445 		        && currentObject->GetTypeName() != wxT("Database")
446 		        && !currentObject->IsCollection())
447 		{
448 			refresh = false;
449 
450 			if (settingRefreshOnClick == REFRESH_OBJECT_ONLY )
451 			{
452 				// We can not update the schema, because it would cause an update to the entire tree.
453 				if (currentObject->GetTypeName() != wxT("Schema"))
454 				{
455 					wxTreeItemId currentItem = currentObject->GetId();
456 
457 					// Do not refresh and instead bail out if dialog of the currently selected
458 					// node or it's child node is open, as refresh would delete this node's object
459 					// and could cause a crash
460 					pgObject *obj = NULL;
461 					if (currentItem)
462 						obj = browser->GetObject(currentItem);
463 
464 					if (obj && obj->CheckOpenDialogs(browser, currentItem))
465 					{
466 						properties->Freeze();
467 						setDisplay(currentObject, properties, sqlPane);
468 						properties->Thaw();
469 						refresh = true;
470 						return;
471 					}
472 
473 					pgObject *newData = currentObject->Refresh(browser, currentItem);
474 
475 					if (newData != 0)
476 					{
477 						wxLogInfo(wxT("Replacing with new node %s %s for refresh"), newData->GetTypeName().c_str(), newData->GetQuotedFullIdentifier().c_str());
478 
479 						browser->DeleteChildren(currentItem);
480 						newData->SetId(currentItem);    // not done automatically
481 						browser->SetItemData(currentItem, newData);
482 
483 						// Update the node text if this is an object, as it may have been renamed
484 						if (!newData->IsCollection())
485 							browser->SetItemText(currentItem, newData->GetDisplayName());
486 
487 						delete currentObject;
488 						currentObject = newData;
489 					}
490 					else
491 					{
492 						// OK, we failed to refresh, so select the parent and delete the child.
493 						browser->SelectItem(browser->GetItemParent(currentItem));
494 						browser->Delete(currentItem);
495 					}
496 				}
497 			}
498 			else
499 				Refresh(currentObject);
500 
501 			refresh = true;
502 		}
503 
504 
505 		if (currentNode)
506 		{
507 			properties->Freeze();
508 			setDisplay(currentObject, properties, sqlPane);
509 			properties->Thaw();
510 		}
511 		else
512 			setDisplay(currentObject, 0, 0);
513 	}
514 }
515 
516 
setDisplay(pgObject * data,ctlListView * props,ctlSQLBox * sqlbox)517 void frmMain::setDisplay(pgObject *data, ctlListView *props, ctlSQLBox *sqlbox)
518 {
519 	pgServer *server = 0;
520 
521 
522 	bool showTree = false;
523 
524 	pgaFactory *factory = data->GetFactory();
525 	if (factory)
526 	{
527 		if (factory == &serverFactory)
528 		{
529 			server = (pgServer *)data;
530 
531 			data->ShowTree(this, browser, props, sqlbox);
532 			showTree = false;
533 		}
534 		else
535 			showTree = true;
536 	}
537 	else
538 		showTree = false;
539 
540 	if (showTree)
541 		data->ShowTree(this, browser, props, sqlbox);
542 
543 	if (sqlbox)
544 	{
545 		sqlbox->SetReadOnly(false);
546 		sqlbox->SetText(data->GetSql(browser));
547 		sqlbox->SetReadOnly(true);
548 	}
549 
550 	pgConn *conn = data->GetConnection();
551 	if (conn && (conn->GetStatus() == PGCONN_BROKEN || conn->GetStatus() == PGCONN_BAD))
552 	{
553 		CheckAlive();
554 		return;
555 	}
556 
557 	unsigned int i;
558 	wxMenuItem *menuItem;
559 	i = newMenu->GetMenuItemCount();
560 	while (i--)
561 	{
562 		menuItem = newMenu->GetMenuItems().Item(i)->GetData();
563 		if (menuItem)
564 			delete newMenu->Remove(menuItem);
565 	}
566 
567 	i = newContextMenu->GetMenuItemCount();
568 	while (i--)
569 	{
570 		menuItem = newContextMenu->GetMenuItems().Item(i)->GetData();
571 		if (menuItem)
572 			delete newContextMenu->Remove(menuItem);
573 	}
574 
575 	editMenu->Enable(newMenuFactory->GetId(), false);
576 
577 	wxMenu *indivMenu = data->GetNewMenu();
578 	if (indivMenu)
579 	{
580 		if (indivMenu->GetMenuItemCount())
581 		{
582 			editMenu->Enable(newMenuFactory->GetId(), true);
583 
584 			for (i = 0 ; i < indivMenu->GetMenuItemCount() ; i++)
585 			{
586 				menuItem = indivMenu->GetMenuItems().Item(i)->GetData();
587 #if wxCHECK_VERSION(2, 9, 0)
588 				wxString lab = menuItem->GetItemLabelText();
589 #else
590 				wxString lab = menuItem->GetLabel(); // deprecated
591 #endif
592 
593 				newMenu->Append(menuItem->GetId(), lab, menuItem->GetHelp());
594 				newContextMenu->Append(menuItem->GetId(), lab, menuItem->GetHelp());
595 			}
596 		}
597 		delete indivMenu;
598 	}
599 
600 	menuFactories->CheckMenu(data, menuBar, toolBar);
601 
602 	menuFactories->EnableSubmenu(menuBar, MNU_CONFIGSUBMENU);
603 	menuFactories->EnableSubmenu(menuBar, MNU_SLONY_SUBMENU);
604 }
605 
606 
OnSelActivated(wxTreeEvent & event)607 void frmMain::OnSelActivated(wxTreeEvent &event)
608 {
609 	// This handler will primarily deal with displaying item
610 	// properties in seperate windows and 'Add xxx...' clicks
611 
612 	// Get the item data, and feed it to the relevant handler,
613 	// cast as required.
614 
615 	wxTreeItemId item = event.GetItem();
616 	pgObject *data = browser->GetObject(item);
617 	if (!data)
618 		return;
619 	pgServer *server;
620 	wxCommandEvent nullEvent;
621 
622 	if (data->IsCreatedBy(serverFactory))
623 	{
624 		server = (pgServer *)data;
625 		if (!server->GetConnected())
626 		{
627 			if (ReconnectServer(server) == PGCONN_OK)
628 			{
629 				// prevent from being collapsed immediately
630 
631 				denyCollapseItem = item;
632 			}
633 		}
634 	}
635 	else
636 	{
637 		if (settings->GetDoubleClickProperties() && propFactory->CheckEnable(data))
638 		{
639 			propFactory->StartDialog(this, data);
640 			event.Skip();
641 			return;
642 		}
643 	}
644 
645 	browser->Expand(item);
646 }
647 
doPopup(wxWindow * win,wxPoint point,pgObject * object)648 void frmMain::doPopup(wxWindow *win, wxPoint point, pgObject *object)
649 {
650 	if (treeContextMenu)
651 		delete treeContextMenu;
652 
653 	treeContextMenu = new wxMenu();
654 
655 	menuFactories->AppendEnabledMenus(menuBar, treeContextMenu);
656 
657 	wxMenuItem *newItem = treeContextMenu->FindItem(newMenuFactory->GetId());
658 
659 	if (newItem)
660 	{
661 		size_t newItemPos;
662 
663 		wxMenuItemList mil = treeContextMenu->GetMenuItems();
664 		for (newItemPos = 0 ; newItemPos < mil.GetCount() ; newItemPos++)
665 		{
666 			if (mil.Item(newItemPos)->GetData()->GetId() == newItem->GetId())
667 				break;
668 		}
669 
670 		if (object)
671 		{
672 			wxMenu *indivMenu = object->GetNewMenu();
673 			if (indivMenu)
674 			{
675 
676 				if (indivMenu->GetMenuItemCount() > 1)
677 				{
678 					wxMenuItem *menuItem = menuBar->FindItem(newMenuFactory->GetId());
679 #if wxCHECK_VERSION(2, 9, 0)
680 					wxString lab = menuItem->GetItemLabelText();
681 #else
682 					wxString lab = menuItem->GetLabel(); // deprecated
683 #endif
684 
685 					treeContextMenu->Insert(newItemPos, newMenuFactory->GetId(), lab, indivMenu, menuItem->GetHelp());
686 				}
687 				else
688 				{
689 					if (indivMenu->GetMenuItemCount() == 1)
690 					{
691 						wxMenuItem *menuItem = indivMenu->GetMenuItems().Item(0)->GetData();
692 #if wxCHECK_VERSION(2, 9, 0)
693 						wxString lab = menuItem->GetItemLabelText();
694 #else
695 						wxString lab = menuItem->GetLabel(); // deprecated
696 #endif
697 						treeContextMenu->Insert(newItemPos, menuItem->GetId(), lab, menuItem->GetHelp());
698 					}
699 					delete indivMenu;
700 				}
701 			}
702 		}
703 		treeContextMenu->Remove(newItem);
704 		delete newItem;
705 	}
706 
707 	if (treeContextMenu->GetMenuItemCount())
708 		win->PopupMenu(treeContextMenu, point);
709 }
710 
711 ////////////////////////////////////////////////////////////////////////////////
712 // This handler will display a popup menu for the currently selected item
713 ////////////////////////////////////////////////////////////////////////////////
OnContextMenu(wxCommandEvent & event)714 void frmMain::OnContextMenu(wxCommandEvent &event)
715 {
716 	wxPoint point;
717 
718 	if (FindFocus() == browser)
719 	{
720 		wxRect rect;
721 		wxTreeItemId item = browser->GetSelection();
722 
723 		browser->GetBoundingRect(item, rect);
724 		point = rect.GetPosition();
725 		wxPoint origin = GetClientAreaOrigin();
726 
727 		// Because this Tree is inside a vertical splitter, we
728 		// must compensate for the size of the other elements
729 		point.x += origin.x;
730 		point.y += origin.y;
731 
732 		doPopup(this, point, browser->GetObject(item));
733 	}
734 
735 }
736 
737 
738 ////////////////////////////////////////////////////////////////////////////////
739 // This handler will display a popup menu for the item at the mouse position
740 ////////////////////////////////////////////////////////////////////////////////
OnSelRightClick(wxTreeEvent & event)741 void frmMain::OnSelRightClick(wxTreeEvent &event)
742 {
743 	wxTreeItemId item = event.GetItem();
744 	if (item != browser->GetSelection())
745 	{
746 		browser->SelectItem(item);
747 
748 		// Prevent changes to "currentObject" by "execSelchange" function by another
749 		// thread. Will hold the lock until we have the actual object in hand.
750 		s_currentObjectMutex.Lock();
751 		currentObject = browser->GetObject(item);
752 		s_currentObjectMutex.Unlock();
753 	}
754 
755 	if (currentObject)
756 		doPopup(browser, event.GetPoint(), currentObject);
757 }
758 
759 
OnDelete(wxCommandEvent & ev)760 void frmMain::OnDelete(wxCommandEvent &ev)
761 {
762 	if (currentObject->CanDrop())
763 		ExecDrop(false);
764 }
765 
766 
ExecDrop(bool cascaded)767 void frmMain::ExecDrop(bool cascaded)
768 {
769 	wxTreeItemId item = browser->GetSelection();
770 	pgCollection *collection = (pgCollection *)browser->GetObject(item);
771 
772 	// Get any table object for later update
773 	wxTreeItemId owneritem;
774 	pgObject *node = (pgObject *)browser->GetObject(item);
775 
776 	int metatype = node->GetMetaType();
777 
778 	switch (metatype)
779 	{
780 		case PGM_COLUMN:
781 			owneritem = node->GetTable()->GetId();
782 			break;
783 
784 		case PGM_CHECK:
785 		case PGM_CONSTRAINT:
786 		case PGM_EXCLUDE:
787 		case PGM_FOREIGNKEY:
788 		case PGM_INDEX:
789 		case PGM_PRIMARYKEY:
790 		case PGM_UNIQUE:
791 		case PGM_TRIGGER:
792 		case PGM_RULE: // Rules are technically table objects! Yeuch
793 		case EDB_PACKAGEFUNCTION:
794 		case EDB_PACKAGEVARIABLE:
795 		case PGM_SCHEDULE:
796 		case PGM_STEP:
797 			if (node->IsCollection())
798 				owneritem = browser->GetParentObject(node->GetId())->GetId();
799 			else
800 				owneritem = browser->GetParentObject(browser->GetParentObject(node->GetId())->GetId())->GetId();
801 			break;
802 
803 		default:
804 			break;
805 	}
806 
807 	// Grab the parent item to re-focus on.
808 	wxString parent = GetNodePath(item).BeforeLast('/');
809 
810 	bool success = false;
811 	if (collection == currentObject)
812 		success = dropSingleObject(currentObject, true, cascaded);
813 	else
814 	{
815 		if (collection && collection->IsCollection())
816 		{
817 			long index = properties->GetFirstSelected();
818 
819 			if (index >= 0)
820 			{
821 				pgObject *data = collection->FindChild(browser, index);
822 
823 				if (!data || !data->CanDrop())
824 					return;
825 
826 				if (properties->GetSelectedItemCount() == 1)
827 				{
828 					success = dropSingleObject(data, true, cascaded);
829 				}
830 				else
831 				{
832 					if (cascaded || data->RequireDropConfirm() || settings->GetConfirmDelete())
833 					{
834 						wxString text, caption;
835 						if (cascaded)
836 						{
837 							text = _("Are you sure you wish to drop multiple objects including all objects that depend on them?");
838 							caption = _("Drop multiple objects cascaded?");
839 						}
840 						else
841 						{
842 							text = _("Are you sure you wish to drop multiple objects?");
843 							caption = _("Drop multiple objects?");
844 						}
845 						wxMessageDialog msg(this, text, caption, wxYES_NO | wxICON_QUESTION | wxNO_DEFAULT);
846 						if (msg.ShowModal() != wxID_YES)
847 						{
848 							return;
849 						}
850 					}
851 
852 					bool done = true;
853 					long count = 0;
854 					while (done && data && index >= 0)
855 					{
856 						if (data->GetSystemObject())
857 						{
858 							wxMessageDialog msg(this,
859 							                    data->GetTranslatedMessage(CANNOTDROPSYSTEM),
860 							                    _("Trying to drop system object"), wxICON_EXCLAMATION);
861 							msg.ShowModal();
862 							return;
863 						}
864 
865 						done = dropSingleObject(data, false, cascaded);
866 
867 						if (done)
868 						{
869 							properties->DeleteItem(index);
870 							count++;
871 							index = properties->GetFirstSelected();
872 
873 							if (index >= 0)
874 								data = collection->FindChild(browser, index);
875 
876 							success = true;
877 						}
878 					}
879 				}
880 			}
881 		}
882 	}
883 
884 	if (success)
885 	{
886 		// If the collection has a table, refresh that as well.
887 		if (owneritem)
888 		{
889 			ObjectBrowserRefreshing(true);
890 			Refresh(browser->GetObject(owneritem));
891 			ObjectBrowserRefreshing(false);
892 		}
893 
894 		// Now re-focus on the parent of the deleted node
895 		if (!parent.IsEmpty())
896 			SetCurrentNode(browser->GetRootItem(), parent);
897 	}
898 }
899 
900 
dropSingleObject(pgObject * data,bool updateFinal,bool cascaded)901 bool frmMain::dropSingleObject(pgObject *data, bool updateFinal, bool cascaded)
902 {
903 	if (updateFinal)
904 	{
905 		// accelerator can bypass disabled menu, so we need to check
906 		if (!data || !data->CanDrop())
907 			return false;
908 
909 		if (data->CheckOpenDialogs(browser, browser->GetSelection()))
910 		{
911 			wxString msg = _("There are properties dialogues open for one or more objects that would be dropped. Please close the properties dialogues and try again.");
912 			wxMessageBox(msg, _("Cannot drop object"), wxICON_WARNING | wxOK);
913 			return false;
914 		}
915 
916 		if (data->GetSystemObject())
917 		{
918 			wxMessageDialog msg(this, data->GetTranslatedMessage(CANNOTDROPSYSTEM),
919 			                    _("Trying to drop system object"), wxICON_EXCLAMATION);
920 			msg.ShowModal();
921 			return false;
922 		}
923 
924 		if (cascaded || data->RequireDropConfirm() || settings->GetConfirmDelete())
925 		{
926 			wxString text, caption;
927 			if (cascaded)
928 			{
929 				text = data->GetTranslatedMessage(DROPINCLUDINGDEPS);
930 				caption = data->GetTranslatedMessage(DROPCASCADETITLE);
931 			}
932 			else
933 			{
934 				/*
935 				*  currentObject is set using the following command.
936 				*  i.e. currentObject = browser->GetObject(item);
937 				*  While fetching this object using this code, somehow it looses its virtual table pointer.
938 				*  Hence, it is not able to call the GetFullIdentifier - virtual function from the
939 				*  particular class, but it will always call this functions from pgObject class always.
940 				*  To rectify this problem, we need to explicitly check the meta data type and call the
941 				*  function from the particular class.
942 				*/
943 				if (data->GetMetaType() == PGM_SERVER)
944 					text = wxString::Format(_("Are you sure you wish to drop server \"%s\"?"),
945 					                        ((pgServer *)data)->GetFullIdentifier().c_str());
946 				else if (data->GetMetaType() == EDB_SYNONYM)
947 					text = ((edbPrivateSynonym *)data)->GetTranslatedMessage(DROPEXCLUDINGDEPS);
948 				else
949 					text = data->GetTranslatedMessage(DROPEXCLUDINGDEPS);
950 				caption = data->GetTranslatedMessage(DROPTITLE);
951 			}
952 			wxMessageDialog msg(this, text, caption, wxYES_NO | wxICON_QUESTION | wxNO_DEFAULT);
953 			if (msg.ShowModal() != wxID_YES)
954 			{
955 				return false;
956 			}
957 		}
958 	}
959 	bool done = data->DropObject(this, browser, cascaded);
960 
961 	if (done)
962 	{
963 		wxLogInfo(wxT("Dropping %s %s"), data->GetTypeName().c_str(), data->GetIdentifier().c_str());
964 
965 		wxTreeItemId parentItem = browser->GetItemParent(data->GetId());
966 
967 		if (updateFinal)
968 		{
969 			wxTreeItemId nextItem;
970 			if (browser->IsVisible(data->GetId()))
971 				nextItem = browser->GetNextVisible(data->GetId());
972 
973 			if (nextItem)
974 			{
975 				pgObject *nextData = browser->GetObject(nextItem);
976 				if (!nextData || nextData->GetType() != data->GetType())
977 					nextItem = browser->GetPrevSibling(data->GetId());
978 			}
979 			else
980 				nextItem = browser->GetPrevSibling(data->GetId());
981 
982 			if (nextItem)
983 				browser->SelectItem(nextItem);
984 		}
985 		pgaFactory *droppedCollFactory = data->GetFactory()->GetCollectionFactory();
986 
987 		wxTreeItemId oldgroupitem;
988 		wxString oldgroupname;
989 		if (data->IsCreatedBy(serverFactory))
990 		{
991 			oldgroupname = ((pgServer *)data)->GetGroup();
992 			oldgroupitem = browser->GetItemParent(data->GetId());
993 		}
994 
995 		browser->Delete(data->GetId());
996 		// data is invalid now
997 
998 		if (updateFinal)
999 		{
1000 			if (!oldgroupname.IsEmpty())
1001 			{
1002 				int total = browser->GetChildrenCount(oldgroupitem, false);
1003 				if (total == 0)
1004 					browser->Delete(oldgroupitem);
1005 				else
1006 				{
1007 					wxString label = oldgroupname + wxT(" (") + NumToStr((long)total) + wxT(")");
1008 					browser->SetItemText(oldgroupitem, label);
1009 				}
1010 			}
1011 			else
1012 			{
1013 				pgCollection *collection = 0;
1014 
1015 				while (parentItem)
1016 				{
1017 					collection = (pgCollection *)browser->GetObject(parentItem);
1018 					if (collection && collection->IsCollection() && collection->GetFactory() == droppedCollFactory)
1019 					{
1020 						collection->UpdateChildCount(browser);
1021 						break;
1022 					}
1023 					parentItem = browser->GetItemParent(parentItem);
1024 				}
1025 			}
1026 		}
1027 
1028 		// Update the server list, if we dropped a server
1029 		StoreServers();
1030 	}
1031 	return done;
1032 }
1033 
1034 
OnNew(wxCommandEvent & ev)1035 void frmMain::OnNew(wxCommandEvent &ev)
1036 {
1037 	pgaFactory *factory = pgaFactory::GetFactory(ev.GetId() - MNU_NEW);
1038 
1039 	if (factory == &serverFactory)
1040 	{
1041 		if (currentObject && currentObject->IsCreatedBy(serverFactory))
1042 		{
1043 			pgServer *server = (pgServer *)currentObject;
1044 			if (!server->GetConnected())
1045 				ReconnectServer(server);
1046 		}
1047 		return;
1048 	}
1049 
1050 	if (currentObject)
1051 	{
1052 		if (!dlgProperty::CreateObjectDialog(this, currentObject, factory))
1053 			CheckAlive();
1054 	}
1055 }
1056 
1057 
OnSaveDefinition(wxCommandEvent & event)1058 void frmMain::OnSaveDefinition(wxCommandEvent &event)
1059 {
1060 
1061 	wxLogInfo(wxT("Saving object definition"));
1062 
1063 	if (sqlPane->GetText().IsNull())
1064 	{
1065 		wxLogError(__("There is nothing in the SQL pane to save!"));
1066 		return;
1067 	}
1068 
1069 	wxString file;
1070 	settings->Read(wxT("frmMain/LastFile"), &file, wxEmptyString);
1071 
1072 #ifdef __WXMSW__
1073 	wxFileDialog filename(this, _("Select output file"), ::wxPathOnly(file), file, _("SQL Scripts (*.sql)|*.sql|All files (*.*)|*.*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
1074 #else
1075 	wxFileDialog filename(this, _("Select output file"), ::wxPathOnly(file), file, _("SQL Scripts (*.sql)|*.sql|All files (*)|*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
1076 #endif
1077 
1078 	// Show the dialogue
1079 	if (filename.ShowModal() == wxID_OK)
1080 	{
1081 		// Write the file
1082 		if (!FileWrite(filename.GetPath(), sqlPane->GetText()))
1083 		{
1084 			wxLogError(__("Could not write the file %s: Errcode=%d."), filename.GetPath().c_str(), wxSysErrorCode());
1085 		}
1086 	}
1087 	else
1088 	{
1089 		wxLogInfo(wxT("User cancelled"));
1090 	}
1091 
1092 	settings->Write(wxT("frmMain/LastFile"), filename.GetPath());
1093 }
1094 
OnToggleSqlPane(wxCommandEvent & event)1095 void frmMain::OnToggleSqlPane(wxCommandEvent &event)
1096 {
1097 	if (viewMenu->IsChecked(MNU_SQLPANE))
1098 		manager.GetPane(wxT("sqlPane")).Show(true);
1099 	else
1100 		manager.GetPane(wxT("sqlPane")).Show(false);
1101 	manager.Update();
1102 }
1103 
OnToggleObjectBrowser(wxCommandEvent & event)1104 void frmMain::OnToggleObjectBrowser(wxCommandEvent &event)
1105 {
1106 	if (viewMenu->IsChecked(MNU_OBJECTBROWSER))
1107 		manager.GetPane(wxT("objectBrowser")).Show(true);
1108 	else
1109 		manager.GetPane(wxT("objectBrowser")).Show(false);
1110 	manager.Update();
1111 }
1112 
OnToggleToolBar(wxCommandEvent & event)1113 void frmMain::OnToggleToolBar(wxCommandEvent &event)
1114 {
1115 	if (viewMenu->IsChecked(MNU_TOOLBAR))
1116 		manager.GetPane(wxT("toolBar")).Show(true);
1117 	else
1118 		manager.GetPane(wxT("toolBar")).Show(false);
1119 	manager.Update();
1120 }
1121 
OnAuiUpdate(wxAuiManagerEvent & event)1122 void frmMain::OnAuiUpdate(wxAuiManagerEvent &event)
1123 {
1124 	if(event.pane->name == wxT("objectBrowser"))
1125 	{
1126 		viewMenu->Check(MNU_OBJECTBROWSER, false);
1127 	}
1128 	else if(event.pane->name == wxT("sqlPane"))
1129 	{
1130 		viewMenu->Check(MNU_SQLPANE, false);
1131 	}
1132 	else if(event.pane->name == wxT("toolBar"))
1133 	{
1134 		viewMenu->Check(MNU_TOOLBAR, false);
1135 	}
1136 	event.Skip();
1137 }
1138 
OnAuiNotebookPageClose(wxAuiNotebookEvent & event)1139 void frmMain::OnAuiNotebookPageClose(wxAuiNotebookEvent &event)
1140 {
1141 	// Prevent the user closing the four main tabs.
1142 	if (event.GetSelection() < 4)
1143 	{
1144 		wxMessageBox(_("This tab cannot be closed."), _("Close tab"), wxICON_INFORMATION | wxOK);
1145 		event.Veto();
1146 		return;
1147 	}
1148 
1149 	event.Skip();
1150 }
1151 
OnDefaultView(wxCommandEvent & event)1152 void frmMain::OnDefaultView(wxCommandEvent &event)
1153 {
1154 	manager.LoadPerspective(FRMMAIN_DEFAULT_PERSPECTIVE, true);
1155 
1156 	// Reset the captions for the current language
1157 	manager.GetPane(wxT("objectBrowser")).Caption(_("Object browser"));
1158 	manager.GetPane(wxT("listViews")).Caption(_("Info pane"));
1159 	manager.GetPane(wxT("sqlPane")).Caption(_("SQL pane"));
1160 	manager.GetPane(wxT("toolBar")).Caption(_("Tool bar"));
1161 
1162 	// tell the manager to "commit" all the changes just made
1163 	manager.Update();
1164 
1165 	// Sync the View menu options
1166 	viewMenu->Check(MNU_SQLPANE, manager.GetPane(wxT("sqlPane")).IsShown());
1167 	viewMenu->Check(MNU_OBJECTBROWSER, manager.GetPane(wxT("objectBrowser")).IsShown());
1168 	viewMenu->Check(MNU_TOOLBAR, manager.GetPane(wxT("toolBar")).IsShown());
1169 }
1170 
OnPositionStc(wxStyledTextEvent & event)1171 void frmMain::OnPositionStc(wxStyledTextEvent &event)
1172 {
1173 	if (sqlPane->GetSelectedText().IsNull())
1174 		editMenu->Enable(MNU_COPY, false);
1175 	else
1176 		editMenu->Enable(MNU_COPY, true);
1177 }
1178