pgadmin3/ctl/ctlTree.cpp
lsv bce303c437 fix #38. Add options "Quick jump to the root node" and "Auto save query text".
Первая предназначена для отключения/включения быстрого перемещения к корневым узлам.
Вторая для отключения/включения автоматического сохранения закладок в Query Tool.
После отключения авто сохранения возможно понадобиться вручную очистить каталог
с сохранёнными закладками %APPDATA%\postgresql\recovery
2023-10-25 21:12:04 +05:00

574 lines
14 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlTree.cpp - wxTreeCtrl containing pgObjects
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "pgAdmin3.h"
#include "ctl/ctlTree.h"
#include "schema/pgObject.h"
#include "schema/pgCollection.h"
#include "schema/pgServer.h"
BEGIN_EVENT_TABLE(ctlTree, wxTreeCtrl)
EVT_CHAR(ctlTree::OnChar)
EVT_MOUSE_EVENTS(ctlTree::OnMouse)
END_EVENT_TABLE()
wxTreeItemId ctlTree::FindItem(const wxTreeItemId &idParent, const wxString &prefixOrig, const bool full )
{
// match is case insensitive as this is more convenient to the user: having
// to press Shift-letter to go to the item starting with a capital letter
// would be too bothersome
wxString prefix = prefixOrig.Lower();
// determine the starting point: we shouldn't take the current item (this
// allows to switch between two items starting with the same letter just by
// pressing it) but we shouldn't jump to the next one if the user is
// continuing to type as otherwise he might easily skip the item he wanted
wxTreeItemId id = idParent;
int le=prefix.length();
if ( prefix.length() == 1 )
{
wxCookieType cookie;
if (HasChildren(id))
id = GetFirstChild(id, cookie);
else
{
// Try a sibling of this or ancestor instead
wxTreeItemId p = id;
wxTreeItemId toFind;
do
{
toFind = GetNextSibling(p);
p = GetItemParent(p);
}
while (p.IsOk() && !toFind.IsOk());
id = toFind;
}
}
// look for the item starting with the given prefix after it
while ( id.IsOk() &&
( ( GetItemText(id) == wxT("Dummy") && !GetItemData(id) ) ||
!( (!full&&GetItemText(id).Lower().StartsWith(prefix)) ||
(full && //GetItemText(id).Lower().StartsWith(prefix) && GetItemText(id).length()==le)
GetObject(id)!=NULL&&GetObject(id)->GetName()==prefix )
)
))
{
wxCookieType cookie;
if ( HasChildren(id) )
id = GetFirstChild(id, cookie);
else
{
// Try a sibling of this or ancestor instead
wxTreeItemId p = id;
wxTreeItemId toFind;
do
{
toFind = GetNextSibling(p);
p = GetItemParent(p);
}
while (p.IsOk() && !toFind.IsOk());
id = toFind;
}
}
// if we haven't found anything...
if ( !id.IsOk() )
{
// ... wrap to the beginning
id = GetRootItem();
if ( HasFlag(wxTR_HIDE_ROOT) )
{
wxCookieType cookie;
// can't select virtual root
if ( HasChildren(id) )
id = GetFirstChild(id, cookie);
else
{
// Try a sibling of this or ancestor instead
wxTreeItemId p = id;
wxTreeItemId toFind;
do
{
toFind = GetNextSibling(p);
p = GetItemParent(p);
}
while (p.IsOk() && !toFind.IsOk());
id = toFind;
}
}
// and try all the items (stop when we get to the one we started from)
while ( id.IsOk() && id != idParent &&
(( GetItemText(id) == wxT("Dummy") && !GetItemData(id) ) ||
!((!full&&GetItemText(id).Lower().StartsWith(prefix)) ||
(full && //GetItemText(id).Lower().StartsWith(prefix) && GetItemText(id).length()==le)
GetObject(id)!=NULL&&GetObject(id)->GetName()==prefix )
)
))
{
wxCookieType cookie;
if ( HasChildren(id) )
id = GetFirstChild(id, cookie);
else
{
// Try a sibling of this or ancestor instead
wxTreeItemId p = id;
wxTreeItemId toFind;
do
{
toFind = GetNextSibling(p);
p = GetItemParent(p);
}
while (p.IsOk() && !toFind.IsOk());
id = toFind;
}
}
// If we haven't found the item, id.IsOk() will be false, as per
// documentation
}
return id;
}
wxTreeItemId ctlTree::GetVerticalItem(wxPoint& pt) {
int flags = 0;
wxTreeItemId item = DoTreeHitTest(pt, flags);
if ((flags & wxTREE_HITTEST_ONITEMINDENT) == wxTREE_HITTEST_ONITEMINDENT) {
if (item) {
wxRect r;
wxTreeItemId itemParent = item;
GetBoundingRect(itemParent, r, true);
r.x = r.x - 19;
wxTreeItemId prev;
bool ex = false;
while (!ex) {
ex = true;
prev = itemParent;
itemParent = GetItemParent(itemParent);
//this->ScrollTo(itemParent);
r.x = r.x - 19;
if (r.x >= pt.x) ex = false;
}
if (prev) return (prev);
}
//wxTreeEvent nevent(wxEVT_TREE_ITEM_MIDDLE_CLICK, this, item);
//nevent.m_pointDrag = CalcScrolledPosition(pt);
//event.Skip(!GetEventHandler()->ProcessEvent(nevent));
}
return NULL;
}
void ctlTree::DrawDbName(const wxTreeItemId& item) {
if (item && settings->GetVisibleDbNameTree()) {
wxRect r;
GetBoundingRect(item, r, false);
wxTreeItemId prev;
wxString name = "";
pgObject* o = GetObject(item);
if (o) {
pgConn* conn = o->GetConnection();
pgServer* s = o->GetServer();
if (conn)
name = conn->GetName().BeforeFirst(' ');
if (name.IsEmpty()) {
return ;
}
wxColour c;
wxClientDC dc(this);
if (s) {
if (!(s->GetColour().IsEmpty())) {
c=s->GetColour();
dc.SetBrush(wxBrush(c, wxSOLID));
} else
dc.SetBrush(*wxYELLOW_BRUSH);
}
// pgServer* s = wxColour(server->GetColour();
wxRect orig;
// dc.SetBrush(*wxYELLOW_BRUSH);
dc.SetPen(*wxTRANSPARENT_PEN);
wxString str = wxString::Format("%s", name);
wxSize sz = dc.GetTextExtent(str);
sz.SetWidth(sz.GetX() + 3);
r.SetWidth(sz.GetX() + 5);
orig = r;
orig.Deflate(2);
orig.SetLeft(orig.GetLeft() + (orig.GetWidth() - sz.GetWidth()));
orig.SetHeight(sz.GetHeight());
orig.SetWidth(sz.GetWidth());
dc.DrawRoundedRectangle(orig, 2);
dc.DrawText(name, wxPoint(orig.x + 2, orig.y + 1));
}
//Update();
//return;
}
}
void ctlTree::OnMouse(wxMouseEvent& event)
{
wxPoint pt = event.GetPosition();
int flags = 0;
wxTreeItemId item = DoTreeHitTest(pt, flags);
if (isJumpRoot) {
if (event.LeftDown())
{
if ((flags & wxTREE_HITTEST_ONITEMINDENT) == wxTREE_HITTEST_ONITEMINDENT) {
if (item) {
wxRect r;
wxTreeItemId itemParent = item;
GetBoundingRect(itemParent, r, false);
wxTreeItemId prev;
prev = GetVerticalItem(pt);
if (prev) SelectItem(prev);
return;
}
//wxTreeEvent nevent(wxEVT_TREE_ITEM_MIDDLE_CLICK, this, item);
//nevent.m_pointDrag = CalcScrolledPosition(pt);
//event.Skip(!GetEventHandler()->ProcessEvent(nevent));
return;
}
}
else {
if ((flags & wxTREE_HITTEST_ONITEMINDENT) == wxTREE_HITTEST_ONITEMINDENT && (item == GetSelection())) {
wxRect r;
GetBoundingRect(item, r, true);
r.width = r.x - 19;
r.x = r.x - 19;
if (r.x < 10) return;
wxTreeItemId prev;
wxTreeItemId itemParent = item;
wxImageList* list = this->GetImageList();
wxClientDC dc(this);
bool ex = false;
while (!ex) {
//ex = true;
prev = itemParent;
itemParent = GetItemParent(itemParent);
//this->ScrollTo(itemParent);
if (!itemParent.IsOk()) break;
r.x = r.x - 19;
//if (r.x >= pt.x) ex = false;
int image = this->GetItemImage(itemParent);
if (image >= 0) {
dc.SetClippingRegion(r.x + 1 - 19, r.y + 1,
16, 16);
list->Draw(image, dc,
r.x + 1 - 19,
r.y + 1,
wxIMAGELIST_DRAW_TRANSPARENT
);
dc.DestroyClippingRegion();
}
}
// dc.SetBrush(*wxRED);
// dc.SetPen(*wxTRANSPARENT_PEN);
// dc.DrawRectangle(r);
Update();
return;
}
}
}
event.Skip();
}
void ctlTree::OnChar(wxKeyEvent &event)
{
int keyCode = event.GetKeyCode();
if ( !event.HasModifiers() &&
((keyCode >= '0' && keyCode <= '9') ||
(keyCode >= 'a' && keyCode <= 'z') ||
(keyCode >= 'A' && keyCode <= 'Z')))
{
wxTreeItemId currItem = GetSelection();
if (!currItem.IsOk())
return;
wxTreeItemId matchItem;
wxTreeItemData* data = GetItemData(currItem);
// Set the item colour
if (data)
{
if (((pgObject*)data)->GetMetaType() == PGM_SERVER)
{
wxString prefix = m_findPrefix + (wxChar)keyCode;
wxTreeItemId id= GetNextSibling(currItem);
while (id.IsOk() &&
((GetItemText(id) == wxT("Dummy") && !GetItemData(id)) ||
!((GetItemText(id).Lower().StartsWith(prefix))
)
))
{
id = GetNextSibling(id);
}
if (id.IsOk()) matchItem = id;
} else
matchItem = FindItem(currItem, m_findPrefix + (wxChar)keyCode);
}
if ( matchItem.IsOk() )
{
EnsureVisible(matchItem);
SelectItem(matchItem);
m_findPrefix += (wxChar)keyCode;
// also start the timer to reset the current prefix if the user
// doesn't press any more alnum keys soon -- we wouldn't want
// to use this prefix for a new item search
if ( !m_findTimer )
{
m_findTimer = new ctlTreeFindTimer(this);
}
m_findTimer->Start(ctlTreeFindTimer::CTLTREE_DELAY, wxTIMER_ONE_SHOT);
return;
}
}
else
{
event.Skip(true);
}
}
ctlTree::ctlTree(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style)
: wxTreeCtrl(parent, id, pos, size, style), m_findTimer(NULL)
{
isJumpRoot = settings->GetJumpRoot();
}
ctlTree::~ctlTree()
{
if ( m_findTimer )
delete m_findTimer;
m_findTimer = NULL;
}
void ctlTree::RemoveDummyChild(pgObject *obj)
{
wxCookieType cookie;
wxTreeItemId childItem = GetFirstChild(obj->GetId(), cookie);
if (childItem && !GetItemData(childItem))
{
// The child was a dummy item, which will be replaced by the following ShowTreeDetail by true items
Delete(childItem);
}
}
pgObject *ctlTree::GetObject(wxTreeItemId id)
{
if (id)
return (pgObject *)GetItemData(id);
return 0;
}
pgCollection *ctlTree::GetParentCollection(wxTreeItemId id)
{
pgCollection *coll = (pgCollection *)GetParentObject(id);
if (coll && coll->IsCollection())
return coll;
return 0;
}
void ctlTree::SetItemImage(const wxTreeItemId &item, int image, wxTreeItemIcon which)
{
wxTreeCtrl::SetItemImage(item, image, which);
wxTreeItemData *data = GetItemData(item);
// Set the item colour
if (data)
{
if (((pgObject *)data)->GetMetaType() == PGM_SERVER)
{
if (!((pgServer *)data)->GetColour().IsEmpty())
SetItemBackgroundColour(item, wxColour(((pgServer *)data)->GetColour()));
}
else if (((pgObject *)data)->GetServer())
{
if (!((pgObject *)data)->GetServer()->GetColour().IsEmpty())
SetItemBackgroundColour(item, wxColour(((pgObject *)data)->GetServer()->GetColour()));
}
}
}
wxTreeItemId ctlTree::AppendItem(const wxTreeItemId &parent, const wxString &text, int image, int selImage, wxTreeItemData *data)
{
wxTreeItemId itm = wxTreeCtrl::AppendItem(parent, text, image, selImage, data);
// Set the item colour
if (data)
{
if (((pgObject *)data)->GetMetaType() == PGM_SERVER)
{
if (!((pgServer *)data)->GetColour().IsEmpty())
SetItemBackgroundColour(itm, wxColour(((pgServer *)data)->GetColour()));
}
else if (((pgObject *)data)->GetServer())
{
if (!((pgObject *)data)->GetServer()->GetColour().IsEmpty())
SetItemBackgroundColour(itm, wxColour(((pgObject *)data)->GetServer()->GetColour()));
}
}
return itm;
}
wxTreeItemId ctlTree::AppendObject(pgObject *parent, pgObject *object)
{
wxString label;
wxTreeItemId item;
if (object->IsCollection())
label = object->GetTypeName();
else
label = object->GetDisplayName();
item = AppendItem(parent->GetId(), label, object->GetIconId(), -1, object);
if (object->IsCollection())
object->ShowTreeDetail(this);
else if (object->WantDummyChild())
AppendItem(object->GetId(), wxT("Dummy"));
return item;
}
pgCollection *ctlTree::AppendCollection(pgObject *parent, pgaFactory &factory)
{
pgCollection *collection = factory.CreateCollection(parent);
AppendObject(parent, collection);
return collection;
}
pgObject *ctlTree::FindObject(pgaFactory &factory, wxTreeItemId parent)
{
wxCookieType cookie;
wxTreeItemId item = GetFirstChild(parent, cookie);
while (item)
{
pgObject *obj = (pgObject *)GetItemData(item);
if (obj && obj->IsCreatedBy(factory))
return obj;
item = GetNextChild(parent, cookie);
}
return 0;
}
pgCollection *ctlTree::FindCollection(pgaFactory &factory, wxTreeItemId parent)
{
pgaFactory *cf = factory.GetCollectionFactory();
if (!cf)
return 0;
pgCollection *collection = (pgCollection *)FindObject(*cf, parent);
if (!collection || !collection->IsCollection())
return 0;
return collection;
}
void ctlTree::NavigateTree(int keyCode)
{
switch(keyCode)
{
case WXK_LEFT:
{
//If tree item has children and is expanded, collapse it, otherwise select it's parent if has one
wxTreeItemId currItem = GetSelection();
if (ItemHasChildren(currItem) && IsExpanded(currItem))
{
Collapse(currItem);
}
else
{
wxTreeItemId parent = GetItemParent(currItem);
if (parent.IsOk())
{
SelectItem(currItem, false);
SelectItem(parent, true);
}
}
}
break;
case WXK_RIGHT:
{
//If tree item do not have any children ignore it,
//otherwise expand it if not expanded, and select first child if already expanded
wxTreeItemId currItem = GetSelection();
if(ItemHasChildren(currItem))
{
if (!IsExpanded(currItem))
{
Expand(currItem);
}
else
{
wxCookieType cookie;
wxTreeItemId firstChild = GetFirstChild(currItem, cookie);
SelectItem(currItem, false);
SelectItem(firstChild, true);
}
}
}
break;
default:
wxASSERT_MSG(false, _("Currently handles only right and left arrow key, other keys are working"));
break;
}
}
//////////////////////
treeObjectIterator::treeObjectIterator(ctlTree *brow, pgObject *obj)
{
browser = brow;
object = obj;
}
pgObject *treeObjectIterator::GetNextObject()
{
if (!object || !browser)
return 0;
if (!lastItem)
lastItem = browser->GetFirstChild(object->GetId(), cookie);
else
lastItem = browser->GetNextChild(object->GetId(), cookie);
if (lastItem)
return browser->GetObject(lastItem);
else
object = 0;
return 0;
}