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