pgadmin3/ctl/explainCanvas.cpp
levinsv 4af765213c support PG11
Поддержка PostgreSQL 11 только для Windows
2018-10-10 22:59:25 +05:00

651 lines
13 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// explainCanvas.cpp - Explain Canvas
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
// App headers
#include "pgAdmin3.h"
#include "ctl/explainCanvas.h"
BEGIN_EVENT_TABLE(ExplainCanvas, wxShapeCanvas)
EVT_MOTION(ExplainCanvas::OnMouseMotion)
END_EVENT_TABLE()
ExplainCanvas::ExplainCanvas(wxWindow *parent)
: wxShapeCanvas(parent), rootShape(NULL)
{
SetDiagram(new wxDiagram);
GetDiagram()->SetCanvas(this);
SetBackgroundColour(*wxWHITE);
popup = NULL;
}
ExplainCanvas::~ExplainCanvas()
{
}
void ExplainCanvas::Clear()
{
GetDiagram()->DeleteAllShapes();
rootShape = NULL;
}
void ExplainCanvas::SetExplainString(const wxString &str)
{
// filtred block never executed
wxString rez=wxT("");
wxString flt=wxT("");
wxStringTokenizer tmpstr(str, wxT("\n"));
int p=-1;
while (tmpstr.HasMoreTokens())
{
wxString tmp = tmpstr.GetNextToken();
if (p>-1)
{
// áëîê never executed
const wxChar *cp = tmp.c_str();
int pp=0;
while (pp<=p)
{
if (*cp != ' ')
{
break;
}
cp++;
pp++;
}
if (pp<=p) p=-1;
}
if (p==-1)
{
p=tmp.Find(wxT("->"));
if (p>-1) {
int p2=tmp.Find(wxT("(never executed)"));
if (p2==-1) {
p=-1;
} else
{
continue;
}
}
flt=flt+tmp+wxT("\n");
}
}
Clear();
// We can have multiple plans in a single explain string
// Add a empty root shape, which will never get drawn, but it will help us
// to keep track of all these plans
rootShape = ExplainShape::Create(0, NULL, wxEmptyString);
AddShape(rootShape);
ExplainShape *last = rootShape;
int maxLevel = 0;
wxStringTokenizer lines(flt, wxT("\n"));
while (lines.HasMoreTokens())
{
wxString tmp = lines.GetNextToken();
wxString line = tmp.Strip(wxString::both);
int braceCount = 0;
do
{
const wxChar *cp = line.c_str();
while (*cp)
{
if (*cp == '(')
braceCount++;
else if (*cp == ')')
braceCount--;
cp++;
}
if (braceCount > 0)
{
wxString tmp = lines.GetNextToken();
line += wxT(" ") + tmp.Strip(wxString::both);
braceCount = 0;
}
else
break;
}
while (lines.HasMoreTokens());
long level = ((tmp.Length() - line.Length() + 4) / 6) + 1;
if (last)
{
if (level != 1)
{
if (line.Left(4) == wxT("-> "))
line = line.Mid(4);
else
{
last->SetCondition(line);
continue;
}
}
while (last != rootShape && level <= last->GetLevel())
last = last->GetUpper();
}
ExplainShape *s = ExplainShape::Create(level, last, line);
if (!s)
continue;
s->SetCanvas(this);
InsertShape(s);
s->Show(true);
if (level > maxLevel)
maxLevel = level;
last = s;
}
int x0 = (int)(rootShape->GetWidth() * 3);
int y0 = (int)(rootShape->GetHeight() * 3 / 2);
int xoffs = (int)(rootShape->GetWidth() * 3);
int yoffs = (int)(rootShape->GetHeight() * 5 / 4);
wxNode *current = GetDiagram()->GetShapeList()->GetFirst();
while (current)
{
ExplainShape *s = (ExplainShape *)current->GetData();
if (!s->totalShapes)
s->totalShapes = 1;
if (s->GetUpper())
s->GetUpper()->totalShapes += s->totalShapes;
current = current->GetNext();
}
current = GetDiagram()->GetShapeList()->GetLast();
while (current)
{
ExplainShape *s = (ExplainShape *)current->GetData();
s->SetX(y0 + (maxLevel - s->GetLevel()) * xoffs);
ExplainShape *upper = s->GetUpper();
if (upper)
{
s->SetY(upper->GetY() + upper->usedShapes * yoffs);
upper->usedShapes += s->totalShapes;
// We don't require to draw a line from the root shape to its
// childrens
if (upper != rootShape)
{
wxLineShape *l = new ExplainLine(s, upper);
l->Show(true);
AddShape(l);
}
}
else
{
s->SetY(y0);
}
current = current->GetPrevious();
}
#define PIXPERUNIT 20
int w = (maxLevel * xoffs + x0 * 2 + PIXPERUNIT - 1) / PIXPERUNIT;
int h = (rootShape->totalShapes * yoffs + y0 * 2 + PIXPERUNIT - 1) / PIXPERUNIT;
SetScrollbars(PIXPERUNIT, PIXPERUNIT, w, h);
}
void ExplainCanvas::OnMouseMotion(wxMouseEvent &ev)
{
ev.Skip(true);
if (ev.Dragging())
return;
wxClientDC dc(this);
PrepareDC(dc);
wxPoint logPos(ev.GetLogicalPosition(dc));
double x, y;
x = (double) logPos.x;
y = (double) logPos.y;
// Find the nearest object
int attachment = 0;
ExplainShape *nearestObj = dynamic_cast<ExplainShape *>(FindShape(x, y, &attachment));
if (nearestObj)
{
ShowPopup(nearestObj);
}
}
void ExplainCanvas::ShowPopup(ExplainShape *s)
{
if (popup || s == NULL)
return;
popup = new ExplainPopup(this, s, &popup);
}
void ExplainCanvas::SaveAsImage(const wxString &fileName, wxBitmapType imageType)
{
if (GetDiagram()->GetCount() == 0)
{
wxMessageBox(_("Nothing to be saved!"), _("Save As an image"), wxOK | wxICON_INFORMATION);
return;
}
int width = 0, height = 0;
GetVirtualSize(&width, &height);
/*
* Create the bitmap from the Explain window
*/
wxMemoryDC memDC;
wxBitmap tempBitmap(width, height);
memDC.SelectObject(tempBitmap);
memDC.Clear();
// Draw the diagram on the bitmap (Memory Device Context)
GetDiagram()->Redraw(memDC);
memDC.SelectObject(wxNullBitmap);
if (!tempBitmap.SaveFile(fileName, imageType))
{
wxLogError(_("Could not write file \"%s\": error code %d."), fileName.c_str(), wxSysErrorCode());
}
}
class ExplainText : public wxWindow
{
public:
ExplainText(ExplainPopup *parent, ExplainShape *s);
protected:
void OnMouseMove(wxMouseEvent &ev);
#ifdef wxUSE_POPUPWIN
void OnMouseLost(wxMouseCaptureLostEvent &ev);
#endif
private:
ExplainPopup *popup;
void OnPaint(wxPaintEvent &ev);
wxString m_desc, m_detail, m_condition, m_cost, m_actual;
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(ExplainText, wxWindow)
EVT_PAINT(ExplainText::OnPaint)
EVT_MOTION(ExplainText::OnMouseMove)
#ifdef wxUSE_POPUPWIN
EVT_MOUSE_CAPTURE_LOST(ExplainText::OnMouseLost)
#endif
END_EVENT_TABLE()
ExplainText::ExplainText(ExplainPopup *parent, ExplainShape *s) : wxWindow(parent, -1)
{
SetBackgroundColour(wxColour(255, 255, 224));
popup = parent;
wxWindowDC dc(this);
dc.SetFont(settings->GetSystemFont());
m_desc = s->description;
m_detail = s->detail;
m_condition = s->condition;
m_cost = s->cost;
m_actual = s->actual;
int w1, w2, h;
dc.GetTextExtent(m_desc, &w1, &h);
dc.GetTextExtent(m_detail, &w2, &h);
if (w1 < w2) w1 = w2;
dc.GetTextExtent(m_condition, &w2, &h);
if (w1 < w2) w1 = w2;
dc.GetTextExtent(m_cost, &w2, &h);
if (w1 < w2) w1 = w2;
dc.GetTextExtent(m_actual, &w2, &h);
if (w1 < w2) w1 = w2;
int n = 2;
if (!m_detail.IsEmpty())
n++;
if (!m_condition.IsEmpty())
n++;
if (!m_cost.IsEmpty())
n++;
if (!m_actual.IsEmpty())
n++;
if (!h)
h = GetCharHeight();
SetSize(GetCharHeight() + w1, GetCharHeight() + h * n + h / 3);
}
void ExplainText::OnMouseMove(wxMouseEvent &ev)
{
popup->OnMouseMove(ev);
}
#ifdef wxUSE_POPUPWIN
void ExplainText::OnMouseLost(wxMouseCaptureLostEvent &ev)
{
/*
* We will not do anything here.
* But - in order to resolve a weird bug on window, when using
* wxPopupTransientWindow, related to loosing the mouse control,
* was not taken care properly, we have to introduce this function
* to avoid the crash.
*
* Please refer:
* http://article.gmane.org/gmane.comp.lib.wxwidgets.devel/82376
*/
}
#endif
void ExplainText::OnPaint(wxPaintEvent &ev)
{
wxPaintDC dc(this);
wxFont stdFont = settings->GetSystemFont();
wxFont boldFont = stdFont;
boldFont.SetWeight(wxBOLD);
int x = GetCharHeight() / 2;
int y = GetCharHeight() / 2;
int w, yoffs;
dc.GetTextExtent(wxT("Dummy"), &w, &yoffs);
dc.SetFont(boldFont);
dc.DrawText(m_desc, x, y);
dc.SetFont(stdFont);
if (!m_detail.IsEmpty())
{
y += yoffs;
dc.DrawText(m_detail, x, y);
}
y += yoffs / 3;
if (!m_condition.IsEmpty())
{
y += yoffs;
dc.DrawText(m_condition, x, y);
}
if (!m_cost.IsEmpty())
{
y += yoffs;
dc.DrawText(m_cost, x, y);
}
if (!m_actual.IsEmpty())
{
y += yoffs;
dc.DrawText(m_actual, x, y);
}
#if wxUSE_POPUPWIN
wxPen pen1 = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)),
pen2 = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW)),
pen3 = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DHIGHLIGHT)),
pen4 = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
wxRect rect(wxPoint(0, 0), GetSize());
// draw the rectangle
dc.SetPen(pen1);
dc.DrawLine(rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom());
dc.DrawLine(rect.GetLeft() + 1, rect.GetTop(), rect.GetRight(), rect.GetTop());
dc.SetPen(pen2);
dc.DrawLine(rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom());
dc.DrawLine(rect.GetLeft(), rect.GetBottom(), rect.GetRight() + 1, rect.GetBottom());
// adjust the rect
rect.Inflate(-1);
// draw the rectangle
dc.SetPen(pen3);
dc.DrawLine(rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom());
dc.DrawLine(rect.GetLeft() + 1, rect.GetTop(), rect.GetRight(), rect.GetTop());
dc.SetPen(pen4);
dc.DrawLine(rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom());
dc.DrawLine(rect.GetLeft(), rect.GetBottom(), rect.GetRight() + 1, rect.GetBottom());
// adjust the rect
rect.Inflate(-1);
dc.SetPen(pen1);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(rect);
#endif
}
BEGIN_EVENT_TABLE(ExplainPopup, pgTipWindowBase)
EVT_MOTION(ExplainPopup::OnMouseMove)
EVT_LEFT_DOWN(ExplainPopup::OnMouseClick)
EVT_RIGHT_DOWN(ExplainPopup::OnMouseClick)
EVT_MIDDLE_DOWN(ExplainPopup::OnMouseClick)
#if wxUSE_POPUPWIN
EVT_MOUSE_CAPTURE_LOST(ExplainPopup::OnMouseLost)
#else
EVT_KILL_FOCUS(ExplainPopup::OnKillFocus)
EVT_ACTIVATE(ExplainPopup::OnActivate)
#endif // !wxUSE_POPUPWIN
END_EVENT_TABLE()
ExplainPopup::ExplainPopup(ExplainCanvas *parent, ExplainShape *shape, ExplainPopup **popup)
#if wxUSE_POPUPWIN
: wxPopupTransientWindow(parent, wxNO_BORDER)
#else
: wxFrame(parent, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxNO_BORDER | wxFRAME_NO_TASKBAR )
#endif
{
wxASSERT(parent != NULL);
wxASSERT(shape != NULL);
if (popup)
m_ptr = popup;
else
m_ptr = NULL;
m_explainText = new ExplainText(this, shape);
#if !wxUSE_POPUPWIN
m_creationTime = wxGetLocalTime();
#endif
double width = 0.0, height = 0.0;
shape->GetBoundingBoxMin(&width, &height);
if (fabs(width) < 4.0) width = 4.0;
if (fabs(height) < 4.0) height = 4.0;
width += (double)4.0;
height += (double)4.0; // Allowance for inaccurate mousing
int x = (int)(shape->GetX() - (width / 2.0));
int y = (int)(shape->GetY() - (height / 2.0));
int sx, sy;
parent->CalcScrolledPosition(x, y, &sx, &sy);
parent->ClientToScreen(&sx, &sy);
m_rectBound.x = sx;
m_rectBound.y = sy;
m_rectBound.width = WXROUND(width);
m_rectBound.height = WXROUND(height);
wxSize popupSize;
popupSize = m_explainText->GetSize();
popupSize.DecTo(wxGetDisplaySize());
SetSize(popupSize);
if (sy > GetClientSize().y * 2 / 3)
sy -= GetSize().y;
sx -= GetSize().x / 2;
if (sx < 5) sx = 5;
if (sx < 0) x = 0;
#if wxUSE_POPUPWIN
Position(wxPoint(sx, sy), wxSize(0, 0));
Popup(m_explainText);
m_explainText->SetFocus();
#ifdef __WXGTK__
m_explainText->CaptureMouse();
#endif
#else
Move(sx, sy);
Show(true);
m_explainText->SetFocus();
m_explainText->CaptureMouse();
#endif
}
void ExplainPopup::OnMouseMove(wxMouseEvent &ev)
{
if ( m_rectBound.width &&
!m_rectBound.Contains(ClientToScreen(ev.GetPosition())) )
{
// mouse left the bounding rect, disappear
Close();
}
else
{
ev.Skip();
}
}
void ExplainPopup::OnMouseClick(wxMouseEvent &ev)
{
Close();
}
ExplainPopup::~ExplainPopup()
{
if (m_ptr)
{
*m_ptr = NULL;
}
#ifdef wxUSE_POPUPWIN
#ifdef __WXGTK__
if (m_explainText->HasCapture() )
m_explainText->ReleaseMouse();
#endif
#endif
if(HasCapture())
ReleaseMouse();
}
void ExplainPopup::Close()
{
if (m_ptr)
{
*m_ptr = NULL;
m_ptr = NULL;
}
if (m_explainText->HasCapture())
m_explainText->ReleaseMouse();
if(HasCapture())
ReleaseMouse();
#if wxUSE_POPUPWIN
Show(false);
#ifdef __WXGTK__
#endif
Destroy();
#else
wxFrame::Close();
#endif
}
#if wxUSE_POPUPWIN
void ExplainPopup::OnDismiss()
{
Close();
}
void ExplainPopup::OnMouseLost(wxMouseCaptureLostEvent &ev)
{
/* Do Nothing */
}
#else
void ExplainPopup::OnKillFocus(wxFocusEvent &event)
{
// Workaround the kill focus event happening just after creation in wxGTK
if (wxGetLocalTime() > m_creationTime + 1 &&
m_rectBound.width &&
!m_rectBound.Contains(::wxGetMousePosition()))
{
Close();
return;
}
m_explainText->SetFocus();
m_explainText->CaptureMouse();
}
void ExplainPopup::OnActivate(wxActivateEvent &event)
{
if (!event.GetActive())
{
if ( m_rectBound.width &&
!m_rectBound.Contains(::wxGetMousePosition()) )
{
Close();
return;
}
m_explainText->SetFocus();
m_explainText->CaptureMouse();
}
}
#endif // !wxUSE_POPUPWIN