pgadmin3/include/ctl/wxTopActivity.h
2025-09-23 16:20:12 +05:00

705 lines
23 KiB
C++

#pragma once
#include <wx/wx.h>
#include "utils/WaitSample.h"
#include "wx/popupwin.h"
#include "wx/dataview.h"
#include "wx/headerctrl.h"
#include <wx/tokenzr.h>
#include <vector>
//#include "utils/misc.h"
// ----------------------------------------------------------------------------
// MyCustomRendererText
// ----------------------------------------------------------------------------
extern std::vector<int> sort_vec_map(const std::vector<int>& src, int& sum);
#define sepListWait ";"
extern int s_pid_HIGHLIGH;
class MyCustomRendererGraph : public wxDataViewCustomRenderer
{
public:
// This renderer can be either activatable or editable, for demonstration
// purposes. In real programs, you should select whether the user should be
// able to activate or edit the cell and it doesn't make sense to switch
// between the two -- but this is just an example, so it doesn't stop us.
explicit MyCustomRendererGraph(wxDataViewCellMode mode, int column)
: wxDataViewCustomRenderer("string", mode, wxALIGN_LEFT)
{
EnableEllipsize(wxELLIPSIZE_END);
col = column;
}
virtual bool Render(wxRect rect, wxDC* dc, int state) wxOVERRIDE
{
wxString l;
wxRect orig = rect;
if (col < 2) {
l = m_value;
if (m_value == "ffffffffffffffff" || m_value == "-1") {
l = "SUM";
RenderText(l, 0, rect, dc, state);
return true;
}
}
if (col == 0) {
if (m_value[0] == '*') {
l = m_value.AfterFirst('*');
// wxFont fn = dc->GetFont().Italic();
// wxDCFontChanger nfont(*dc, fn);
dc->SetBrush(*wxLIGHT_GREY_BRUSH);
dc->SetPen(*wxTRANSPARENT_PEN);
rect.Deflate(1);
dc->DrawRoundedRectangle(rect, 3);
RenderText(l, 0, rect, dc, state);
}
else
{
l = m_value;
RenderText(l, 0, rect, dc, state);
}
return true;
}
if (col == 1) {
RenderText(l, 0, rect, dc, state);
return true;
}
//dc->SetBrush( *wxLIGHT_GREY_BRUSH );
dc->SetBrush(*wxYELLOW_BRUSH);
//dc->SetPen(*wxTRANSPARENT_PEN);
dc->SetPen(*wxBLACK_PEN);
rect.Deflate(1);
//WaitSample*ws= static_cast<topDataViewCtrl*>(GetView())->GetSample();
//dc->DrawRoundedRectangle( rect, 3 );
wxFont fn = dc->GetFont();
fn.SetPointSize(fn.GetPointSize() - 1);
wxDCFontChanger nfont(*dc, fn);
wxString s = m_value, t;
wxStringTokenizer tk(s, sepListWait, wxTOKEN_DEFAULT);
bool isDis = true;
bool isGrp = true;
int f = 0;
float itog = 0;
int widt = orig.width - 20;
wxPoint p(orig.x, orig.y);
while (tk.HasMoreTokens())
{
l = tk.GetNextToken();
if (f == 0) {
float tmp;
l = l.AfterLast(' ');
wxSscanf(l, "%f", &tmp);
itog = tmp;
float sek = itog / 1000;
wxString tt = wxString::Format("%.1f", sek); // time sec
wxSize sz = GetTextExtent(tt);
wxRect drw(orig.x + orig.width - sz.x - 2, orig.y, sz.x + 2, orig.GetHeight());
wxRect npos(0, 0, sz.x, sz.y);
npos = npos.CentreIn(drw);
dc->DrawText(tt, npos.x, npos.y);
widt = orig.width - sz.x - 2;
}
if (f > 0) {
float tmp;
wxString w;
long cl;
l = l.AfterFirst(' ');
wxSscanf(l, "%ld %f", &cl, &tmp);
wxColour c(cl);
//c.Set(cl);
dc->SetBrush(c);
int ww = widt * tmp;
wxRect drw(p.x, p.y, ww, rect.GetHeight());
float sek = itog * tmp / 1000;
wxString tt = wxString::Format("%.1f", sek); // time sec
wxSize sz = GetTextExtent(tt);
wxRect npos(0, 0, sz.x, sz.y);
npos = npos.CentreIn(drw);
dc->DrawRectangle(drw);
{
wxColour fg(ContrastColorBlackOrWhite(c));
wxDCTextColourChanger setfg(*dc, fg);
if (sz.GetWidth() < drw.GetWidth()) dc->DrawText(tt, npos.x, npos.y);
}
p.x = p.x + ww;
}
f++;
}
return true;
}
virtual bool ActivateCell(const wxRect& WXUNUSED(cell),
wxDataViewModel* WXUNUSED(model),
const wxDataViewItem& WXUNUSED(item),
unsigned int WXUNUSED(col),
const wxMouseEvent* mouseEvent) wxOVERRIDE
{
return false;
}
virtual wxSize GetSize() const wxOVERRIDE
{
wxSize txtSize = GetTextExtent(m_value);
int lines = m_value.Freq('\n') + 1;
if (lines > 1) {
// wxLogMessage("MyCustomRendererText GetSize() %s", position);
txtSize.SetHeight(txtSize.GetHeight() * lines + 1 * lines);
}
else {
#ifdef __WXGTK__
txtSize.SetHeight(txtSize.GetHeight() + 3);
#else
txtSize.SetHeight(-1);
#endif
}
if (col == 2) txtSize.SetWidth(-1);
else
{
//maxw = txtSize.GetWidth();
//maxw=wxMax(txtSize.GetWidth(), maxw);
txtSize.SetWidth(txtSize.GetWidth() + 1);
}
return txtSize;
//return GetView()->FromDIP(wxSize(60, 20));
}
virtual bool SetValue(const wxVariant& value) wxOVERRIDE
{
m_value = value.GetString();
return true;
}
virtual bool GetValue(wxVariant& WXUNUSED(value)) const wxOVERRIDE { return true; }
#if wxUSE_ACCESSIBILITY
virtual wxString GetAccessibleDescription() const wxOVERRIDE
{
return m_value;
}
#endif // wxUSE_ACCESSIBILITY
virtual bool HasEditorCtrl() const wxOVERRIDE { return false; }
private:
wxString m_value;
int col;
int maxw = -1;
};
class MyIndexListModel : public wxDataViewIndexListModel
{
public:
MyIndexListModel(WaitSample* w) { ws = w; }
void SetAllRows(const std::vector<key3>& keys, const std::map<key3, vec_int>& wait, bool isfp) {
k = keys;
w = wait;
//IsFirstPid = isfp;
Reset(keys.size());
}
key3 GetRowValue(long row) {
key3 r{};
if (row >= 0 && row < k.size()) {
r = k[row];
}
return r;
}
// Implement base class pure virtual methods.
unsigned GetCount() const wxOVERRIDE { return k.size(); }
void GetValueByRow(wxVariant& val, unsigned row, unsigned col) const wxOVERRIDE
{
//val=wxString::Format("r:%d,c:%d",row,col);
//val = m_strings[row];
//if (row + 1 < m_strings.Count() && col == 1 ) val = m_strings[row + 1];
bool ishint = false;
int r = row;
if (!val.IsNull()) {
//r = r * -1;
ishint = true;
}
if (k.size() > 0) {
key3 kk = k.at(r);
if (col == 0) {
long ttmp = kk.pid;
if (kk.sum > 0) val = wxString::Format("*%ld", ttmp); // backend
else
val = wxString::Format("%ld", ttmp);
}
else
val = wxString::Format("%llx", kk.qid);
if (col == 2) {
vec_int v = w.at(kk);
int sz = v.size();
val = wxString::Format("count w=%d", sz);
int itog = 0;
std::vector<int> map_sum_all = sort_vec_map(v, itog);
int period = ws->getPeriod();
int total = itog;
wxString h;
if (total != 0) {
wxString x = wxString::Format("all %ld", (long)itog * period);
for (int i = 0; i < map_sum_all.size(); i++) {
int wait_index = map_sum_all[i];
if (v[wait_index] == 0) continue;
float w_sum = (v[wait_index] / (float)total);
float sek = ((period * v[wait_index]) / 1000.0);
wxString w_name = ws->GetName(wait_index, WAIT_NAME);
wxString w_grp = ws->GetName(wait_index, WAIT_GRP);
wxString full = ws->GetName(wait_index, WAIT_FULL);
long clr = ws->GetColorByWaitName(full);
wxString pr = wxString::Format("%s %ld %.3f", w_name, clr, w_sum);
if (ishint) {
if (!h.IsEmpty()) h += "\n";
h.Append(wxString::Format("%-8.1f%-15s", sek, w_name));
}
else {
if (!x.IsEmpty()) x += sepListWait;
x += pr;
}
}
if (ishint) val = h;
else
val = x;
}
}
}
}
bool SetValueByRow(const wxVariant&, unsigned, unsigned) wxOVERRIDE
{
return false;
}
unsigned int GetRow(const wxDataViewItem& item) const wxOVERRIDE {
unsigned int i;
// wxUIntToPtr()
i=wxPtrToUInt(item.GetID());
i=i-1;
return i;
} // 0 row = header
unsigned int GetColumnCount() const wxOVERRIDE { return 0; }
wxString GetColumnType(unsigned int) const wxOVERRIDE { return ""; }
private:
bool IsFirstPid = true;
std::vector<key3> k;
std::map<key3, vec_int> w;
WaitSample* ws = NULL;
wxDECLARE_NO_COPY_CLASS(MyIndexListModel);
};
//----------------------------------------------------------------------------
// SimpleTransientPopup
//----------------------------------------------------------------------------
class wxTopActivity;
class topDataViewCtrl;
class SimpleTransientPopup : public wxFrame
{
public:
friend class wxTopActivity;
friend class topDataViewCtrl;
SimpleTransientPopup(wxWindow* parent, bool scrolled, wxTopActivity* small_ctl, wxPoint p, wxString title);
virtual ~SimpleTransientPopup();
// wxPopupTransientWindow virtual methods are all overridden to log them
private:
//wxScrolledWindow* m_panel;
wxPanel* m_panel;
wxTopActivity* top;
topDataViewCtrl* dvc;
wxObjectDataPtr<MyIndexListModel> m_index_list_model;
wxPoint mouseDownPos_;
bool dragg = false;
wxTimer m_taskTimer;
private:
void OnMouse(wxMouseEvent& event);
void OnSize(wxSizeEvent& event);
void OnSetFocus(wxFocusEvent& event);
void OnKillFocus(wxFocusEvent& event);
void OnTimerEvent(wxTimerEvent& pEvent);
void OnClose(wxCommandEvent& event);
private:
wxDECLARE_ABSTRACT_CLASS(SimpleTransientPopup);
wxDECLARE_EVENT_TABLE();
};
struct title_info {
bool iner = false;
bool remove;
bool down_line;
int total;
wxString title;
};
/// <summary>
/// Графический элемент отображающий данные WaitSample
/// Может использоваться в двух режимах.
/// </summary>
class wxTopActivity : public wxControl
{
WaitSample* ws;
wxString m_title;
wxSize setsize;
wxArrayString seriosName;
std::vector<wxDateTime> xAxis;
std::vector<wxTimeSpan> yAxis;
std::vector<vec_int> val;
std::vector<long> colors;
std::vector<title_info> m_title_i;
std::map<wxString, int> m_collapse;
std::map<wxString, int> m_sumtotal;
wxPoint mouse, mouseS;
wxRect m_area;
int m_click = 0;
int m_fix_detail_idx = -1;
// fix sel Range
wxDateTime fix_pos_L;
wxDateTime fix_pos_R;
wxPoint m_fix_pos; // context win
wxString m_filter_detail;
long long m_qid_filter = -1;
bool m_regroup = false;
int m_lastInterval = 0;
int m_RightTime = -1;
wxString m_filter;
int m_agg_int = 5000;
int m_count_wait;
int m_inter[9] = {
5000 ,// 5 sek
10000 ,//10 sek
30000 ,//30 s
60000 ,//1 min
5 * 60000 ,//5 min
10 * 60000,//10 min
15 * 60000,//15 min
30 * 60000,//30 min
60 * 60000 //60 min
};
SimpleTransientPopup* m_simplePopup;
void paintSelRange(wxDC& dc, int width_sample);
public:
wxTopActivity(wxWindow* parent, WaitSample* WS, wxSize sz);
wxSize DoGetBestClientSize();
int getAggregateInterval() { return m_agg_int; };
void setViewRange(int m_aggregate_interval, int RightTime);
WaitSample* getViewRange(int& m_aggregate_interval, int& RightTime);
void paintEvent(wxPaintEvent& evt);
void OnEraseBackground(wxEraseEvent& event);
void paintNow();
void SetFilter(long long qid);
/// <summary>
/// Возвращает время указаной границы
/// </summary>
/// <param name="isLeftRange">true для левой границы, инаце правая</param>
///
int getTimeSelRange(bool isLeftRange);
void render(wxDC& dc);
// some useful events
void mouseMoved(wxMouseEvent& event);
void mouseDown(wxMouseEvent& event);
void mouseWheelMoved(wxMouseEvent& event);
void mouseReleased(wxMouseEvent& event);
void rightClick(wxMouseEvent& event);
void mouseLeftWindow(wxMouseEvent& event);
void keyPressed(wxKeyEvent& event);
void keyReleased(wxKeyEvent& event);
void resize(wxSizeEvent& event);
DECLARE_EVENT_TABLE()
};
class topDataViewCtrl :
public wxDataViewCtrl
{
public:
topDataViewCtrl(wxWindow* parent, wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = 0,
wxTopActivity* topactive = NULL,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxASCII_STR(wxDataViewCtrlNameStr)
) : wxDataViewCtrl(parent, id, pos, size, style, validator, name)
{
//SetMinSize(wxSize(300,200));
top = topactive;
w = NULL;
m_win_s = NULL;
w = top->getViewRange(agg, right_g);
Bind(wxEVT_DATAVIEW_COLUMN_HEADER_CLICK, [&](wxDataViewEvent& event) {
int col = event.GetColumn();
if (GetColumn(col)->GetTitle() == "PID") {
ignoreBG = !ignoreBG;
CalcRowsDataView(true, ispidfirst);
Refresh();
}
});
Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, [&](wxDataViewEvent& event) {
int col = event.GetColumn();
wxDataViewItem item(event.GetItem());
long row = wxPtrToUInt(item.GetID());
row--;
if (col == 1) //qid
{
MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
key3 k = m->GetRowValue(row);
if (k.qid != -1) top->SetFilter(k.qid);
}
if (col == 0) { //pid
MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
key3 k = m->GetRowValue(row);
if (k.pid > 0) s_pid_HIGHLIGH = k.pid;
// else
// s_pid_HIGHLIGH = -1;
}
});
Bind(wxEVT_DATAVIEW_COLUMN_REORDERED, [&](wxDataViewEvent& event) {
wxDataViewColumn* const col = event.GetDataViewColumn();
if (!col)
{
return;
}
ispidfirst = col->GetTitle() == "PID" && event.GetColumn() == 0;
if (!ispidfirst) ispidfirst = col->GetTitle() == "QID" && event.GetColumn() == 1;
CalcRowsDataView(true, ispidfirst);
Refresh();
if (m_win_s != NULL) m_win_s->Refresh();
});
Bind(wxEVT_CHAR, [&](wxKeyEvent& event) {
bool fnd = false;
wxChar charcode = event.GetUnicodeKey();
int l = m_find.length();
if (event.GetKeyCode() == WXK_ESCAPE) {
//GetParent()->Close(true);
m_find = "";
if (m_win_s != NULL) {
m_win_s->Destroy();
m_win_s = NULL;
}
}
else
if (event.GetKeyCode() == WXK_F3) {
//GetParent()->Close(true);
fnd = true;
}
else
if (event.GetKeyCode() == WXK_BACK) {
if (l > 0) m_find.RemoveLast();
//m_win_s->SetValue(m_find);
fnd = true;
}
else
if (wxIsprint(charcode))
{
//txt->EmulateKeyPress(event);
m_find += charcode;
fnd = true;
}
//else
// event.Skip();
if (fnd) {
if (m_win_s == NULL && m_find.length() > 0) {
wxRect r;
r.width = GetColumn(0)->GetWidth();
#ifdef __WXGTK__
r.height = GTKGetUniformRowHeight();
#endif
wxWindow* t = this;
#ifdef WIN32
//wxWindow* t = this->GenericGetHeader();
r.height = this->GenericGetHeader()->GetSize().GetHeight();
#else
#endif
m_win_s = new wxTextCtrl(t, wxID_ANY, "",
r.GetPosition(),
r.GetSize(),
wxTE_PROCESS_ENTER
| wxTE_READONLY
);
m_win_s->SetInsertionPointEnd();
}
if (m_win_s != NULL) {
m_win_s->SetValue(m_find); m_win_s->Refresh();
}
if (m_find.length() > 0) {
MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
wxDataViewItem item(GetCurrentItem());
long row = wxPtrToUInt(item.GetID());
if (!(row < m->GetCount())) row = 0;
while (row != -1 && row < m->GetCount()) {
key3 k = m->GetRowValue(row);
wxString v = wxString::Format("%d,%llx", k.pid, k.qid);
// m->GetValueByRow(v, row, 0);
bool isfind = v.Contains(m_find);
if (isfind) {
wxDataViewItem it = wxDataViewItem(wxUIntToPtr(row + 1));
SetCurrentItem(it);
EnsureVisible(it);
return;
}
row++;
}
}
return;
}
// end function
event.Skip();
});
GetMainWindow()->Bind(wxEVT_MOTION, [&](wxMouseEvent& event) {
if (event.Dragging())
return;
wxClientDC dc(GetMainWindow());
PrepareDC(dc);
//wxPoint logPos(event.GetLogicalPosition(dc));
//wxPoint mc= GetMainWindow()->ScreenToClient(event.GetPosition());
wxPoint mc = event.GetPosition();
wxString position;
//position = wxString::Format("x=%d y=%d", logPos.x, logPos.y);
//wxLogMessage("Mouse pos %s", position);
int dy = 0;
wxSize sz;
#ifdef WIN32
wxHeaderCtrl* const header = GenericGetHeader();
if (header) {
sz = header->GetSize();
//header->Refresh();
dy = sz.GetHeight();
}
mc.y += dy;
#else
mc.y += 18; // only linux compile
#endif
wxDataViewItem item;
wxDataViewColumn* column;
HitTest(mc, item, column);
if (item != NULL && column != NULL)
{
int row = wxPtrToUInt(item.GetID()) - 1;
int ncol = column->GetModelColumn();
if (row >= 0) {
if (column && column->GetModelColumn() == 2) {
wxVariant vr;
vr = "gethint";
if ((lastcol != ncol) || (lastrow != row)) {
MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
m->GetValueByRow(vr, row, ncol);
GetMainWindow()->SetToolTip(vr.GetString());
lastrow = row;
lastcol = ncol;
}
}
else if (column && column->GetModelColumn() == 0) {
// PID
MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
key3 k = m->GetRowValue(row);
if (k.sum > 0) {
if (((lastcol != ncol) || (lastrow != row))) {
wxString s = w->GetBackendTypeName(k.sum);
GetMainWindow()->SetToolTip(s);
lastrow = row;
lastcol = ncol;
}
}
else
{
GetMainWindow()->UnsetToolTip(); lastcol = -1;
}
}
else if (column && column->GetModelColumn() == 1) {
//qid
MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
key3 k = m->GetRowValue(row);
if (k.qid != 0) {
if (((lastcol != ncol) || (lastrow != row))) {
wxString s = w->GetQueryByQid(k.qid);
if (s.IsEmpty()) s = "not found sql text";
GetMainWindow()->SetToolTip(s);
lastrow = row;
lastcol = ncol;
}
}
else
{
GetMainWindow()->UnsetToolTip(); lastcol = -1; lastrow = -1;
}
}
else
{
GetMainWindow()->UnsetToolTip();
lastcol = -1;
}
}
}
else
{
GetMainWindow()->UnsetToolTip();
lastcol = -1; ; lastrow = -1;
}
});
}
WaitSample* GetSample() { return w; }
void BuildColumn(int mode);
bool CalcRowsDataView(bool force, bool IsPidFirst);
//void AddRow(wxString csvtext);
//void OnMouseMove(wxMouseEvent& event);
//void OnMouseDown(wxMouseEvent& event);
//void OnKEY_DOWN(wxKeyEvent& event);
//void OnKEY_UP(wxKeyEvent& event);
//void OnEVT_DATAVIEW_COLUMN_HEADER_CLICK(wxDataViewEvent& event);
//void OnEVT_DATAVIEW_CONTEXT_MENU(wxCommandEvent& event);
//void OnEVT_DATAVIEW_SELECTION_CHANGED(wxDataViewEvent& event);
//void OnContextMenu(wxDataViewEvent& event);
DECLARE_EVENT_TABLE()
private:
int lastrow = -1, lastcol = 1;
int agg, right_g, left_g;
bool ignoreBG = false;
bool ispidfirst = true;
wxString m_find;
wxTextCtrl* m_win_s;
wxTopActivity* top;
WaitSample* w;
};
#undef sepListWait