pgadmin3/frm/frmLog.cpp
2026-03-02 10:24:40 +05:00

1345 lines
44 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/regex.h>
#include <wx/xrc/xmlres.h>
#include <wx/image.h>
#include <wx/textbuf.h>
#include <wx/sysopt.h>
// wxAUI
#include <wx/aui/aui.h>
// App headers
#include "frm/frmMain.h"
#include "frm/frmLog.h"
#include "db/pgConn.h"
#include "utils/pgfeatures.h"
#include "schema/pgServer.h"
#include "schema/pgUser.h"
#include "ctl/ctlMenuToolbar.h"
#include "ctl/ctlAuiNotebook.h"
#include "utils/csvfiles.h"
#include "log/StorageModel.h"
#include "utils/utffile.h"
#include "utils/misc.h"
#include <wx/arrimpl.cpp>
#ifdef WIN32
#include <wx/msw/ole/automtn.h>
#endif
WX_DEFINE_OBJARRAY(RemoteConnArray2);
wxBEGIN_EVENT_TABLE(frmLog, pgFrame)
// test
EVT_MENU(MNU_FIND_TEXT, frmLog::OnFind)
EVT_MENU(MNU_SEND_MAIL, frmLog::OnSendMail)
EVT_CHECKBOX(ID_SET_GROUP, frmLog::OnSetGroup)
EVT_CHECKBOX(ID_SET_DETAILGROUP, frmLog::OnSetDetailGroup)
EVT_BUTTON(ID_CLEAR_ALL_FILTER, frmLog::OnClearAllFilter)
EVT_BUTTON(ID_ADD_FILTER, frmLog::OnAddFilterIgnore)
EVT_BUTTON(ID_ADD_UFilter, frmLog::OnAddUFilter)
EVT_BUTTON(ID_DEL_UFilter, frmLog::OnDelUFilter)
EVT_BUTTON(ID_EXTENDED_LOG, frmLog::OnExtendedLog)
EVT_TEXT_ENTER(ID_COUNT_FILES, frmLog::OnTextChange)
EVT_BUTTON(ID_HELP_LOG, frmLog::OnHelp)
EVT_COMBOBOX(ID_CBOX_UFilter, frmLog::OnChangeUFilter)
EVT_COMBOBOX(ID_CBOX_SMART, frmLog::OnChangeSmart)
EVT_SET_FOCUS(frmLog::OnSetFocus)
EVT_CLOSE(frmLog::OnClose)
EVT_KILL_FOCUS(frmLog::OnKillFocus)
EVT_ACTIVATE(frmLog::OnActivate)
wxEND_EVENT_TABLE()
//#include <wx/arrimpl.cpp>
//WX_DEFINE_OBJARRAY(RemoteConnArray);
void frmLog::OnActivate(wxActivateEvent& event) {
m_storage_model->getStorage()->SetErrMsgFlag(false);
seticon(false);
event.Skip();
}
void frmLog::OnSetFocus(wxFocusEvent& event) {
m_storage_model->getStorage()->SetErrMsgFlag(false);
seticon(false);
}
void frmLog::OnKillFocus(wxFocusEvent& event) {
m_storage_model->getStorage()->SetErrMsgFlag(false);
seticon(false);
}
void frmLog::OnClose(wxCloseEvent& event) {
if (event.CanVeto()&& detail->IsChecked())
{
my_view->setGroupMode(true);
detail->SetValue(false);
//detail->Enable(event.IsChecked());
event.Veto();
return;
}
DBthread->DoTerminate();
while (!DBthread->isReadyRows()) {
wxThread::Sleep(50);
wxYield();
}
DBthread->GoReadRows();
wxThread::Sleep(50);
event.Skip();
}
// Class declarations
void frmLog::OnClearAllFilter(wxCommandEvent& event) {
my_view->ClearAllFilter(false);
}
void frmLog::OnAddFilterIgnore(wxCommandEvent& event) {
wxString Fname = "LoadSkip";
my_view->ModUserFilter(Fname, "AddUserFilter", listUserFilter, contentFilter);
//my_view->AddFilterIgnore(Fname);
}
void frmLog::OnAddUFilter(wxCommandEvent& event) {
wxString txt = listUserFilter->GetValue();
my_view->ModUserFilter(txt, "AddUserFilter", listUserFilter, contentFilter);
}
void frmLog::OnDelUFilter(wxCommandEvent& event) {
my_view->ModUserFilter("", "RemoveFilter", listUserFilter, contentFilter);
}
void frmLog::OnChangeUFilter(wxCommandEvent& event) {
if (event.GetSelection() >= 0) {
my_view->ModUserFilter("", "ChangeFilter", listUserFilter, contentFilter);
}
}
void frmLog::OnExtendedLog(wxCommandEvent& event) {
//my_view->ModUserFilter("", "RemoveFilter", listUserFilter, contentFilter);
StorageModel* m = dynamic_cast<StorageModel*>(my_view->GetModel());
m->DeleteAll();
bool b = true;
b=group->GetValue();
my_view->setGroupMode(b);
detail->SetValue(false);
my_view->ViewGroup(false);
}
void frmLog::OnTextChange(wxCommandEvent& event) {
//my_view->ModUserFilter("", "RemoveFilter", listUserFilter, contentFilter);
wxTextCtrl* t = (wxTextCtrl*)event.GetEventObject();
wxString text = t->GetValue();
int count = 1;
if (wxSscanf(text, "%d", &count) > 0) {
bool isOk=DBthread->SetLimitFiles(count);
OnExtendedLog(event);
};
}
void frmLog::OnChangeSmart(wxCommandEvent& event) {
int n = event.GetSelection();
if (n >= 0) {
listUserFilter->SetSelection(n);
my_view->ModUserFilter("", "ChangeFilter", listUserFilter, contentFilter);
}
}
void frmLog::OnSendMail(wxCommandEvent& event) {
//wxMessageBox("send mail");
wxDataViewItem item;
item = my_view->GetCurrentItem();
if (!item.IsOk() ) {
return;
}
wxVariant v, t;
StorageModel* m = dynamic_cast<StorageModel*>(my_view->GetModel());
Storage* st = m->getStorage();
wxArrayString a;
int r = m->GetRow(item);
if (r != -1) {
a=st->GetAllFields(r, st->IsFilter());
}
else return;
wxString str = "";
#ifdef DEBUG
wxString fn = "C:\\Users\\lsv\\Source\\Repos\\pgadmin64\\pgadmin\\x64\\Debug_(3.0)\\testhtml.txt" ;
#else
wxString fn = "mail.template";
#endif // DEBUG
wxUtfFile file(fn, wxFile::read, wxFONTENCODING_UTF8);
if (file.IsOpened())
{
file.Read(str);
file.Close();
wxStringTokenizer tk(str, "\n", wxTOKEN_DEFAULT);
wxString cc;
wxString to;
wxString html;
wxString templat;
while (tk.HasMoreTokens())
{
wxString l = tk.GetNextToken();
if (l.StartsWith("Cc=")) { cc = l.After('='); continue; }
if (l.StartsWith("To=")) { to = l.After('='); continue; }
if (l.StartsWith("<tr>")) {
wxString le;
wxString r;
for (int i = 0; i < a.Count(); i++) {
templat = l;
le = escapeHtml(a[i++],false);
r = escapeHtml(a[i],false);
int co = templat.Replace("$1", le);
co += templat.Replace("$2", r);
html.Append(templat);
}
continue;
}
html.Append(l);
}
#ifdef WIN32
wxAutomationObject oObject;
if (oObject.GetInstance("Outlook.Application")) {
wxAutomationObject msg;
wxVariant n[1];
wxString strI;
int i = 0;
strI << i;
n[0] = wxVariant(strI);
bool rez = oObject.GetObject(msg, "CreateItem", 1, n);
if (rez) {
//oObject.PutProperty("Visible", true);
msg.PutProperty("Subject", "Error ");
msg.PutProperty("BodyFormat", 2);
msg.PutProperty("To", to);
//
msg.PutProperty("Cc", cc);
msg.PutProperty("HTMLBody", html);
msg.CallMethod("Display");
}
//oObject.PutProperty("ActiveCell.Font.Bold", @true);
}
#endif
}
}
void frmLog::OnFind(wxCommandEvent& event) {
}
void frmLog::OnHelp(wxCommandEvent& event) {
wxMessageBox(wxString::FromUTF8("Для включения фильтра нужно:\n"
" Щелкнуть правой кнопкой мыши по полю.Для инверсии фильтра нужно удерживать Ctrl.\n"
" Выбрать значение в контекстном меню заголовка колонки.\n"
" Там отображаются 20 самых частых значения в колонке с указанием количества этих значений.\n"
" Ввести в поле значения для фильтра, выделить это значение и нажать Enter.\n"
" Для фильтра используется только выделенный текст.\n"
" Такой фильтр будет работать на поиск выделенного вхождения в поле.\n"
" Если в выделенной строке первым символом будет \"!\" то фильтр инверсируется.\n"
"\n"
"Shift+KeyUP,Shift+KeyDOWN - переход на запись с тем же sql_state.\n"
"Alt+KeyUP,Alt+KeyDOWN - переход на запись с другим sql_state.\n"
"Ctrl + S - отправка строки лога по почте Outlook. \n"
" Шаблон письма в файле mail.template в первых двух строках шаблона можно указать адреса которые будут подставляться в письмо.\n"
));
}
void frmLog::OnSetGroup(wxCommandEvent& event)
{
//wxDataViewColumn* const col = m_ctrl[Page_List]->GetColumn(0);
if (event.IsChecked())
{
//wxLogMessage("Group set check");
my_view->setGroupMode(true);
detail->SetValue(false);
}
else {
//wxLogMessage("Group unset check");
my_view->setGroupMode(false);
}
detail->Enable(event.IsChecked());
}
bool frmLog::CheckConn(wxString host,int port) {
for (size_t i = 0; i < conArray.GetCount(); i++)
{
if ((conArray[i].conn->GetHostName() == host)&&(conArray[i].conn->GetPort() == port))
return true;
}
return false;
}
pgConn* frmLog::createConn(pgServer* srv) {
pgConn* conn;
if (!srv->GetConnected()) mainForm->ReconnectServer(srv, false);
conn = srv->CreateConn(wxEmptyString, 0, "Log conn");
return conn;
}
void frmLog::AddNewConn(pgConn* con) {
if (con != NULL) {
if (!con->HasFeature(FEATURE_CSVLOG)) return;
//logfileName.Add("");
//savedPartialLine.Add("");
//logfileLength.Add(0);
//len.Add(0);
conArray.Add(new RemoteConn2(con));
}
}
void frmLog::OnSetDetailGroup(wxCommandEvent& event)
{
//wxDataViewColumn* const col = m_ctrl[Page_List]->GetColumn(0);
if (event.IsChecked())
{
my_view->ViewGroup(true);
}
else {
my_view->ViewGroup(false);
}
}
void* MyThread::Entry() {
while (!m_exit)
{
{
wxMutexLocker lock(s_mutexDBReading);
m_addNewRows.Clear();
m_serversName.Clear();
m_startRowsSevers.Clear();
getFilename();
}
s_goRead.Wait();
if (m_exit) break;
}
return NULL;
}
void MyThread::ResetInfoFiles() {
readfiles.clear();
}
bool MyThread::SetLimitFiles(int limit) {
if (limit != limitfiles) {
limitfiles = limit;
ResetInfoFiles();
return true;
}
return false;
}
wxString MyThread::AppendNewRows(MyDataViewCtrl* my_view, Storage* st) {
//Storage* st = m_storage_model->getStorage();
//int rows = st->getCountStore();
int ra, ri;
st->ClearRowsStat();
my_view->Freeze();
for (size_t i = 0; i < m_startRowsSevers.GetCount(); i++) {
if (m_exit) return "exit";
int sr = m_startRowsSevers[i];
int er = m_addNewRows.GetCount();
if ((i+1)< m_startRowsSevers.GetCount()) er= m_startRowsSevers[i+1];
if (sr == er) continue;
st->SetHost(m_serversName[i]);
for (int k = sr; k < er; k++) {
if (m_exit) return "exit";
wxString ss = m_addNewRows[k];
if (ss.IsEmpty())
continue;
my_view->AddRow(ss);
}
}
st->GetRowsStat(ra, ri);
my_view->Thaw();
//int newrows = st->getCountStore();
//if (loglen !=logfileLength)
if (st->GetErrMsgFlag()) {
m_seticon=true;
}
return wxString::Format("Add rows %d ignore %d. View rows ", ra, ri);
}
void MyThread::getFilename() {
pgSet* set;
namepage = "";
m_seticon = false;
wxString mask = ".csv$";
for (size_t i = 0; i < m_conArray.GetCount(); i++) {
if (m_exit) break;
RemoteConn2* po = (RemoteConn2*) m_conArray[i];
wxString dbname = po->conn->GetDbname();
if (!namepage.IsEmpty()) namepage += ",";
m_serversName.Add(po->conn->GetHostName());
m_startRowsSevers.Add(m_addNewRows.GetCount());
if (!po->conn->IsAlive()) {
wxDateTime n = wxDateTime::Now();
m_seticon = true;
if (po->nextrun < n) {
if (!po->conn->Reconnect(false))
{
wxTimeSpan sp(0, 2);
po->nextrun = wxDateTime::Now() + sp;
namepage += " " + dbname;
continue;
}
}
else {
namepage += " " + dbname;
continue;
}
}
wxString sql = wxString::Format(
"select * from ( \
select current_setting('log_directory') || '/' || name filename, modification filetime, size len \
FROM pg_ls_logdir() where name ~ %s and name !~'db.csv$' ORDER BY modification DESC limit %d) l order by filetime ASC", po->conn->qtDbString(mask), limitfiles
);
set = po->conn->ExecuteSet(sql);
if (set)
{
//logfileTimestamp = set->GetDateTime(wxT("filetime"));
namepage += dbname;
while (!set->Eof()) {
wxString fn = set->GetVal(wxT("filename"));
if (fn.AfterLast(sepPath).CmpNoCase("db.csv")==0) {
set->MoveNext();
continue;
}
wxString key = wxString::Format("%lu_server_%s",i,fn);
info_files inf;
long len= set->GetLong(wxT("len"));
if (auto it = readfiles.find(key); it != readfiles.end()) {
inf = it->second;
}
else {
inf.filename = fn;
inf.readlastposition = 0;
inf.size = len;
}
//len[i] = set->GetLong(wxT("len"));
//m_storage_model->getStorage()->SetHost(m_conArray[i].conn->GetHostName());
//m_startRowsSevers[i] = m_addNewRows.GetCount();
if (inf.readlastposition != len) {
inf.size = len;
readLogFile(inf, po->conn);
readfiles[key] = inf;
}
set->MoveNext();
}
delete set;
}
}
sendText("LOAD OK");
}
void MyThread::readLogFile(info_files &inf,
//wxString logfileName, long& lenfile, long& logfileLength, wxString& savedPartialLine,
pgConn* conn) {
wxString line;
// If GPDB 3.3 and later, log is normally in CSV format. Let's get a whole log line before calling addLogLine,
// so we can do things smarter.
// PostgreSQL can log in CSV format, as well as regular format. Normally, we'd only see
// the regular format logs here, because pg_logdir_ls only returns those. But if pg_logdir_ls is
// changed to return the csv format log files, we should handle it.
wxString logfileName = inf.filename;
long logfileLength = inf.readlastposition;
bool csv_log_format = logfileName.Right(4) == wxT(".csv");
if (csv_log_format && inf.savedPartialLine.length() > 0)
{
if (logfileLength == 0) // Starting at beginning of log file
inf.savedPartialLine.clear();
else
line = inf.savedPartialLine;
}
wxString funcname = "pg_read_binary_file(";
while (inf.size > logfileLength)
{
//statusBar->SetStatusText(_("Reading log from server..."));
if (m_exit) return;
pgSet* set = conn->ExecuteSet(wxT("SELECT ") + funcname +
conn->qtDbString(logfileName) + wxT(", ") + NumToStr(logfileLength) + wxT(", 50000,true)"));
if (!set)
{
conn->IsAlive();
return;
}
if (set->NumRows() == 0 || set->NumCols() ==0 || set->GetVal(0).IsNull()) {
delete set;
return;
}
char* raw1 = set->GetCharPtr(0);
if (!raw1 || !*raw1)
{
delete set;
break;
}
char* raw;
unsigned char m[50001];
if (true) {
raw = (char*)&m[0];
unsigned char c;
unsigned char* startChar;
int pos = 0;
raw1 = raw1 + 2;
int utf8charLen = 0;
while (*raw1 != 0) {
c = *raw1;
c = c - '0';
if (c > 9) c = *raw1 - 'a' + 10;
raw1++;
m[pos] = c << 4;
c = *raw1 - '0';
if (c > 9) c = *raw1 - 'a' + 10;
c = c | m[pos];
m[pos] = c;
// check utf-8 char
if (utf8charLen == 0) {
startChar = &m[pos];
if (c >> 7 == 0)
utf8charLen = 1;
else if (c >> 5 == 0x6)
utf8charLen = 2;
else if (c >> 4 == 0xE)
utf8charLen = 3;
else if (c >> 5 == 0x1E)
utf8charLen = 4;
else
utf8charLen = 0;
// bad utf8 format
}
pos++;
raw1++;
utf8charLen--;
}
//
if (utf8charLen != 0) {
//read = startChar - &m[0];
// remove bad utf-8 char
*startChar = 0;
}
else
m[pos] = 0;
}
else {
raw = raw1;
}
int l = strlen(raw);
logfileLength += l;
wxString host = conn->GetHostName();
//status->SetLabelText(wxString::Format("%s Load bytes %ld", host, logfileLength));
sendText(wxString::Format("%s file: %s Load bytes %ld", host, logfileName, logfileLength));
wxString str;
str = line + wxTextBuffer::Translate(wxString(raw, set->GetConversion()), wxTextFileType_Unix);
//if (wxString(wxString(raw, wxConvLibc).wx_str(), wxConvUTF8).Len() > 0)
// str = line + wxString(wxString(raw, wxConvLibc).wx_str(), wxConvUTF8);
//else {
// str = line + wxTextBuffer::Translate(wxString(raw, set->GetConversion()), wxTextFileType_Unix);
//}
delete set;
if (str.Len() == 0)
{
wxString msgstr = host+" The server log contains entries in multiple encodings and cannot be displayed by pgAdmin.";
//wxMessageBox(msgstr);
sendText(msgstr);
return;
}
if (csv_log_format)
{
// This will work for any DB using CSV format logs
CSVLineTokenizer tk(str);
//my_view->Freeze();
while (tk.HasMoreLines())
{
line.Clear();
bool partial;
str = tk.GetNextLine(partial);
if (partial)
{
line = str; // Start of a log line, but not complete. Loop back, Read more data.
break;
}
// Looks like we have a good complete CSV log record.
//my_view->AddRow(str.Trim());
m_addNewRows.Add(str.Trim());
}
//my_view->Thaw();
}
else
{
}
}
inf.savedPartialLine.clear();
if (!line.IsEmpty())
{
// We finished reading to the end of the log file, but still have some data left
if (csv_log_format)
{
inf.savedPartialLine = line; // Save partial log line for next read of the data file.
line.Clear();
}
else
//my_view->AddRow(line.Trim());
m_addNewRows.Add(line.Trim());
}
inf.readlastposition = logfileLength;
}
void frmLog::OnAddLabelTextThread(wxThreadEvent& event) {
wxString s=event.GetString();
if (s == "LOAD OK") {
//m_timer.Stop();
//wxTimerEvent ev;
// OnTimer(ev);
//m_timer.Start(timerInterval);
return;
}
if (!msgtext.IsEmpty() && !s.IsEmpty()) s += " : ";
s += msgtext;
status->SetLabelText(s);
}
void frmLog::OnTimer(wxTimerEvent& event) {
if (!DBthread->isReadyRows()) return;
wxString rez=DBthread->AppendNewRows(my_view, m_storage_model->getStorage());
msgtext = wxString::Format("%s%d", rez, m_storage_model->GetRowCount());
status->SetLabelText(msgtext);
if (DBthread->IsIconError()) {
seticon(true);
}
wxString namepage= DBthread->getNamePage();
if (namepage.IsEmpty()) namepage = "not connect";
if (m_notebook->GetPageText(0) != namepage) m_notebook->SetPageText(0, namepage);
DBthread->GoReadRows();
}
#include "log/log_xpm.xpm"
#include "log/log_red_xpm.xpm"
void frmLog::seticon(bool errflag) {
//wxImage img = *sql_32_png_img;
if (errflag) {
SetIcon(idefRed);
}
else {
SetIcon(idef);
}
return;
wxBitmap* b = new wxBitmap(log_xpm);
//wxIcon ico=img.
wxMemoryDC dc(*b);
dc.SetBrush(*wxYELLOW_BRUSH);
//dc.SetBackground(*wxYELLOW_BRUSH);
dc.SetBackground(*wxRED_BRUSH);
dc.SetBrush(*wxRED_BRUSH);
//dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetTextForeground(*wxRED);
dc.SetPen(*wxRED_PEN);
wxRect rect(7,4,7, 7);
if (errflag) dc.DrawRoundedRectangle(rect, 0);
// wxFont font = wxFont(5, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
// wxFontStyle s;
// font.SetStyle(wxFONTFLAG_ANTIALIASED);
// dc.SetFont(font);
wxImage img = b->ConvertToImage();
dc.SelectObject(wxNullBitmap);
int w = img.GetWidth();
int h = img.GetHeight();
wxColor p;
wxColor c= wxColor(0,255,0);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
p = wxColour(img.GetRed(x, y),
img.GetGreen(x, y),
img.GetBlue(x, y));
//if (img.IsTransparent(x, y)) img.SetRGB(x, y, 255, 255, 255);
if (p == c) {
//img.SetTra
img.SetAlpha(x, y, 255);
img.SetRGB(x, y, img.GetMaskRed(), img.GetMaskGreen(), img.GetMaskBlue());
;
};
}
//wxIcon* ico = new wxIcon();
wxIcon ico=GetIcon();
wxBitmap* bmp = new wxBitmap(img);
ico.CopyFromBitmap(*bmp);
//SetIcon(*sql_32_png_ico);
SetIcon(ico);
}
wxSize MywxAuiDefaultTabArt::GetTabSize(wxDC& dc,
wxWindow* wnd,
const wxString& caption,
const wxBitmapBundle& bitmap,
bool WXUNUSED(active),
int close_button_state,
int* x_extent)
{
wxCoord measured_textx, measured_texty, tmp;
dc.SetFont(m_normalFont);
dc.GetTextExtent(caption, &measured_textx, &measured_texty);
dc.GetTextExtent(wxT("ABCDEFXj"), &tmp, &measured_texty);
// add padding around the text
wxCoord tab_width = measured_textx;
wxCoord tab_height = measured_texty;
// if the close button is showing, add space for it
if (close_button_state != wxAUI_BUTTON_STATE_HIDDEN)
{
// increase by button size plus the padding
tab_width += m_activeCloseBmp.GetBitmapFor(wnd).GetLogicalWidth() + wnd->FromDIP(3);
}
// if there's a bitmap, add space for it
if (bitmap.IsOk())
{
// we need the correct size of the bitmap to be used on this window in
// logical dimensions for drawing
const wxSize bitmapSize = bitmap.GetPreferredLogicalSizeFor(wnd);
// increase by bitmap plus right side bitmap padding
tab_width += bitmapSize.x + wnd->FromDIP(3);
tab_height = wxMax(tab_height, bitmapSize.y);
}
// add padding
wxSize padding = wnd->FromDIP(wxSize(16, 10));
tab_width += padding.x;
tab_height += padding.y;
if (m_flags & wxAUI_NB_TAB_FIXED_WIDTH)
{
tab_width = m_fixedTabWidth;
}
*x_extent = tab_width;
return wxSize(tab_width, tab_height);
}
void MywxAuiDefaultTabArt::DrawTab(wxDC& dc,
wxWindow* wnd,
const wxAuiNotebookPage& page,
const wxRect& in_rect,
int close_button_state,
wxRect* out_tab_rect,
wxRect* out_button_rect,
int* x_extent)
{
wxCoord normal_textx, normal_texty;
wxCoord selected_textx, selected_texty;
wxCoord texty;
// if the caption is empty, measure some temporary text
wxString caption = page.caption;
if (caption.empty())
caption = wxT("Xj");
dc.SetFont(m_normalFont);
dc.GetTextExtent(caption, &selected_textx, &selected_texty);
dc.SetFont(m_normalFont);
dc.GetTextExtent(caption, &normal_textx, &normal_texty);
// figure out the size of the tab
wxSize tab_size = GetTabSize(dc,
wnd,
page.caption,
page.bitmap,
page.active,
close_button_state,
x_extent);
wxCoord tab_height = m_tabCtrlHeight - 3;
wxCoord tab_width = tab_size.x;
wxCoord tab_x = in_rect.x;
wxCoord tab_y = in_rect.y + in_rect.height - tab_height;
caption = page.caption;
// select pen, brush and font for the tab to be drawn
if (page.active)
{
dc.SetFont(m_normalFont);
texty = selected_texty;
}
else
{
dc.SetFont(m_normalFont);
texty = normal_texty;
}
// create points that will make the tab outline
int clip_width = tab_width;
if (tab_x + clip_width > in_rect.x + in_rect.width)
clip_width = (in_rect.x + in_rect.width) - tab_x;
// since the above code above doesn't play well with WXDFB or WXCOCOA,
// we'll just use a rectangle for the clipping region for now --
dc.SetClippingRegion(tab_x, tab_y, clip_width + 1, tab_height - 3);
wxPoint border_points[6];
border_points[0] = wxPoint(tab_x, tab_y + tab_height - 4);
border_points[1] = wxPoint(tab_x, tab_y + 2);
border_points[2] = wxPoint(tab_x + 2, tab_y);
border_points[3] = wxPoint(tab_x + tab_width - 2, tab_y);
border_points[4] = wxPoint(tab_x + tab_width, tab_y + 2);
border_points[5] = wxPoint(tab_x + tab_width, tab_y + tab_height - 4);
// TODO: else if (m_flags &wxAUI_NB_LEFT) {}
// TODO: else if (m_flags &wxAUI_NB_RIGHT) {}
int drawn_tab_yoff = border_points[1].y;
int drawn_tab_height = border_points[0].y - border_points[1].y;
bool isdark = wxSystemSettings::GetAppearance().IsUsingDarkBackground();
wxColor back_color = m_baseColour;
if (page.active)
{
// draw active tab
// draw base background color
wxRect r(tab_x, tab_y, tab_width, tab_height);
dc.SetPen(wxPen(m_activeColour));
dc.SetBrush(wxBrush(m_activeColour));
dc.DrawRectangle(r.x + 1, r.y + 1, r.width - 1, r.height - 4);
// this white helps fill out the gradient at the top of the tab
wxColor gradient = *wxWHITE;
if (isdark)
{
//dark mode, we go darker
gradient = m_activeColour.ChangeLightness(70);
}
back_color = gradient;
dc.SetPen(wxPen(gradient));
dc.SetBrush(wxBrush(gradient));
dc.DrawRectangle(r.x + 2, r.y + 1, r.width - 3, r.height - 4);
// these two points help the rounded corners appear more antialiased
dc.SetPen(wxPen(m_activeColour));
dc.DrawPoint(r.x + 2, r.y + 1);
dc.DrawPoint(r.x + r.width - 2, r.y + 1);
// set rectangle down a bit for gradient drawing
r.SetHeight(r.GetHeight() / 2);
r.x += 2;
r.width -= 3;
r.y += r.height;
r.y -= 2;
// draw gradient background
wxColor top_color = gradient;
wxColor bottom_color = m_activeColour;
dc.GradientFillLinear(r, bottom_color, top_color, wxNORTH);
}
else
{
// draw inactive tab
wxRect r(tab_x, tab_y + 1, tab_width, tab_height - 3);
// start the gradient up a bit and leave the inside border inset
// by a pixel for a 3D look. Only the top half of the inactive
// tab will have a slight gradient
r.x += 3;
r.y++;
r.width -= 4;
r.height /= 2;
r.height--;
// -- draw top gradient fill for glossy look
wxColor top_color = m_baseColour;
wxColor bottom_color = top_color.ChangeLightness(160);
if (isdark)
{
//dark mode, we go darker
top_color = m_activeColour.ChangeLightness(70);
bottom_color = m_baseColour;
}
dc.GradientFillLinear(r, bottom_color, top_color, wxNORTH);
r.y += r.height;
r.y--;
// -- draw bottom fill for glossy look
top_color = m_baseColour;
bottom_color = m_baseColour;
dc.GradientFillLinear(r, top_color, bottom_color, wxSOUTH);
}
// draw tab outline
dc.SetPen(m_borderPen);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawPolygon(WXSIZEOF(border_points), border_points);
// there are two horizontal grey lines at the bottom of the tab control,
// this gets rid of the top one of those lines in the tab control
if (page.active)
{
if (m_flags & wxAUI_NB_BOTTOM)
dc.SetPen(wxPen(m_baseColour.ChangeLightness(170)));
// TODO: else if (m_flags &wxAUI_NB_LEFT) {}
// TODO: else if (m_flags &wxAUI_NB_RIGHT) {}
else //for wxAUI_NB_TOP
dc.SetPen(m_baseColourPen);
dc.DrawLine(border_points[0].x + 1,
border_points[0].y,
border_points[5].x,
border_points[5].y);
}
int text_offset;
int bitmap_offset = 0;
if (page.bitmap.IsOk())
{
bitmap_offset = tab_x + wnd->FromDIP(8);
const wxBitmap bitmap = page.bitmap.GetBitmapFor(wnd);
// draw bitmap
dc.DrawBitmap(bitmap,
bitmap_offset,
drawn_tab_yoff + (drawn_tab_height / 2) - (bitmap.GetLogicalHeight() / 2),
true);
text_offset = bitmap_offset + bitmap.GetLogicalWidth();
text_offset += wnd->FromDIP(3); // bitmap padding
}
else
{
text_offset = tab_x + wnd->FromDIP(8);
}
// draw close button if necessary
int close_button_width = 0;
wxString draw_text = caption;
//
size_t pos = 0; size_t poss = 0;
int x = text_offset;
int ttx, tty;
int y = drawn_tab_yoff + (drawn_tab_height) / 2 - (texty / 2) - 1;
{
wxDCBrushChanger setBrush(dc, *wxYELLOW_BRUSH);
while ((pos = draw_text.find(' ', poss)) != wxString::npos) {
dc.GetTextExtent(draw_text.SubString(poss, pos), &ttx, &tty);
x += ttx;
size_t en=draw_text.find(',', pos + 1);
if (en == wxString::npos) en = draw_text.Len();
dc.GetTextExtent(draw_text.SubString(pos+1, en-1), &ttx, &tty);
wxRect r(x, y, ttx, tty);
dc.DrawRoundedRectangle(r, 2);
x += ttx;
poss = en;
}
//
}
// draw tab text
wxColor sys_color = wxSystemSettings::GetColour(
page.active ? wxSYS_COLOUR_CAPTIONTEXT : wxSYS_COLOUR_INACTIVECAPTIONTEXT);
dc.SetTextForeground(sys_color);
dc.DrawText(draw_text,
text_offset,
drawn_tab_yoff + (drawn_tab_height) / 2 - (texty / 2) - 1);
// draw focus rectangle except under macOS where it looks out of place
#ifndef __WXOSX__
if (page.active && (wnd->FindFocus() == wnd))
{
wxRect focusRectText(text_offset, (drawn_tab_yoff + (drawn_tab_height) / 2 - (texty / 2) - 1),
selected_textx, selected_texty);
wxRect focusRect;
wxRect focusRectBitmap;
if (page.bitmap.IsOk())
{
const wxBitmap bitmap = page.bitmap.GetBitmapFor(wnd);
focusRectBitmap = wxRect(bitmap_offset, drawn_tab_yoff + (drawn_tab_height / 2) - (bitmap.GetLogicalHeight() / 2),
bitmap.GetLogicalWidth(), bitmap.GetLogicalHeight());
}
if (page.bitmap.IsOk() && draw_text.IsEmpty())
focusRect = focusRectBitmap;
else if (!page.bitmap.IsOk() && !draw_text.IsEmpty())
focusRect = focusRectText;
else if (page.bitmap.IsOk() && !draw_text.IsEmpty())
focusRect = focusRectText.Union(focusRectBitmap);
focusRect.Inflate(2, 2);
wxRendererNative::Get().DrawFocusRect(wnd, dc, focusRect, 0);
}
#endif // !__WXOSX__
* out_tab_rect = wxRect(tab_x, tab_y, tab_width, tab_height);
dc.DestroyClippingRegion();
}
frmLog::frmLog(frmMain *form, const wxString &_title, pgServer *srv) : pgFrame(NULL, _title)
{
dlgName = wxT("frmLog");
RestorePosition(-1, -1, 700, 500, 700, 500);
//SetIcon(wxIcon(log_xpm));
idef = wxIcon(log_xpm);
idefRed = wxIcon(log_red_xpm);
seticon(false);
mainForm = form;
// Setup accelerators
wxAcceleratorEntry entries[2];
entries[0].Set(wxACCEL_CTRL, (int)'F', MNU_FIND);
entries[1].Set(wxACCEL_CTRL, (int)'S', MNU_SEND_MAIL);
wxAcceleratorTable accel(2, entries);
SetAcceleratorTable(accel);
SetAcceleratorTable(accel);
m_notebook = new wxAuiNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,0 );
m_notebook->SetFont(settings->GetSystemFont());
MywxAuiDefaultTabArt* art = new MywxAuiDefaultTabArt();
wxPanel* testPanel = new wxPanel(m_notebook, wxID_ANY);
//BuildDataViewCtrl(testPanel, Page_Test);
my_view = new MyDataViewCtrl(testPanel, wxID_ANY, wxDefaultPosition,
wxDefaultSize, wxDV_VARIABLE_LINE_HEIGHT | wxDV_HORIZ_RULES | wxDV_VERT_RULES);
my_view->SetFont(settings->GetSystemFont());
my_view->GetMainWindow()->Bind(wxEVT_MOTION, &MyDataViewCtrl::OnMouseMove, my_view);
my_view->GetMainWindow()->Bind(wxEVT_KEY_DOWN, &MyDataViewCtrl::OnKEY_DOWN, my_view);
my_view->GetMainWindow()->Bind(wxEVT_KEY_UP, &MyDataViewCtrl::OnKEY_UP, my_view);
my_view->Bind(wxEVT_DATAVIEW_COLUMN_HEADER_CLICK, &MyDataViewCtrl::OnEVT_DATAVIEW_COLUMN_HEADER_CLICK, my_view);
my_view->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &MyDataViewCtrl::OnEVT_DATAVIEW_SELECTION_CHANGED, my_view);
my_view->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &MyDataViewCtrl::OnContextMenu, my_view);
// my_view->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &MyDataViewCtrl::OnContextMenu, my_view);
m_timer.Bind(wxEVT_TIMER, &frmLog::OnTimer, this);
my_view->Bind(wxEVT_MENU, &MyDataViewCtrl::OnEVT_DATAVIEW_CONTEXT_MENU, my_view);
m_storage_model = new StorageModel(my_view);
//wxObjectDataPtr<StorageModel> m_storage_model(new StorageModel(my_view));
my_view->AssociateModel(m_storage_model.get());
m_storage_model->BuildColumns(my_view);
wxString s;
s=settings->Read(dlgName + "/ColsWidth","");
if (s.Len()>0) my_view->setSettingString(s);
//settings->Write(dlgName + "/ColsWidth", s);
wxSizer* zeroPanelSz = new wxBoxSizer(wxVERTICAL);
my_view->SetMinSize(wxSize(-1, 200));
zeroPanelSz->Add(my_view, 1, wxGROW | wxALL, 5);
status = new wxStaticText(testPanel, wxID_ANY, "status text");
zeroPanelSz->Add(
status,
0, wxGROW | wxALL, 5);
//zeroPanelSz->Add(button_sizer);
//zeroPanelSz->Add(sizerCurrent);
my_view->setStatusObj(status);
wxBoxSizer* sSizer = new wxBoxSizer(wxHORIZONTAL);
group = new wxCheckBox(testPanel, ID_SET_GROUP, "Mode group");
sSizer->Add(group,
wxSizerFlags().Centre().DoubleBorder());
detail = new wxCheckBox(testPanel, ID_SET_DETAILGROUP, "View detail group");
sSizer->Add(detail,
wxSizerFlags().Centre().DoubleBorder());
const wxSizerFlags border1 = wxSizerFlags().DoubleBorder();
//wxBoxSizer* button_sizer = new wxBoxSizer(wxHORIZONTAL);
sSizer->Add(new wxButton(testPanel, ID_EXTENDED_LOG, "Clear table"), border1);
sSizer->Add(new wxStaticText(testPanel, wxID_ANY, "Load last file counts:"), border1);
sSizer->Add(new wxTextCtrl(testPanel, ID_COUNT_FILES, "1",wxDefaultPosition,wxDefaultSize, wxTE_PROCESS_ENTER), border1);
sSizer->Add(new wxButton(testPanel, ID_CLEAR_ALL_FILTER, "Clear All Filter"), border1);
//sSizer->Add(new wxButton(testPanel, ID_DELETE_SEL, "Delete selected"), border);
sSizer->Add(new wxButton(testPanel, ID_ADD_FILTER, "Add Filter Ignore"), border1);
sSizer->Add(new wxButton(testPanel, ID_HELP_LOG, "Help"), border1);
smart = new wxComboBox(testPanel, ID_CBOX_SMART, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, 0);
//smart->SetWindowStyle(smart->GetWindowStyle()|wxCB_READONLY);
smart->SetMinSize(wxSize(300,-1));
//listUserFilter = new wxComboBox(testPanel, ID_CBOX_UFilter, wxT("Combo!"), wxDefaultPosition, wxDefaultSize, 0, NULL, 0);
sSizer->Add(smart, border1);
my_view->smart = smart;
zeroPanelSz->Add(sSizer);
testPanel->SetSizerAndFit(zeroPanelSz);
m_notebook->AddPage(testPanel, "Log");
wxBoxSizer* setingSZ = new wxBoxSizer(wxHORIZONTAL);
setingSZ->Add(m_notebook);
wxPanel* settingPanel = new wxPanel(m_notebook, wxID_ANY);
lb = new wxCheckListBox(settingPanel,wxID_ANY);
wxTreeItemIdValue foldercookie, servercookie;
wxTreeItemId folderitem, serveritem;
pgObject* object;
pgServer* server;
std::vector<wxString> vec;
folderitem = mainForm->GetBrowser()->GetFirstChild(mainForm->GetBrowser()->GetRootItem(), foldercookie);
while (folderitem)
{
if (mainForm->GetBrowser()->ItemHasChildren(folderitem))
{
serveritem = mainForm->GetBrowser()->GetFirstChild(folderitem, servercookie);
while (serveritem)
{
object = mainForm->GetBrowser()->GetObject(serveritem);
if (object && object->IsCreatedBy(serverFactory))
{
server = (pgServer*)object;
wxString srvname = wxString::Format("%s:%d", server->GetName(), server->GetPort());
vec.push_back(srvname);
}
serveritem = mainForm->GetBrowser()->GetNextChild(folderitem, servercookie);
}
}
folderitem = mainForm->GetBrowser()->GetNextChild(mainForm->GetBrowser()->GetRootItem(), foldercookie);
}
lb->Append(vec);
wxString srvs;
wxString srvname = wxString::Format("%s:%d", srv->GetName(), srv->GetPort());
bool onlyOne = srv->GetConnected();
if (onlyOne) {
SetTitle(_title + wxString::Format("(%s)", srvname));
lb->Enable(false);
}
settings->Read(dlgName + "/AutoConnect", &srvs, "");
if (!srvs.IsEmpty()) srvs += ";";
srvs += srvname;
wxStringTokenizer tk(srvs, ";", wxTOKEN_RET_EMPTY_ALL);
while (tk.HasMoreTokens())
{
wxString l = tk.GetNextToken();
pgServer* s = getServer(l);
if (s != NULL) {
if (!CheckConn(s->GetName(), s->GetPort())) {
if (!onlyOne || (onlyOne && srvname == l)) {
pgConn* conn = createConn(s);
AddNewConn(conn);
}
for (unsigned int x = 0; x < lb->GetCount(); x++)
if (l==lb->GetString(x)) lb->Check(x, true);
}
}
}
wxSizer* zeroPanelSz2 = new wxBoxSizer(wxHORIZONTAL);
lb->SetMinSize(wxSize(-1, 200));
zeroPanelSz2->Add(lb, 1, wxGROW | wxALL, 5);
wxPanel *m_panel2 = new wxPanel(settingPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
zeroPanelSz2->Add(m_panel2, 1, wxEXPAND | wxALL, 5);
wxBoxSizer* bSizer3;
bSizer3 = new wxBoxSizer(wxVERTICAL);
wxBoxSizer* bSizer2;
bSizer2 = new wxBoxSizer(wxHORIZONTAL);
listUserFilter = new wxComboBox(m_panel2, ID_CBOX_UFilter, wxT("Combo!"), wxDefaultPosition, wxDefaultSize, 0, NULL, 0);
bSizer2->Add(listUserFilter, 1, wxALL | wxEXPAND, 2);
wxButton *m_button1 = new wxButton(m_panel2, ID_ADD_UFilter, wxT("Add"), wxDefaultPosition, wxDefaultSize, 0);
m_button1->SetMaxSize(wxSize(30, -1));
bSizer2->Add(m_button1, 0, wxALL, 2);
wxButton *m_button2 = new wxButton(m_panel2, ID_DEL_UFilter, wxT("Del"), wxDefaultPosition, wxDefaultSize, 0);
m_button2->SetMaxSize(wxSize(30, -1));
bSizer2->Add(m_button2, 0, wxALL, 2);
bSizer3->Add(bSizer2, 0, wxEXPAND, 5);
wxBoxSizer* bSizer5;
bSizer5 = new wxBoxSizer(wxVERTICAL);
contentFilter = new wxTextCtrl(m_panel2, ID_TEXT_UFilter, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
bSizer5->Add(contentFilter, 1, wxALL | wxEXPAND, 2);
bSizer3->Add(bSizer5, 1, wxEXPAND, 2);
m_panel2->SetSizer(bSizer3);
//listUserFilter->Bind();
settingPanel->SetSizerAndFit(zeroPanelSz2);
m_notebook->AddPage(settingPanel, "Settings");
m_notebook->SetArtProvider(art);
//m_notebook->SetSelectedFont(settings->GetSystemFont());
//m_notebook->SetNormalFont(settings->GetSystemFont());
bool b=true;
settings->Read(dlgName + "/Mode",&b, false);
group->SetValue(b);
my_view->setGroupMode(b);
my_view->ModUserFilter("","Init", listUserFilter, contentFilter);
// if (mainForm) getFilename();
Connect(wxID_ANY, wxEVT_THREAD, wxThreadEventHandler(frmLog::OnAddLabelTextThread), NULL, this);
DBthread = new MyThread(conArray,this,1);
if (DBthread->Create() != wxTHREAD_NO_ERROR)
{
wxLogError(wxT("Cant create thread!"));
}
DBthread->Run();
m_timer.Start(timerInterval);
}
pgServer* frmLog::getServer(wxString& strserver) {
wxTreeItemIdValue foldercookie, servercookie;
wxTreeItemId folderitem, serveritem;
pgObject* object;
pgServer* server;
folderitem = mainForm->GetBrowser()->GetFirstChild(mainForm->GetBrowser()->GetRootItem(), foldercookie);
while (folderitem)
{
if (mainForm->GetBrowser()->ItemHasChildren(folderitem))
{
serveritem = mainForm->GetBrowser()->GetFirstChild(folderitem, servercookie);
while (serveritem)
{
object = mainForm->GetBrowser()->GetObject(serveritem);
if (object && object->IsCreatedBy(serverFactory))
{
server = (pgServer*)object;
wxString srvname = wxString::Format("%s:%d", server->GetName(), server->GetPort());
if (srvname == strserver) {
return server;
}
}
serveritem = mainForm->GetBrowser()->GetNextChild(folderitem, servercookie);
}
}
folderitem = mainForm->GetBrowser()->GetNextChild(mainForm->GetBrowser()->GetRootItem(), foldercookie);
}
return 0;
}
frmLog::~frmLog()
{
m_timer.Stop();
// If connection is still available, delete it
SavePosition();
wxString srvs;
for (unsigned int x = 0; x < lb->GetCount(); x++)
if (lb->IsChecked(x)) {
if (!srvs.IsEmpty()) srvs += ";";
srvs += lb->GetString(x);
}
settings->Write(dlgName + "/AutoConnect", srvs);
wxString s = my_view->getSettingString();
settings->Write(dlgName+"/ColsWidth",s);
settings->WriteBool(dlgName + "/Mode", group->IsChecked());
Storage *st=m_storage_model->getStorage();
st->saveFilters();
// If the status window wasn't launched in standalone mode...
if (mainForm)
mainForm->RemoveFrame(this);
mainForm->Logfrm = NULL;
}
void frmLog::Go()
{
// Show the window
Show(true);
}
LogFactory::LogFactory(menuFactoryList* list, wxMenu* mnu, ctlMenuToolbar* toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Log view..."), _("Log view CSV format"));
}
wxWindow* LogFactory::StartDialog(frmMain* form, pgObject* obj)
{
pgServer* srv = (pgServer *) obj;
wxString txt = "";
if (form->Logfrm != NULL) {
if (!form->Logfrm->CheckConn(srv->GetName(), srv->GetPort())) {
pgConn* conn = form->Logfrm->createConn(srv);
form->Logfrm->AddNewConn(conn);
}
}
else {
form->Logfrm = new frmLog(form, txt, srv);
if (form->Logfrm!=NULL)
form->AddFrame(form->Logfrm);
}
//frmLog* frm = new frmLog(form, obj);
form->Logfrm->Go();
return 0;
}
bool LogFactory::CheckEnable(pgObject* obj)
{
if (!obj)
return false;
if (obj->GetMetaType() == PGM_SERVER) {
// if (!((pgServer*)obj)->GetConnected())
// return false;
return true;
}
return false;
}