pgadmin3/ctl/ctlSQLGrid.cpp
lsv 295b7f97d1 Fixes and improvements (fmrReport)
1. Сортировка колонок на вкладках Статистика сохряняется по возможности.
2. Узлы плана которые помечены как (never executed) не подсвечиваются.
3. При построении плана всегда добавляется опция "SUMMARY on"
4. Исправлено не корректное отображение зависимостей для таблиц из публикаций.
5. В отчетах о статистике добавлена итоговая информация по таблицам отчета.
2025-08-14 19:34:30 +05:00

1273 lines
40 KiB
C++
Raw Blame History

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ctlSQLGrid.cpp - SQL Query result window
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/clipbrd.h>
#include <wx/generic/gridctrl.h>
#include "db/pgConn.h"
#include "ctl/ctlSQLGrid.h"
#include "utils/sysSettings.h"
#include "frm/frmExport.h"
#include <wx/regex.h>
#include "ctl/ctlSQLResult.h"
#include "utils/misc.h"
#include "utils/FunctionPGHelper.h"
#include "utils/PreviewHtml.h"
#define EXTRAEXTENT_HEIGHT 6
#define EXTRAEXTENT_WIDTH 6
//DEFINE_EVENT_TYPE(myEVT_SHOW_POPUP)
BEGIN_EVENT_TABLE(ctlSQLGrid, wxGrid)
EVT_MOUSEWHEEL(ctlSQLGrid::OnMouseWheel)
//EVT_CUSTOM(wxID_ANY, myEVT_SHOW_POPUP,ctlSQLGrid::OnShowPopup)
EVT_GRID_COL_SIZE(ctlSQLGrid::OnGridColSize)
EVT_GRID_LABEL_LEFT_CLICK(ctlSQLGrid::OnLabelClick)
EVT_GRID_CELL_RIGHT_CLICK(ctlSQLGrid::OnCellRightClick)
EVT_GRID_SELECT_CELL(ctlSQLGrid::OnGridSelectCell)
//EVT_PAINT( ctlSQLGrid::OnPaint )
END_EVENT_TABLE()
IMPLEMENT_DYNAMIC_CLASS(ctlSQLGrid, wxGrid)
ctlSQLGrid::ctlSQLGrid()
{
}
void ctlSQLGrid::setresizedpi() {
// Set cells font
wxFont fntCells(settings->GetSQLFont());
SetDefaultCellFont(fntCells);
// Set labels font
wxFont fntLabel(settings->GetSystemFont());
fntLabel.SetWeight(wxFONTWEIGHT_BOLD);
SetLabelFont(fntLabel);
SetColLabelAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
SetDefaultCellAlignment(wxALIGN_LEFT, wxALIGN_TOP);
SetRowLabelSize(FromDIP(50));
SetDefaultRowSize(fntCells.GetPointSize() * 2 + 2);
SetColLabelSize(fntLabel.GetPointSize() * 4);
}
ctlSQLGrid::ctlSQLGrid(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size)
: wxGrid(parent, id, pos, size, wxWANTS_CHARS | wxVSCROLL | wxHSCROLL)
{
/*
Bind(wxEVT_DPI_CHANGED, [this](wxDPIChangedEvent& event) {
setresizedpi();
event.Skip();
});
*/
Bind(wxEVT_THREAD, &ctlSQLGrid::OnShowPopup, this);
setresizedpi();
SetDefaultCellOverflow(false);
//SetDefaultRenderer(new wxGridCellAutoWrapStringRenderer);
SetDefaultRenderer(new CursorCellRenderer);
//SetUseNativeColLabels(true);
//UseNativeColHeader(true);
SetCellHighlightColour(wxColor(0, 0, 0));
#ifdef __WXGTK__
wxColour selbg = GetSelectionBackground();
wxColour labbg = GetLabelBackgroundColour();
wxString t1 = selbg.GetAsString();
wxString t2 = labbg.GetAsString();
wxColour cline = GetGridLineColour();
wxString t3 = cline.GetAsString();
if (labbg.GetRGB() == cline.GetRGB()) {
int min = wxMin(labbg.GetBlue(), labbg.GetGreen());
min = wxMin(min, labbg.GetRed());
if (min > 200) min = min - 30; else min = min + 30;
wxColour labbgn(min, min, min);
SetLabelBackgroundColour(labbgn);
}
#endif
grp = NULL;
isSort = false;
searchStr = "";
Connect(wxID_ANY, wxEVT_GRID_LABEL_LEFT_DCLICK, wxGridEventHandler(ctlSQLGrid::OnLabelDoubleClick));
}
#include "wx/renderer.h"
#include "wx/headerctrl.h"
void ctlSQLGrid::OnGridSelectCell(wxGridEvent& evt) {
int row = evt.GetRow();
//int col = evt.GetCol();
wxGridCellCoords cr = GetGridCursorCoords();
if (cr.GetRow() != row && cr.GetRow() != -1) {
GetGridRowLabelWindow()->Refresh();
}
}
void ctlSQLGrid::DrawRowLabel(wxDC& dc, int row) {
int c = GetGridCursorRow();
if (c != row) wxGrid::DrawRowLabel(dc, row);
else {
//wxColour c = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT);
wxColour c = GetGridRowLabelWindow()->GetBackgroundColour();
int d = 20;
int r = c.GetRed() + d; int g = c.GetGreen() + d; int b = c.GetBlue() + d;
if (r > 255 || g > 255 || b > 255) {
r = r - 2 * d;
g = g - 2 * d;
b = b - 2 * d;
}
wxColour c3(r, g, b);
wxDCBrushChanger setBrush(dc, c3);
wxDCPenChanger setPen(dc, *wxTRANSPARENT_PEN);
wxRect rect(0, GetRowTop(row), GetRowLabelSize(), GetRowHeight(row));
dc.DrawRectangle(rect);
wxGrid::DrawRowLabel(dc, row);
}
}
void ctlSQLGrid::DrawColLabel(wxDC& dc, int col) {
wxGrid::DrawColLabel(dc, col);
if (!IsSort()) return;
int colLeft = GetColLeft(col);
wxRect rect(colLeft, 0, GetColWidth(col), m_colLabelHeight);
sqlResultTable* t = (sqlResultTable*)GetTable();
wxHeaderSortIconType sortArrow = t->getSortColumn(col) != 0
? t->getSortColumn(col) > 0
? wxHDR_SORT_ICON_UP
: wxHDR_SORT_ICON_DOWN
: wxHDR_SORT_ICON_NONE;
if (sortArrow != wxHDR_SORT_ICON_NONE)
{
wxRect ar = rect;
// make a rect for the arrow
ar.height = FromDIP(4);
ar.width = FromDIP(8);
ar.y += (rect.height - ar.height) / 2;
ar.x = ar.x + rect.width - 3 * ar.width / 2;
int arrowSpace = 0;
arrowSpace = 3 * ar.width / 2; // space to preserve when drawing the label
wxPoint triPt[3];
if (sortArrow & wxHDR_SORT_ICON_UP)
{
triPt[0].x = ar.width / 2;
triPt[0].y = 0;
triPt[1].x = ar.width;
triPt[1].y = ar.height;
triPt[2].x = 0;
triPt[2].y = ar.height;
}
else
{
triPt[0].x = 0;
triPt[0].y = 0;
triPt[1].x = ar.width;
triPt[1].y = 0;
triPt[2].x = ar.width / 2;
triPt[2].y = ar.height;
}
wxColour c;
c = wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW);
for (int k = 0; k < MAX_COL_SORT; k++) {
int cl = t->colsortnumber[k];
if (cl != -1) {
if (cl == col) {
if (k == 0) c = wxColor(155, 17, 48); // red
if (k == 1) c = wxColor(255, 255, 0); // yellow
if (k == 2) c = wxColor(34, 199, 76); // green
if (k == 3) c = wxColor(12, 38, 160); // blue
}
}
else break;
}
wxDCPenChanger setPen(dc, c);
wxDCBrushChanger setBrush(dc, c);
wxDCClipper clip(dc, rect);
dc.DrawPolygon(3, triPt, ar.x, ar.y);
}
//wxRendererNative::Get().DrawHeaderButton
// (m_colWindow,
// //GetColLabelWindow(),
// dc,
// rect,
// 0,
// IsSortingBy(col)
// ? IsSortOrderAscending()
// ? wxHDR_SORT_ICON_UP
// : wxHDR_SORT_ICON_DOWN
// : wxHDR_SORT_ICON_NONE
// );
}
void ctlSQLGrid::OnGridColSize(wxGridSizeEvent& event)
{
// Save key="index:label", value=size
int col = event.GetRowOrCol();
if (col < 0) return;
colSizes[GetColKeyValue(col)] = GetColSize(col);
}
void ctlSQLGrid::OnCopy(wxCommandEvent& ev)
{
Copy(false);
}
void ctlSQLGrid::OnMouseWheel(wxMouseEvent& event)
{
if (event.ControlDown() || event.CmdDown())
{
wxFont fontlabel = GetLabelFont();
wxFont fontcells = GetDefaultCellFont();
if (event.GetWheelRotation() > 0)
{
fontlabel.SetPointSize(fontlabel.GetPointSize() - 1);
fontcells.SetPointSize(fontcells.GetPointSize() - 1);
}
else
{
fontlabel.SetPointSize(fontlabel.GetPointSize() + 1);
fontcells.SetPointSize(fontcells.GetPointSize() + 1);
}
SetLabelFont(fontlabel);
SetDefaultCellFont(fontcells);
SetColLabelSize(fontlabel.GetPointSize() * 4);
SetDefaultRowSize(fontcells.GetPointSize() * 2);
for (int index = 0; index < GetNumberCols(); index++)
SetColSize(index, -1);
ForceRefresh();
}
else
event.Skip();
}
wxString ctlSQLGrid::GetExportLine(int row)
{
return GetExportLine(row, 0, GetNumberCols() - 1);
}
wxString ctlSQLGrid::GetExportLine(int row, int col1, int col2)
{
wxArrayInt cols;
wxString str;
int i;
if (col2 < col1)
return str;
cols.Alloc(col2 - col1 + 1);
for (i = col1; i <= col2; i++)
{
cols.Add(i);
}
return GetExportLine(row, cols);
}
wxString ctlSQLGrid::GetExportLine(int row, wxArrayInt cols)
{
wxString str;
unsigned int col;
if (GetNumberCols() == 0 || GetRowSize(row) == 0)
return str;
wxString colsep = settings->GetCopyColSeparator();
if (generatesql == 2 || generatesql == 1) colsep = wxT(",");
if (generatesql == 3) colsep = wxT(" and ");
wxString qtsimbol = settings->GetCopyQuoteChar();
if (generatesql > 0) qtsimbol = wxT("'");
wxString head = wxT("insert into tbl(");
if (cols.Count() > 1 && generatesql > 1) str.Append("(");
for (col = 0; col < cols.Count(); col++)
{
if (col > 0)
str.Append(colsep);
if (col > 0) head.Append(colsep);
head = head + GetColumnName(cols[col]);
wxString text = GetCellValue(row, cols[col]);
wxString cname = GetColumnName(cols[col]);
bool needQuote = false;
if (settings->GetCopyQuoting() == 1)
{
needQuote = IsColText(cols[col]);
}
else if (settings->GetCopyQuoting() == 2)
/* Quote everything */
needQuote = true;
if (text.Length() == 0 && generatesql > 0) { needQuote = false; }
else
if (generatesql > 0) needQuote = IsColText(cols[col]);
if (generatesql > 0) {
if (text.Length() != 0) {
text.Replace(wxT("'"), wxT("''"));
}
else text = wxT("null");
}
if (generatesql == 3) if (text == "null") str.Append(cname).Append(" is "); else
str.Append(cname).Append("=");
if (needQuote)
str.Append(qtsimbol);
str.Append(text);
if (needQuote)
str.Append(qtsimbol);
}
if (cols.Count() > 1 && generatesql > 1) str.Append(")");
if (generatesql == 1) str = head + wxT(") values (") + str + ");";
return str;
}
wxString ctlSQLGrid::GetColumnName(int colNum)
{
wxString columnName = GetColLabelValue(colNum);
columnName = columnName.Left(columnName.find(wxT("\n")));
return columnName;
}
void ctlSQLGrid::AppendColumnHeader(wxString& str, int start, int end)
{
size_t i, arrsize;
arrsize = (end - start + 1);
wxArrayInt columns;
for (i = 0; i < arrsize; i++)
{
columns.Add(start + i);
}
AppendColumnHeader(str, columns);
}
void ctlSQLGrid::AppendColumnHeader(wxString& str, wxArrayInt columns)
{
if (settings->GetColumnNames() || generatesql == 2)
{
bool CopyQuoting = (settings->GetCopyQuoting() == 1 || settings->GetCopyQuoting() == 2);
size_t i;
wxString fielddelim = ",";
if (generatesql == 3) return;
if (generatesql == 1) return;
for (i = 0; i < columns.Count(); i++)
{
long columnPos = columns.Item(i);
if (generatesql == 2) {
if (i > 0) str.Append(fielddelim);
if (i == 0 && columns.Count() > 1) str.Append("(");
str.Append(GetColumnName(columnPos));
if (i == columns.Count() - 1 && columns.Count() > 1 && generatesql == 2) str.Append(") in (");
if (columns.Count() == 1 && generatesql == 2) str.Append(" in (");
}
else
{
if (i > 0)
str.Append(settings->GetCopyColSeparator());
if (CopyQuoting)
str.Append(settings->GetCopyQuoteChar());
str.Append(GetColumnName(columnPos));
if (CopyQuoting)
str.Append(settings->GetCopyQuoteChar());
}
}
str.Append(END_OF_LINE);
}
}
int compare_int(int* a, int* b)
{
if (*a > *b) return 1;
else if (*a < *b) return -1;
else return 0;
}
#include <map>
class selMerge {
public:
std::map<int, std::map<int, int>> getmap() { return grid; }
void add(int row, int col);
void setArrayColumns(int row, wxArrayInt& columns);
void addRange(int r1, int r2, int c1, int c2) {
//std::map<int, int> v;
for (int r = r1; r <= r2; r++)
{
auto& v = grid[r];
for (int c = c1; c <= c2; c++) {
v[c] = c;
}
//grid[r].swap(v);
}
}
private:
std::map<int, std::map<int, int>> grid;
};
void selMerge::add(int row, int col) {
std::map<int, int> v;
v = grid[row];
v[col] = col;
}
void selMerge::setArrayColumns(int row, wxArrayInt& columns) {
auto& v = grid[row];
for (const auto& pair : v) {
//std::cout << pair.first << ": " << pair.second << '\n';
int k = pair.second;
columns.Add(k);
}
};
int ctlSQLGrid::CopyTableToHtml(wxString htmlquery) {
wxString htm = "<html>"
"<head>"
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n"
"<style type = \"text/css\">\n"
"#maket { width: 90%; border-collapse: collapse; table-layout: fixed;}\n"
"TD { vertical-align: top; border: 1px solid; padding: 2px; }\n"
;
int s = htmlquery.find('"');
int e = htmlquery.find('"', s + 1);
if (s > 0 && e > s) {
wxString fnt = htmlquery.SubString(s + 1, e - 1);
htm += "PRE { " + fnt + ";}\n";
}
wxArrayInt cols = GetSelectedCols();
if (cols.GetCount() == 0) {
for (int i = 0; i < GetNumberCols(); i++)
{
cols.Add(i);
}
}
wxArrayInt rows = GetSelectedRows();
size_t numRows = GetNumberRows();
bool isRowsArray = rows.GetCount() > 0;
if (isRowsArray) numRows = rows.GetCount();
wxString bg = GetGridRowLabelWindow()->GetBackgroundColour().GetAsString(wxC2S_HTML_SYNTAX);
wxString head;
head = wxString::Format("<tr style=\"font-weight: bold; background: %s;\">", bg);
htm += wxString::Format("TD#cn { width: %dpx;font-weight: bold; background: %s;}\n", GetRowLabelSize(), bg);
head += wxString::Format("<td id=\"cn\"></td>");
int sumWidth = GetRowLabelSize();
for (int i = 0; i < cols.Count(); i++)
{
long columnPos = cols.Item(i);
int w = GetColWidth(columnPos);
sumWidth += w + 2;
htm += wxString::Format("TD#c%d { width: %dpx;}\n", i, w);
head += wxString::Format("<td id=\"c%d\">%s</td>", i, GetColumnName(columnPos));
}
head += "</tr>\n";
htm += "</style></head><body>";
htm.Replace("90%", wxString::Format("%dpx", sumWidth));
htm += htmlquery;
htm += "<br><table cellspacing=\"0\" cellpadding=\"0\" id=\"maket\">";
// AppendColumnHeader(str, cols);
htm += head;
for (int i = 0; i < numRows; i++)
{
long rowPos = i;
if (isRowsArray) rowPos = rows.Item(i);
if (GetRowSize(rowPos) == 0) continue;
htm += "<tr>\n";
htm += wxString::Format("<td id=\"cn\"><pre>%d</pre></td>", rowPos + 1);
for (int c = 0; c < cols.Count(); c++) {
wxString text = GetCellValue(rowPos, cols[c]);
htm += wxString::Format("<td id=\"c%d\"><pre>%s</pre></td>", c, escapeHtml(text, true));
}
htm += "</tr>\n";
//wxString cname = GetColumnName(cols[c]);
}
htm += "</table>";
if (wxTheClipboard->Open())
{
wxDataObjectComposite* dataobj = new wxDataObjectComposite();
dataobj->Add(new wxTextDataObject(htm));
dataobj->Add(new wxHTMLDataObject(htm));
wxTheClipboard->SetData(dataobj);
wxTheClipboard->Close();
}
return 0;
}
int ctlSQLGrid::Copy(int gensql)
{
wxString str, tmp, linedelim = "";
int copied = 0;
size_t i;
generatesql = gensql;
//sqlResultTable* t = (sqlResultTable*)GetTable();
wxString sql = sqlquerytext.Lower();
int j = 0;
wxChar c;
wxString tn = wxEmptyString;
while ((j = sql.find("from", j)) > -1) {
j = j + 4;
c = sql[j];
i = j;
while (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
i++;
c = sql[i];
}
j = i;
if (c == '(') {
continue;
}
while (c != ' ' && c != '\t' && c != '\n' && c != '\r' && c != ',' && c != '(' && c != ')' && c != ';') {
j++;
if (sql.Len() == j) break;
c = sql[j];
}
tn = sql.SubString(i, j - 1); // table name
if (sql.Len() == j) break;
j = j + 1;
i = j;
if (sql.Len() == j) break;
c = sql[j];
while (c > ' ' && c != ',' && c != '(' && c != ')') {
j++;
if (sql.Len() == j) break;
c = sql[j];
}
if (j > i) {
wxString al = sql.SubString(i, j - 1); // alias name
if (al.StartsWith("where")
|| al.StartsWith("order")
|| al.StartsWith("limit")
) break;
tn = al;
}
break;
}
if (gensql == 2) linedelim = ",";
else if (gensql == 3) linedelim = " or ";
int lenrow = 0;
if (GetSelectedRows().GetCount())
{
AppendColumnHeader(str, 0, (GetNumberCols() - 1));
wxArrayInt rows = GetSelectedRows();
for (i = 0; i < rows.GetCount(); i++)
{
tmp = GetExportLine(rows.Item(i));
lenrow += tmp.Len() + linedelim.Len();
if (tmp.IsEmpty()) continue;
if (!tn.IsEmpty() && (generatesql == 1)) tmp.Replace("tbl", tn, false);
str.Append(tmp);
if (i < rows.GetCount() - 1 && (generatesql > 1)) str.Append(linedelim);
if (rows.GetCount() > 1)
{
bool delim = true;
if ((generatesql > 1) && (lenrow < 50))
delim = false;
else
{
lenrow = 0;
}
if (delim) str.Append(END_OF_LINE);
}
}
copied = rows.GetCount();
}
else if (GetSelectedCols().GetCount())
{
wxArrayInt cols = GetSelectedCols();
size_t numRows = GetNumberRows();
AppendColumnHeader(str, cols);
for (i = 0; i < numRows; i++)
{
tmp = GetExportLine(i, cols);
lenrow += tmp.Len() + linedelim.Len();
if (!tn.IsEmpty() && (generatesql == 1)) tmp.Replace("tbl", tn, false);
str.Append(tmp);
if (i < (numRows - 1) && (generatesql > 1)) str.Append(linedelim);
if (numRows > 1)
{
bool delim = true;
if ((generatesql > 1) && (lenrow < 50))
delim = false;
else
{
lenrow = 0;
}
if (delim) str.Append(END_OF_LINE);
}
}
copied = numRows;
}
else if (GetSelectionBlockTopLeft().GetCount() > 0 &&
GetSelectionBlockBottomRight().GetCount() > 0)
{
unsigned int x1, x2, y1, y2;
int count = GetSelectionBlockTopLeft().GetCount();
x1 = GetSelectionBlockTopLeft()[0].GetCol();
x2 = GetSelectionBlockBottomRight()[0].GetCol();
y1 = GetSelectionBlockTopLeft()[0].GetRow();
y2 = GetSelectionBlockBottomRight()[0].GetRow();
copied = 0;
AppendColumnHeader(str, x1, x2);
selMerge m;
for (size_t n = 0; n < count; n++)
{
x1 = GetSelectionBlockTopLeft()[n].GetCol();
x2 = GetSelectionBlockBottomRight()[n].GetCol();
y1 = GetSelectionBlockTopLeft()[n].GetRow();
y2 = GetSelectionBlockBottomRight()[n].GetRow();
if (generatesql > 1) {
m.addRange(y1, y2, x1, x2);
}
else
{
for (i = y1; i <= y2; i++)
{
tmp = GetExportLine(i, x1, x2);
lenrow += tmp.Len() + linedelim.Len();
if (!tn.IsEmpty() && (generatesql == 1)) tmp.Replace("tbl", tn, false);
str.Append(tmp);
if (i < y2 && (generatesql > 1)) str.Append(linedelim);
if (y2 > y1)
{
bool delim = true;
if ((generatesql > 1) && (lenrow < 50))
delim = false;
else
{
lenrow = 0;
}
if (delim) str.Append(END_OF_LINE);
}
}
}
copied = copied + (y2 - y1 + 1);
}
if (generatesql > 1) {
auto g = m.getmap();
wxArrayInt cols;
int maxr = g.size();
int i = 1;
for (const auto& pair : g) {
int r = pair.first;
cols.Clear();
m.setArrayColumns(r, cols);
str.Append(GetExportLine(r, cols));
if (i < maxr) str.Append(linedelim);
i++;
}
}
}
else
{
int row, col;
if (generatesql > 1) {
wxGridCellCoordsArray cord = GetSelectedCells();
int count = cord.GetCount();
int curr_row = 1000000000;
int next_row = 1000000000;
for (size_t n = 0; n < count; n++)
{
wxGridCellCoords& coords = cord[n];
if (coords.GetRow() < curr_row) curr_row = coords.GetRow();
}
while (curr_row != 1000000000)
{
wxArrayInt colsNum;
next_row = 1000000000;
for (size_t n = 0; n < count; n++)
{
wxGridCellCoords& coords = cord[n];
//wxString msg;
//msg.Printf(wxT("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> row=%d col=%d.\n"), coords.GetRow(), coords.GetCol());
//wxMessageBox(msg, wxT("About coord"), wxOK | wxICON_INFORMATION, this);
int r = coords.GetRow();
if (r == curr_row) {
colsNum.Add(coords.GetCol());
}
else if (r > curr_row && r < next_row) next_row = r;
}
colsNum.Sort(compare_int);
if (generatesql == 2) AppendColumnHeader(str, colsNum);
tmp = GetExportLine(curr_row, colsNum);
str.Append(tmp);
lenrow += tmp.Len() + linedelim.Len();
if ((next_row != 1000000000) && (generatesql == 3)) str.Append(linedelim);
if ((next_row != 1000000000) && (generatesql == 2)) str.Append(") or ");
{
bool delim = true;
if ((generatesql > 1) && (lenrow < 50))
delim = false;
else
{
lenrow = 0;
}
if (delim) str.Append(END_OF_LINE);
}
curr_row = next_row;
copied++;
}
}
else {
row = GetGridCursorRow();
col = GetGridCursorCol();
AppendColumnHeader(str, col, col);
tmp = GetExportLine(row, col, col);
if (!tn.IsEmpty() && (generatesql == 1)) tmp.Replace("tbl", tn, false);
str.Append(tmp);
copied = 1;
}
}
if (copied && (generatesql == 2)) str.Append(")");
if (copied && wxTheClipboard->Open())
{
wxTheClipboard->SetData(new wxTextDataObject(str));
wxTheClipboard->Close();
}
else
{
copied = 0;
}
return copied;
}
void ctlSQLGrid::OnLabelDoubleClick(wxGridEvent& event)
{
int maxHeight, maxWidth;
GetClientSize(&maxWidth, &maxHeight);
int row = event.GetRow();
int col = event.GetCol();
int extent, extentWant = 0;
if (row >= 0)
{
for (col = 0; col < GetNumberCols(); col++)
{
extent = GetBestSize(row, col).GetHeight();
if (extent > extentWant)
extentWant = extent;
}
extentWant += EXTRAEXTENT_HEIGHT;
extentWant = wxMax(extentWant, GetRowMinimalAcceptableHeight());
extentWant = wxMin(extentWant, maxHeight * 3 / 4);
int currentHeight = GetRowHeight(row);
if (currentHeight >= maxHeight * 3 / 4 || currentHeight == extentWant)
extentWant = GetRowMinimalAcceptableHeight();
else if (currentHeight < maxHeight / 4)
extentWant = wxMin(maxHeight / 4, extentWant);
else if (currentHeight < maxHeight / 2)
extentWant = wxMin(maxHeight / 2, extentWant);
else if (currentHeight < maxHeight * 3 / 4)
extentWant = wxMin(maxHeight * 3 / 4, extentWant);
if (extentWant != currentHeight)
{
BeginBatch();
if (IsCellEditControlShown())
{
HideCellEditControl();
SaveEditControlValue();
}
SetRowSize(row, extentWant);
EndBatch();
}
}
else if (col >= 0)
{
// Holding Ctrl or Meta switches back to automatic column's sizing
if (event.ControlDown() || event.CmdDown())
{
colSizes.erase(GetColKeyValue(col));
BeginBatch();
if (IsCellEditControlShown())
{
HideCellEditControl();
SaveEditControlValue();
}
AutoSizeColumn(col, false);
EndBatch();
}
else // toggle between some predefined sizes
{
if (col < (int)colMaxSizes.GetCount() && colMaxSizes[col] >= 0)
extentWant = colMaxSizes[col];
else
{
for (row = 0; row < GetNumberRows(); row++)
{
if (CheckRowPresent(row))
{
extent = GetBestSize(row, col).GetWidth();
if (extent > extentWant)
extentWant = extent;
}
}
}
extentWant += EXTRAEXTENT_WIDTH;
extentWant = wxMax(extentWant, GetColMinimalAcceptableWidth());
extentWant = wxMin(extentWant, maxWidth * 3 / 4);
int currentWidth = GetColSize(col);
if (currentWidth >= maxWidth * 3 / 4 || currentWidth == extentWant)
extentWant = GetColMinimalAcceptableWidth();
else if (currentWidth < maxWidth / 4)
extentWant = wxMin(maxWidth / 4, extentWant);
else if (currentWidth < maxWidth / 2)
extentWant = wxMin(maxWidth / 2, extentWant);
else if (currentWidth < maxWidth * 3 / 4)
extentWant = wxMin(maxWidth * 3 / 4, extentWant);
if (extentWant != currentWidth)
{
BeginBatch();
if (IsCellEditControlShown())
{
HideCellEditControl();
SaveEditControlValue();
}
SetColSize(col, extentWant);
EndBatch();
colSizes[GetColKeyValue(col)] = extentWant;
}
}
}
}
void ctlSQLGrid::OnMouseEvent(wxMouseEvent& event)
{
if (event.RightDown()) {
wxGridEvent ev;
OnCellRightClick(ev);
return;
}
event.Skip();
}
void ctlSQLGrid::OnShowPopup(wxThreadEvent& event) {
// wxMessageBox("omshowpopup "+event.GetString(), "msg");
wxString s = event.GetString();
wxPoint p = rpos;
wxPoint p1(rpos);
wxString bg;
wxColour bgColor = settings->GetSQLBoxColourBackground();
if (settings->GetSQLBoxUseSystemBackground())
{
bgColor = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
}
bg = bgColor.GetAsString(wxC2S_HTML_SYNTAX);
// parse context
wxRegEx r(L"(?im)(select|from|where|set|insert|into|delete)\\b", wxRE_NEWLINE);
int cnt = 0;
if (r.IsValid()) {
size_t start=0;
std::unordered_set<wxString> unic;
while (r.Matches(s.Mid(start))) {
size_t start2 = 0, len2 = 0;
r.GetMatch(&start2, &len2, 1);
unic.insert(s.Mid(start + start2,len2));
r.GetMatch(&start2, &len2, 0);
start = start + start2 + len2;
}
cnt = unic.size();
}
if (cnt >= 2) {
wxString q = s;
wxString html;
ctlSQLBox* box = new ctlSQLBox((wxWindow*) winMain, CTL_SQLQUERY, wxDefaultPosition, wxSize(0, 0), wxTE_MULTILINE | wxTE_RICH2);
box->SetText(q);
int l = q.Length();
box->Colourise(0, box->GetLength());
//bg = box->SetSQLBoxColourBackground(false).GetAsString(wxC2S_CSS_SYNTAX);
html = box->TextToHtml(0, box->GetLength(),false);
delete box;
s = html;
s = "<html><body BGCOLOR=\"" + bg + "\">" + s + "</body></html>";
}
else {
//simple text
PreviewHtml v;
wxString tt=v.Preview(s,fmtpreview::AUTO);
s = tt;
}
delete m_Popup;
wxSize rr(350, 70);
FunctionPGHelper fh(s);
wxString key = "content";
m_Popup = new popuphelp(this, key, &fh, p, rr);
if (m_Popup && m_Popup->IsValid() && rr != m_Popup->GetSizePopup()) {
// recreate with new size
rr = m_Popup->GetSizePopup();
delete m_Popup;
m_Popup = new popuphelp((wxWindow*)winMain, key, &fh, p, rr);
}
if (m_Popup && m_Popup->IsValid()) {
//m_PopupHelp->UpdateWindowUI(true);
wxSize top_sz = m_Popup->GetSizePopup();
wxPoint posScreen;
wxSize sizeScreen;
const int displayNum = wxDisplay::GetFromPoint(p);
if (displayNum != wxNOT_FOUND)
{
const wxRect rectScreen = wxDisplay(displayNum).GetGeometry();
posScreen = rectScreen.GetPosition();
sizeScreen = rectScreen.GetSize();
}
else // outside of any display?
{
// just use the primary one then
posScreen = wxPoint(0, 0);
sizeScreen = wxGetDisplaySize();
}
wxSize top_new(top_sz);
wxPoint oldp(p);
if (p.x + top_new.x > sizeScreen.x) p.x = sizeScreen.x - top_new.x - 20;
if (p.y + top_new.y > sizeScreen.y) p.y = sizeScreen.y - top_new.y - 20;
if (oldp == p) p.x = p.x + 20;
m_Popup->Move(p);
wxRect r = m_Popup->GetScreenRect();
if (r.Contains(p1)) {
//wxMouseEvent mv(wxEVT_MOTION,);
//wxGetMousePosition();
}
//m_PopupHelp->Position(p, wxSize(0, 17));
m_Popup->Popup();
//wxPopupTransientWindow
}
}
void ctlSQLGrid::OnCellRightClick(wxGridEvent& event)
{
int row = event.GetRow();
int col = event.GetCol();
wxPoint p;
if (row != -1 && col != -1) {
rrow = row;
rcol = col;
rpos = ClientToScreen(event.GetPosition());
wxThreadEvent e(wxEVT_THREAD);
//e.SetString(s);
e.SetString(GetCellValue(row, col));
//GetEventHandler()->AddPendingEvent(e);
wxPostEvent(this, e);
//event.Skip();
return;
}
else
{
row = rrow;
col = rcol;
p = rpos;
}
wxString s = GetCellValue(row, col);
}
void ctlSQLGrid::OnLabelClick(wxGridEvent& event)
{
int row = event.GetRow();
int col = event.GetCol();
if (row >= 0 && grp) {
grp->VisibleGroup(row, GetRowSize(row + 1) == 0);
Refresh();
return;
}
if (col >= 0 && (event.AltDown()))
{
// continue for sort event
if (IsSort()) {
sqlResultTable* t = (sqlResultTable*)GetTable();
t->setSortColumn(col);
return;
}
}
// add support for (de)selecting multiple rows and cols with Control pressed
else if (row >= 0 && (event.ControlDown() || event.CmdDown()))
{
if (GetSelectedRows().Index(row) == wxNOT_FOUND)
SelectRow(row, true);
else
DeselectRow(row);
}
else if (col >= 0 && (event.ControlDown() || event.CmdDown()))
{
if (GetSelectedCols().Index(col) == wxNOT_FOUND)
SelectCol(col, true);
else
DeselectCol(col);
event.Skip();
}
else
event.Skip();
}
void ctlSQLGrid::AutoSizeColumn(int col, bool setAsMin, bool doLimit)
{
ColKeySizeHashMap::iterator it = colSizes.find(GetColKeyValue(col));
if (it != colSizes.end()) // Restore user-specified size
SetColSize(col, it->second);
else
wxGrid::AutoSizeColumn(col, setAsMin);
if (doLimit)
{
int newSize, oldSize;
int maxSize, totalSize = 0, availSize;
oldSize = GetColSize(col);
availSize = GetClientSize().GetWidth() - GetRowLabelSize();
maxSize = availSize / 2;
for (int i = 0; i < GetNumberCols(); i++)
totalSize += GetColSize(i);
if (oldSize > maxSize && totalSize > availSize)
{
totalSize -= oldSize;
/* Shrink wide column to maxSize.
* If the rest of the columns are short, make sure to use all the remaining space,
* but no more than oldSize (which is enough according to AutoSizeColumns())
*/
newSize = wxMin(oldSize, wxMax(maxSize, availSize - totalSize));
SetColSize(col, newSize);
}
}
}
void ctlSQLGrid::AutoSizeColumns(bool setAsMin)
{
wxCoord newSize, oldSize, maxH = 0, newSizeH = 0;
wxCoord maxSize, totalSize = 0, availSize;
int col, nCols = GetNumberCols();
int row, nRows = GetNumberRows();
colMaxSizes.Empty();
/* We need to check each cell's width to choose best. wxGrid::AutoSizeColumns()
* is good, but looping through long result sets gives a noticeable slowdown.
* Thus we'll check every first 500 cells for each column.
*/
// First pass: auto-size columns
for (col = 0; col < nCols; col++)
{
ColKeySizeHashMap::iterator it = colSizes.find(GetColKeyValue(col));
if (it != colSizes.end()) // Restore user-specified size
{
newSize = it->second;
colMaxSizes.Add(-1);
}
else
{
wxClientDC dc(GetGridWindow());
newSize = 0;
// get cells's width
for (row = 0; row < wxMin(nRows, 500); row++)
{
wxSize size = GetBestSize(row, col);
if (size.x > newSize)
newSize = size.x;
if (size.y > newSizeH)
newSizeH = size.y;
}
// get column's label width
wxCoord w, h;
dc.SetFont(GetLabelFont());
dc.GetMultiLineTextExtent(GetColLabelValue(col), &w, &h);
if (GetColLabelTextOrientation() == wxVERTICAL)
w = h;
if (h > maxH)
maxH = h;
if (w > newSize)
newSize = w;
if (!newSize)
newSize = GetRowLabelSize();
else
// leave some space around text
newSize += FromDIP(EXTRAEXTENT_WIDTH);
colMaxSizes.Add(newSize);
}
SetColSize(col, newSize);
totalSize += newSize;
}
SetColLabelSize(maxH + FromDIP(EXTRAEXTENT_HEIGHT));
availSize = GetClientSize().GetWidth() - GetRowLabelSize();
//int newDef = GetDefaultRowSize()+FromDIP(1);
//SetDefaultRowSize(newDef, true);
// Second pass: shrink wide columns if exceeded available width
if (totalSize > availSize)
{
// A wide column shouldn't take up more than 50% of the visible space
maxSize = availSize / 2;
for (col = 0; col < nCols; col++)
{
oldSize = GetColSize(col);
// Is too wide and no user-specified size
if (oldSize > maxSize && !(col < (int)colMaxSizes.GetCount() && colMaxSizes[col] == -1))
{
totalSize -= oldSize;
/* Shrink wide column to maxSize.
* If the rest of the columns are short, make sure to use all the remaining space,
* but no more than oldSize (which is enough according to first pass)
*/
newSize = wxMin(oldSize, wxMax(maxSize, availSize - totalSize));
SetColSize(col, newSize);
totalSize += newSize;
}
}
}
}
wxString ctlSQLGrid::GetColKeyValue(int col)
{
wxString colKey = wxString::Format(wxT("%d:"), col) + GetColLabelValue(col);
return colKey;
}
wxSize ctlSQLGrid::GetBestSize(int row, int col)
{
wxSize size;
wxGridCellAttr* attr = GetCellAttr(row, col);
wxGridCellRenderer* renderer = attr->GetRenderer(this, row, col);
if (renderer)
{
wxClientDC dc(GetGridWindow());
size = renderer->GetBestSize(*this, *attr, dc, row, col);
// int h = renderer->GetBestHeight(*this, *attr, dc, row, col, size.GetWidth())
size.SetHeight(size.GetHeight() + FromDIP(EXTRAEXTENT_HEIGHT));
renderer->DecRef();
}
attr->DecRef();
return size;
}
int recurse(ctlSQLGrid* g, int pos, int row, double& transfer) {
wxString text;
double leveltime = 0; //actual time level
double lastnode = 0;
while (row < g->GetNumberRows()) {
text = g->GetCellValue(row, 0);
int p = 0;
while (text.at(p) == ' ')
{
p++;
}
if (p == pos) {
//
lastnode = 0;
if (text.at(p) == '-') {
//
double m = 1;
// -> Nested Loop (cost=205.13..273.44 rows=4 width=188) (actual time=13.157..13.157 rows=0 loops=1)
bool isstd = false;
if (text.Contains("(never executed)")) {
isstd = true;
}
wxRegEx foundstr(wxT("actual time=.*?\\.\\.([0-9.]+).*?loops=([0-9]+)\\)"), wxRE_ADVANCED);
if (foundstr.Matches(text)) {
wxString v = foundstr.GetMatch(text, 1);
v.ToCDouble(&lastnode);
v = foundstr.GetMatch(text, 2);
v.ToDouble(&m);
lastnode = lastnode * m;
leveltime = leveltime + lastnode;
}
if (isstd)
g->grp->ColoriseRow(row, wxColour(224, 255, 224)); // green
else
g->grp->ColoriseRow(row, wxColour(248, 240, 130)); // yellow
}
else
{
g->grp->ColoriseRow(row, wxColour(224, 255, 224)); // green
g->GetTable()->SetRowLabelValue(row, wxEmptyString);
}
row++;
continue;
} if (p < pos) {
// end level
// leveltime <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
transfer = leveltime;
return row;
} if (p > pos) {
// nested level
//g->SetRowGroup(row-1);
wxString s;
int newrow = recurse(g, p, row, transfer);
//
//leveltime=leveltime+transfer;
//GroupRows *u=g->getgroup();
double tt = lastnode - transfer;
text = g->GetCellValue(row - 1, 0);
if ((text.Find("Append") > 0)
|| (text.Find("Gather") > 0))
tt = lastnode;
g->grp->AddGroup(row - 1, newrow - 1, tt);
s << (tt);
s = s + wxT("-");
g->GetTable()->SetRowLabelValue(row - 1, s);
row = newrow;
}
}
return row;
}
void ctlSQLGrid::SetRowGroup(int row) { };
bool ctlSQLGrid::FullArrayCollapseRowsPlan(bool clear)
{
//wxString colKey = wxString::Format(wxT("%d:"), col) + GetColLabelValue(col);
wxString text;
//for(int row = 0; row < GetNumberRows(); ++row)
if (grp) { delete grp; }
if (clear) { grp = NULL; return true; }
grp = new GroupRows(this);
double transfersum = 0;
int r = recurse(this, 0, 0, transfersum);
grp->CalcTime();
//for(int row = 0; row < GetNumberRows(); ++row) {
// text = GetCellValue(row, 0);
// //if (row%2==0) SetRowAttr(row,pAttr);
//}
return true;
}