Extended hints

В результатах запроса, нажатием правой кнопки мыши можно вызвать окно
подсказки, с возможностью выделения содержимого и его копирования (Rbutton).
This commit is contained in:
lsv 2025-07-31 16:14:09 +05:00 committed by lsv
parent c2c44c18f1
commit 077de1ad7c
17 changed files with 4789 additions and 3946 deletions

View file

@ -25,11 +25,19 @@ private:
void OnSortGrid(wxListEvent& event);
bool nosort; // если кто то пользуется SetItemData то не будем сортировать такие ctlListView
int order, prev_col;
// будем сохранять длинные строки 0 колонки в этом массиве
bool storelongstring = false;
std::vector<wxString> longstring;
public:
bool SetItemData(long item, long data) {
nosort = true;
return wxListView::SetItemData(item, data);
}
void SetModeStoreLongString() { storelongstring = true; }
bool DeleteAllItems() {
longstring.clear();
return wxListView::DeleteAllItems();
}
ctlListView(wxWindow* p, int id, wxPoint pos, wxSize siz, long attr = 0);
long GetSelection();
wxString GetText(long row, long col = 0);

View file

@ -68,6 +68,7 @@ public:
int GetIndexColor(const wxColour &color) const;
void ClearMark();
void RowVisibleCenter(long row);
void SetFindString(const wxString &findstr);
int GetCountMark();
wxMenu* GetPopupMenu();
bool RunKeyCommand(wxKeyEvent& event,int numCmd=-1);

View file

@ -53,7 +53,7 @@ public:
void Create(wxWindow *parent, wxWindowID id = -1, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = 0);
void HighlightBrace(int lb, int rb);
void SetDatabase(pgConn *db);
wxString TextToHtml(int start, int end);
wxString TextToHtml(int start, int end, bool isAddNewLine=false);
void Copy();
void OnKeyDown(wxKeyEvent &event);
void OnAutoComplete(wxCommandEvent &event);

View file

@ -15,6 +15,8 @@
// wxWindows headers
#include <wx/grid.h>
#include <wx/brush.h>
#include "utils/popuphelp.h"
class GroupRows;
class ctlSQLGrid : public wxGrid
@ -52,6 +54,8 @@ public:
void OnGridSelectCell(wxGridEvent& evt);
void OnLabelClick(wxGridEvent& event);
void OnCellRightClick(wxGridEvent& event);
void OnMouseEvent(wxMouseEvent& event);
void OnShowPopup(wxThreadEvent& event);
bool FullArrayCollapseRowsPlan(bool clear);
void AutoSizeColumn(int col, bool setAsMin = false, bool doLimit = true);
void AutoSizeColumns(bool setAsMin);
@ -83,7 +87,11 @@ private:
// Max size for each column
wxArrayInt colMaxSizes;
bool isSort;
// viewr
popuphelp* m_Popup = NULL;
int rcol, rrow;
wxPoint rpos;
//FunctionPGHelper fh;
};
class GroupRows

View file

@ -63,12 +63,14 @@ enum
MNU_QUERYSTATETRIGGER,
MNU_WAITENABLE,
MNU_WAITSAVE,
CMD_EVENT_FIND_STR,
TIMER_REFRESHUI_ID,
TIMER_STATUS_ID,
TIMER_LOCKS_ID,
TIMER_XACT_ID,
TIMER_LOG_ID,
TIMER_QUERYSTATE_ID
TIMER_QUERYSTATE_ID,
TIMER_LOGHINT_ID
};
@ -80,9 +82,6 @@ enum
PANE_LOG,
PANE_QUERYSTATE
};
//
// This number MUST be incremented if changing any of the default perspectives
//
#define FRMSTATUS_PERSPECTIVE_VER wxT("8275")
@ -241,6 +240,7 @@ private:
wxTimer *refreshUITimer;
wxTimer *statusTimer, *locksTimer, *xactTimer, *logTimer, *querystateTimer;
wxTimer *delayHitLog;
int statusRate, locksRate, xactRate, logRate, querystateRate;
ctlListView *statusList;
@ -261,6 +261,9 @@ private:
wxArrayString filterValue;
int statusColWidth[12], lockColWidth[10], xactColWidth[5], querystateColWidth[5];
popuphelp* m_Popup = NULL;
long lastlogitem = -1, lastlogitemShow=-1;
wxPoint lastmouse;
int cboToRate();
wxString rateToCboString(int rate);
@ -288,6 +291,8 @@ private:
void OnToggleWaitEnable(wxCommandEvent& event);
void OnEmptyAction(wxCommandEvent &event);
void OnLogContextMenu(wxCommandEvent& event);
void OnMoveMouseLog(wxMouseEvent& event);
void OnTimerHintLog(wxTimerEvent& event);
void OnToggleToolBar(wxCommandEvent &event);
void OnDefaultView(wxCommandEvent &event);
@ -348,6 +353,7 @@ private:
void OnLogKeyUp(wxKeyEvent& event);
void OnAddLabelTextThread(wxThreadEvent& event);
void ActivatePane(wxString name);
void OnCmdFindStrLog(wxCommandEvent& event);
void OnChangeDatabase(wxCommandEvent &ev);

View file

@ -155,10 +155,11 @@ public:
int getCountGroup(int row);
int GetTotalCountGroup(int rowfilter);
wxArrayString GetAllFields(int row, bool isfilter);
// use for parse csv log file
static Line getLineParse(const wxString& str, bool csv = false, const wxString& host=wxEmptyString);
static wxString get_field(Line& l, MyConst::colField col);
private:
bool checkFilter(Line& l);
Line getLineParse(const wxString& str, bool csv = false);
wxString get_field(Line& l, MyConst::colField col);
LineFilter getLineFilter(wxString strflt,wxString fn);
void getLineToCache(int row, bool filter = true);
bool CompareFilterLine(int row, bool filter);

View file

@ -9,12 +9,27 @@
#include <wx/textfile.h>
#include <wx/filename.h>
extern sysSettings *settings;
extern sysSettings* settings;
class FunctionPGHelper
{
public:
FunctionPGHelper() {};
/// <summary>
/// Создать только переданный в конструкторе html текст с именем "content"
/// </summary>
/// <param name="content"></param>
FunctionPGHelper(const wxString& content) {
body.clear();
Add("content", content);
isload = true;
};
int Size() {
return body.size();
}
void SetTimerClose(int ms) { m_interval = ms; }
int GetTimerClose() { return m_interval; }
void Add(const wxString& key, const wxString& v) { body.emplace(key, v); }
wxString getHelpString(wxString fnd, bool isPart = true) {
if (!isValid()) return wxEmptyString;
auto search = body.find(fnd);
@ -46,9 +61,9 @@ public:
wxString getSqlCommandHelp(wxString fnd) {
wxUniChar sep = wxFileName::GetPathSeparator();
fnd.Replace(" ", "");
wxString f = wxFindFirstFile(path + sep+"sql-"+fnd+"*.html");
wxString last,txt;
wxString f = wxFindFirstFile(path + sep + "sql-" + fnd + "*.html");
wxString last, txt;
int c = 0;
while (!f.empty())
{
@ -61,7 +76,7 @@ public:
if (last.empty()) {
return wxEmptyString;
}
else if (c==1) {
else if (c == 1) {
return getHelpFile(last);
}
else {
@ -100,6 +115,7 @@ public:
}
private:
bool isload = false;
int m_interval = -1;
wxString path;
std::map<wxString, wxString> body;
void loadfile() {

149
include/utils/PreviewHtml.h Normal file
View file

@ -0,0 +1,149 @@
#pragma once
#include <wx/wx.h>
#define PREVIEW_SEP 1
#define PREVIEW_DIGITS 2
#define PREVIEW_WORD 4
#define PREVIEW_SPACE 8
#define PREVIEW_ENDFIELD 16
#define PREVIEW_ENDROW 32
#define PREVIEW_QUOTE 64
#define CHKFLAG(val,par) ((val & par)>0)
enum class fmtpreview {
AUTO, AUTOVACCUM,CSV
};
struct Element {
wxString src;
wxString html;
int flags = 0;
} ;
static wxString titles_log[] = {
L"log_time",
L"user_name",
L"database_name",
L"process_id",
L"connection_from",
L"session_id",
L"session_line_num",
L"command_tag",
L"session_start_time",
L"virtual_transaction_id",
L"transaction_id",
L"error_severity",
L"sql_state_code",
L"message",
L"detail",
L"hint",
L"internal_query",
L"internal_query_pos",
L"context",
L"query",
L"query_pos",
L"location",
L"application_name",
L"backend_type",
L"leader_pid",
L"query_id"
};
class PreviewHtml
{
public:
//void SetColors();
PreviewHtml() { InitColor(); };
wxString Preview(const wxString& txt, fmtpreview type);
private:
void InitColor();
bool saveTokenIfNotEmpty(wxString& savestr, int flag) {
if (savestr.Length() > 0) {
wxString tmp = savestr;
tmp=escapeHtml(tmp,true);
tmp.Replace(" ", "&nbsp;");
if (CHKFLAG(flag, PREVIEW_ENDROW)) tmp = "<br>";
if (CHKFLAG(flag, PREVIEW_DIGITS)) {
int l = savestr.Length();
if (l > 4 && !savestr.Contains('.')) {
int dl = 3;
wxString fmt;
std::vector<wxString> dd;
bool smalll = true;
while (l > 0) {
l = l - dl;
if (l < 0) {
dl = dl + l;
l = 0;
}
wxString d3 = savestr.Mid(l, dl);
if (smalll)
dd.push_back("<font size=+1>" + d3 + "</font>");
else
dd.push_back(d3);
smalll = !smalll;
}
for (auto i = dd.rbegin(); i != dd.rend(); i++)
{
fmt += *i;
}
tmp = fmt;
}
wxString t = wxString::Format("<font color=\"%s\">%s</font>", numcolor, tmp);
tmp = t;
}
if (CHKFLAG(flag, PREVIEW_QUOTE)) {
wxString t = wxString::Format("<font color=\"%s\">%s</font>",quotecolor,tmp);
tmp = t;
}
tokens.push_back({savestr,tmp,flag});
savestr = "";
return true;
}
return false;
}
int FindElement(int start_pos, int flag_find, wxString& value_find, int is_what_find) {
int p = start_pos;
int rez = -1;
while (p < tokens.size()) {
Element t = tokens[p];
bool f1 = is_what_find & 1;
bool f2 = is_what_find & 2;
bool r1 = false;
bool r2 = false;
if (f1 && CHKFLAG(t.flags, flag_find)) {
r1 = true;
}
if (f2 && t.src==value_find ) {
r2 = true;
}
if ((is_what_find == 3 && r1 && r2)
|| (is_what_find == 2 && r2)
|| (is_what_find == 1 && r1)
) {
rez = p;
break;
}
p++;
}
return rez;
}
wxString generateHtml() {
wxString s;
for (size_t i = 0; i < tokens.size(); i++) {
Element t = tokens[i];
s += t.html;
}
// s = "<html><body BGCOLOR=\"" + bgcolor + "\">" + s + "</body></html>";
// s=wxString::Format("<html><body BGCOLOR=\"%s\" FGCOLOR=\"%s\"><font face=\"%s\" size=\"%d\">%s</font></body></html>", bgcolor,fgcolor,fname,fsize, s);
return s;
}
std::vector<Element> tokens;
// colors
wxString bgcolor, numcolor, fgcolor, quotecolor;
wxString fname;
int fsize;
};

View file

@ -8,27 +8,58 @@
#include <wx/regex.h>
#include <map>
#include <vector>
#include "wx/display.h"
class popuphelp :
public wxPopupTransientWindow
public wxPopupTransientWindow
{
public:
//popuphelp(wxWindow* parent);
bool ProcessLeftDown(wxMouseEvent& event)
{
return false;
}
bool IsValid() {
return isvalid;
}
popuphelp(wxWindow* parent,wxString keyword, FunctionPGHelper *hhelper) : wxPopupTransientWindow(parent) {
SetSize(450,370);
this->hhelper = hhelper;
SetBackgroundColour(*wxBLACK);
htmlWindow = new wxHtmlWindow(this, -1, wxDefaultPosition,GetSize());
htmlWindow->SetRelatedStatusBar(0);
//htmlWindow->SetPage("<html><body><h1>TEST</h1><span fgcolor=\"#332233\">Set Page Works</span></body></hmtl>");
wxString txt = hhelper->getHelpString(keyword);
~popuphelp() {
delete closeTimer;
}
//popuphelp(wxWindow* parent);
bool ProcessLeftDown(wxMouseEvent& event)
{
return false;
}
bool IsValid() {
return isvalid;
}
void SetSizePopup(const wxSize& sz) {
SetSize(sz);
Layout();
Fit();
sizew = sz;
}
wxSize GetSizePopup() { return sizew; }
popuphelp(wxWindow* parent,wxString keyword, FunctionPGHelper *hhelper,wxPoint &posit,wxSize &newSz) : wxPopupTransientWindow(parent) {
wxSize top_sz(newSz);
sizew = top_sz;
//SetSize(top_sz);
this->hhelper = hhelper;
SetBackgroundColour(*wxBLACK);
extern sysSettings* settings;
wxFont fnt = settings->GetSQLFont();
int size = fnt.GetPointSize();
SetFont(fnt);
htmlWindow = new wxHtmlWindow(this, -1, wxDefaultPosition, sizew);
wxFont font(size, wxFontFamily::wxFONTFAMILY_MODERN, wxFontStyle::wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
wxString fixf = font.GetFaceName();
wxString scalf = fnt.GetFaceName();
int f_sizes[7];
f_sizes[0] = int(size -4);
f_sizes[1] = int(size -2);
f_sizes[2] = size;
f_sizes[3] = int(size +1);
f_sizes[4] = int(size +3);
f_sizes[5] = int(size +5);
f_sizes[6] = int(size +7);
htmlWindow->SetFonts(scalf, fixf, f_sizes);
htmlWindow->SetRelatedStatusBar(0);
//htmlWindow->SetPage("<html><body><h1>TEST</h1><span fgcolor=\"#332233\">Set Page Works</span></body></hmtl>");
wxString txt = hhelper->getHelpString(keyword);
if (txt.IsEmpty()) {
txt = hhelper->getSqlCommandHelp(keyword);
@ -38,47 +69,80 @@ public:
}
}
SetPage(txt);
//wxSize sz= htmlWindow->GetSize();
//sz = htmlWindow->GetBestSize();
//htmlWindow->SetHTMLBackgroundImage(wxBitmapBundle::FromSVGFile("data/bg.svg", wxSize(65, 45)));
wxBoxSizer* topsizer;
topsizer = new wxBoxSizer(wxVERTICAL);
//htmlWindow->SetSize(htmlWindow->GetInternalRepresentation()->GetWidth(), htmlWindow->GetInternalRepresentation()->GetHeight());
//wxSize sz= htmlWindow->GetSize();
//sz = htmlWindow->GetBestSize();
//htmlWindow->SetHTMLBackgroundImage(wxBitmapBundle::FromSVGFile("data/bg.svg", wxSize(65, 45)));
wxBoxSizer* topsizer;
topsizer = new wxBoxSizer(wxVERTICAL);
//htmlWindow->SetInitialSize(wxSize(htmlWindow->GetInternalRepresentation()->GetWidth(), htmlWindow->GetInternalRepresentation()->GetHeight()));
//htmlWindow->SetInitialSize(wxSize(htmlWindow->GetInternalRepresentation()->GetWidth(), htmlWindow->GetInternalRepresentation()->GetHeight()));
//SetSize(wxSize(300,150));
topsizer->Add(htmlWindow, 1, wxALL, 1);
//SetSize(wxSize(300,150));
topsizer->Add(htmlWindow, 1, wxALL, 1);
//wxButton* bu1 = new wxButton(this, wxID_OK, _("OK"));
//bu1->SetDefault();
//wxButton* bu1 = new wxButton(this, wxID_OK, _("OK"));
//bu1->SetDefault();
//topsizer->Add(bu1, 0, wxALL | wxALIGN_RIGHT, 15);
//topsizer->Add(bu1, 0, wxALL | wxALIGN_RIGHT, 15);
SetSizer(topsizer);
topsizer->Fit(this);
SetSizer(topsizer);
topsizer->Fit(this);
int xx, yy;
htmlWindow->GetVirtualSize(&xx, &yy);
//wxSize sz = GetSize();
wxPoint posScreen;
wxSize sizeScreen;
const int displayNum = wxDisplay::GetFromPoint(posit);
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);
//this->Bind(wxEVT_HTML_CELL_CLICKED, [&](wxHtmlCellEvent& event) {
// wxHtmlCell* c = event.GetCell();
//
// wxString ctext=c->ConvertToText(NULL);
// ctext=htmlWindow->SelectionToText();
// wxString s = wxString::Format("cell = %s",ctext.c_str());
// wxMessageBox(s, "cell", wxOK | wxICON_INFORMATION);
if (xx > sizeScreen.x) xx = sizeScreen.x - 120;
if (yy > sizeScreen.y) yy = sizeScreen.y - 120;
// });
this->Bind(wxEVT_HTML_LINK_CLICKED, [&](wxHtmlLinkEvent& event) {
wxHtmlLinkInfo i = event.GetLinkInfo();
wxString name = i.GetHref();
wxString body=this->hhelper->getHelpString(name);
if (xx > top_sz.x || yy > top_sz.y) {
int dx = 0;
if (htmlWindow->IsScrollbarShown(wxHORIZONTAL)) dx=htmlWindow->GetScrollThumb(wxHORIZONTAL);
SetSizePopup(wxSize(xx+dx, yy+5));
}
//this->Bind(wxEVT_HTML_CELL_CLICKED, [&](wxHtmlCellEvent& event) {
// wxHtmlCell* c = event.GetCell();
//
// wxString ctext=c->ConvertToText(NULL);
// ctext=htmlWindow->SelectionToText();
// wxString s = wxString::Format("cell = %s",ctext.c_str());
// wxMessageBox(s, "cell", wxOK | wxICON_INFORMATION);
// });
this->Bind(wxEVT_HTML_LINK_CLICKED, [&](wxHtmlLinkEvent& event) {
wxHtmlLinkInfo i = event.GetLinkInfo();
wxString name = i.GetHref();
wxString body=this->hhelper->getHelpString(name);
if (body.IsEmpty()) {
body = this->hhelper->getHelpFile(name);
}
SetPage(body);
//ctext=htmlWindow->SelectionToText();
//wxString s = wxString::Format("cell = %s",ctext.c_str());
});
SetPage(body);
//ctext=htmlWindow->SelectionToText();
//wxString s = wxString::Format("cell = %s",ctext.c_str());
});
htmlWindow->Bind(wxEVT_RIGHT_UP, [&](wxMouseEvent& event) {
wxString name;
wxLongLong e = wxGetLocalTimeMillis();
if (e - startTimeWin < 150)
return;
//wxString body = this->hhelper->getHelpString(name);
wxString ctext = htmlWindow->SelectionToText();
if (!ctext.IsEmpty()) {
@ -88,6 +152,17 @@ public:
{
}
wxString wname = GetParent()->GetName();
if (wname == "frmStatus") {
//CMD_EVENT_FIND_STR
wxCommandEvent event(wxEVT_MENU, 281);
event.SetEventObject(this);
// Give it some contents
event.SetString(ctext);
// Do send it
GetParent()->ProcessWindowEvent(event);
}
//statusBar->SetStatusText(wxString::Format(" TIMER COUNT %d x:%d,y:%d", count, m.x, m.y));
Hide();
return;
}
@ -95,21 +170,43 @@ public:
//ctext=htmlWindow->SelectionToText();
//wxString s = wxString::Format("cell = %s",ctext.c_str());
});
}
startTimeWin = wxGetLocalTimeMillis();
int inter = hhelper->GetTimerClose();
if (inter != -1) {
closeTimer = new wxTimer(this);
Bind(wxEVT_TIMER, [&](wxTimerEvent& event) {
closeTimer->Stop();
wxPoint pm = this->GetScreenPosition();
wxRect rc = this->GetSize();
wxPoint m = wxGetMousePosition();
rc.x = pm.x;
rc.y = pm.y;
if (!rc.Contains(m)) {
Hide();
return;
}
});
closeTimer->StartOnce(inter);
}
}
private:
bool isvalid = true;
wxHtmlWindow* htmlWindow;
FunctionPGHelper* hhelper;
bool isvalid = true;
wxHtmlWindow* htmlWindow;
wxLongLong startTimeWin;
wxSize sizew;
FunctionPGHelper* hhelper;
std::vector<wxString> hist;
void SetPage(wxString innerbody,bool gethistory=false) {
void SetPage(wxString innerbody,bool gethistory=false) {
wxString h;
int p = innerbody.Find("<body>");
if (p > -1) {
innerbody.Replace("<body>", "<html><body TEXT=\"#000000\" BGCOLOR=\"#FFFFA0\" LINK=\"#0000FF\" VLINK=\"#FF0000\" ALINK=\"#000088\">", false);
h = "" + innerbody + "";
} else
h = "<html><body TEXT=\"#000000\" BGCOLOR=\"#FFFFA0\" LINK=\"#0000FF\" VLINK=\"#FF0000\" ALINK=\"#000088\">" + innerbody + "</body></hmtl>";
if (innerbody.Find("<html>")>=0) h = innerbody;
else
if (p > -1) {
innerbody.Replace("<body>", "<html><body TEXT=\"#000000\" BGCOLOR=\"#FFFFE0\" LINK=\"#0000FF\" VLINK=\"#FF0000\" ALINK=\"#000088\">", false);
h = "" + innerbody + "";
}
else
h = "<html><body TEXT=\"#000000\" BGCOLOR=\"#FFFFE0\" LINK=\"#0000FF\" VLINK=\"#FF0000\" ALINK=\"#000088\">" + innerbody + "</body></hmtl>";
if (gethistory) {
if (hist.size() < 2) {
@ -122,7 +219,9 @@ private:
else {
hist.push_back(h);
}
htmlWindow->SetPage(h);
}
htmlWindow->SetPage(h);
}
private:
wxTimer *closeTimer=NULL;
};
#endif