mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 06:05:49 -06:00
2588 lines
77 KiB
C++
2588 lines
77 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// pgAdmin III - PostgreSQL Tools
|
||
//
|
||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||
// This software is released under the PostgreSQL Licence
|
||
//
|
||
// frmReport.cpp - The report file dialogue
|
||
//
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
|
||
// App headers
|
||
#include "pgAdmin3.h"
|
||
#include <wx/file.h>
|
||
#include <wx/regex.h>
|
||
|
||
#include "frm/frmMain.h"
|
||
#include "frm/frmReport.h"
|
||
#include "utils/sysSettings.h"
|
||
#include "utils/misc.h"
|
||
#include "ctl/ctlListView.h"
|
||
#include "schema/pgObject.h"
|
||
#include "schema/pgCollection.h"
|
||
#include "schema/pgServer.h"
|
||
#include "schema/pgDatabase.h"
|
||
#include "schema/pgSchema.h"
|
||
#include "schema/pgTable.h"
|
||
#include "schema/pgColumn.h"
|
||
#include "schema/pgConstraints.h"
|
||
#include "agent/pgaJob.h"
|
||
#include "schema/pgForeignKey.h"
|
||
#include "schema/pgIndexConstraint.h"
|
||
#include "schema/pgCheck.h"
|
||
#include "utils/utffile.h"
|
||
#include <wx/stdpaths.h>
|
||
#include "../utils/diff_match_patch.h"
|
||
// XML2/XSLT headers
|
||
#include <libxslt/transform.h>
|
||
#include <libxslt/xsltutils.h>
|
||
|
||
#define txtTitle CTRL_TEXT("txtTitle")
|
||
#define txtNotes CTRL_TEXT("txtNotes")
|
||
#define txtHtmlFile CTRL_TEXT("txtHtmlFile")
|
||
#define txtXmlFile CTRL_TEXT("txtXmlFile")
|
||
#define txtHtmlStylesheet CTRL_TEXT("txtHtmlStylesheet")
|
||
#define txtXmlStylesheet CTRL_TEXT("txtXmlStylesheet")
|
||
#define btnOK CTRL_BUTTON("wxID_OK")
|
||
#define btnFile CTRL_BUTTON("btnFile")
|
||
#define btnStylesheet CTRL_BUTTON("btnStylesheet")
|
||
#define rbHtml CTRL_RADIOBUTTON("rbHtml")
|
||
#define rbXml CTRL_RADIOBUTTON("rbXml")
|
||
#define rbHtmlBuiltin CTRL_RADIOBUTTON("rbHtmlBuiltin")
|
||
#define rbHtmlEmbed CTRL_RADIOBUTTON("rbHtmlEmbed")
|
||
#define rbHtmlLink CTRL_RADIOBUTTON("rbHtmlLink")
|
||
#define rbXmlPlain CTRL_RADIOBUTTON("rbXmlPlain")
|
||
#define rbXmlLink CTRL_RADIOBUTTON("rbXmlLink")
|
||
#define rbXmlProcess CTRL_RADIOBUTTON("rbXmlProcess")
|
||
#define chkSql CTRL_CHECKBOX("chkSql")
|
||
#define chkBrowser CTRL_CHECKBOX("chkBrowser")
|
||
|
||
BEGIN_EVENT_TABLE(frmReport, pgDialog)
|
||
EVT_RADIOBUTTON(XRCID("rbHtml"), frmReport::OnChange)
|
||
EVT_RADIOBUTTON(XRCID("rbXml"), frmReport::OnChange)
|
||
EVT_RADIOBUTTON(XRCID("rbHtmlBuiltin"), frmReport::OnChange)
|
||
EVT_RADIOBUTTON(XRCID("rbHtmlEmbed"), frmReport::OnChange)
|
||
EVT_RADIOBUTTON(XRCID("rbHtmlLink"), frmReport::OnChange)
|
||
EVT_RADIOBUTTON(XRCID("rbXmlPlain"), frmReport::OnChange)
|
||
EVT_RADIOBUTTON(XRCID("rbXmlLink"), frmReport::OnChange)
|
||
EVT_RADIOBUTTON(XRCID("rbXmlProcess"), frmReport::OnChange)
|
||
EVT_TEXT(XRCID("txtHtmlFile"), frmReport::OnChange)
|
||
EVT_TEXT(XRCID("txtXmlFile"), frmReport::OnChange)
|
||
EVT_TEXT(XRCID("txtHtmlStylesheet"), frmReport::OnChange)
|
||
EVT_TEXT(XRCID("txtXmlStylesheet"), frmReport::OnChange)
|
||
EVT_BUTTON(XRCID("btnFile"), frmReport::OnBrowseFile)
|
||
EVT_BUTTON(XRCID("btnStylesheet"), frmReport::OnBrowseStylesheet)
|
||
EVT_BUTTON(wxID_HELP, frmReport::OnHelp)
|
||
EVT_BUTTON(wxID_OK, frmReport::OnOK)
|
||
EVT_BUTTON(wxID_CANCEL, frmReport::OnCancel)
|
||
END_EVENT_TABLE()
|
||
|
||
extern wxString dataDir;
|
||
|
||
typedef std::vector<double> vectord;
|
||
template<typename A, typename B>
|
||
std::pair<B, A> flip_pair(const std::pair<A, B>& p)
|
||
{
|
||
return std::pair<B, A>(p.second, p.first);
|
||
}
|
||
template<typename A, typename B>
|
||
std::multimap<B, A> flip_map(const std::map<A, B>& src)
|
||
{
|
||
std::multimap<B, A> dst;
|
||
std::transform(src.begin(), src.end(), std::inserter(dst, dst.begin()),
|
||
flip_pair<A, B>);
|
||
return dst;
|
||
}
|
||
const static wxString names[] = { "Count","Sum","Min","Max","Avg","Mediana","Moda"};
|
||
class Stats
|
||
{
|
||
std::vector<vectord> columns;
|
||
std::vector<int> types_col;
|
||
std::map<wxString, double> metrics;
|
||
int rows = 0;
|
||
public:
|
||
Stats(int rows, int cols, const std::vector<int> &types) {
|
||
|
||
for (int x = 0; x < cols; x++) {
|
||
vectord arrays(rows);
|
||
columns.push_back(arrays);
|
||
}
|
||
types_col = types;
|
||
this->rows = rows;
|
||
}
|
||
void addValue(int row,int col,double d) {
|
||
if (types_col[col] < 1) return;
|
||
if (col == 0) {
|
||
return; // no use 0 column
|
||
}
|
||
columns[col][row] = d;
|
||
}
|
||
int getCountMetrics() {
|
||
return 7;
|
||
}
|
||
void calc() {
|
||
metrics.clear();
|
||
// Count
|
||
{
|
||
//wxString k = names[nm];
|
||
for (int col = 1; col < columns.size(); col++) {
|
||
if (types_col[col] < 1) continue;
|
||
vectord elements=columns[col];
|
||
//elements.reserve(1000);
|
||
double sum = 0.0;
|
||
double avg = 0;
|
||
double med = 0;
|
||
std::size_t n = elements.size();
|
||
std::sort(elements.begin(), elements.end());
|
||
|
||
//std::cout << "max=" << elements.back() << std::endl;
|
||
double max = elements.back();
|
||
metrics[getKey(names[3], col)] = max; //max
|
||
//std::cout << "min=" << elements.front() << std::endl;
|
||
metrics[getKey(names[2], col)] = elements.front(); //min
|
||
for (auto& x : elements) {
|
||
sum += x;
|
||
}
|
||
if (n == 0) continue;
|
||
//std::cout << "avg=" << sum / n << std::endl;
|
||
metrics[getKey(names[1], col)] = sum;
|
||
avg = sum / n;
|
||
metrics[getKey(names[4], col)] = avg;
|
||
if (n == 1) {
|
||
//std::cout << "med=" << elements[0] << std::endl;
|
||
med = elements[0];
|
||
}
|
||
else if (n % 2 == 0) {
|
||
std::size_t i = n / 2;
|
||
//std::cout << "med=" << (double)(elements[i] + elements[i - 1]) / 2.0 << std::endl;
|
||
med = (elements[i] + elements[i - 1]) / 2.0;
|
||
}
|
||
else {
|
||
//std::cout << "med=" << elements[n / 2] << std::endl;
|
||
med = elements[n / 2];
|
||
}
|
||
metrics[getKey(names[5], col)] = med;
|
||
std::set<double> elements_set;
|
||
for (auto& x : elements) {
|
||
elements_set.insert(x);
|
||
}
|
||
std::map<double, std::size_t> elements_count;
|
||
for (auto& x : elements_set) {
|
||
elements_count.insert(std::pair<int, std::size_t>(x, std::count(elements.begin(), elements.end(), x)));
|
||
}
|
||
std::multimap<std::size_t, double> count_elements;
|
||
count_elements = flip_map(elements_count);
|
||
//std::cout << "mod: ";
|
||
std::size_t count_mod = count_elements.rbegin()->first;
|
||
for (auto& x : count_elements) {
|
||
if (x.first == count_mod) {
|
||
//std::cout << x.second << ", ";
|
||
metrics[getKey(names[6], col)] = x.second;
|
||
}
|
||
else {}
|
||
}
|
||
//std::cout << "(" << count_mod << ")" << std::endl;
|
||
wxString s_count_mod = "(" + NumToStr((long)count_mod) + ")";
|
||
}
|
||
}
|
||
}
|
||
wxArrayString GetRowForTable(int numMetric) {
|
||
wxArrayString footcols;
|
||
wxString name;
|
||
bool isEmpty = true;
|
||
if (numMetric >= getCountMetrics()) return footcols;
|
||
for (int x = 0; x < columns.size();x++) {
|
||
if (x == 0) {
|
||
// Title
|
||
name = names[numMetric];
|
||
footcols.Add(name);
|
||
}
|
||
else {
|
||
if (numMetric==0) {
|
||
if (x == 1) footcols.Add(NumToStr((long)rows));
|
||
else
|
||
footcols.Add("");
|
||
isEmpty = false;
|
||
}
|
||
else {
|
||
wxString k = getKey(name, x);
|
||
wxString v;
|
||
auto it = metrics.find(k);
|
||
double d;
|
||
if (it != metrics.end()) {
|
||
d = it->second;
|
||
//if
|
||
wxString suff;
|
||
|
||
if (types_col[x] == 2) {
|
||
if (d > 1024) {
|
||
d = d / 1024;
|
||
suff = "GB";
|
||
}
|
||
else suff = "MB";
|
||
}
|
||
|
||
if (suff.Length() > 0) v.Printf(wxT("%.2lf %s"), d, suff);
|
||
else v.Printf(wxT("%.2lf"), d);
|
||
} else {
|
||
|
||
}
|
||
footcols.Add(v);
|
||
if (v.Length() > 0) isEmpty = false;
|
||
}
|
||
}
|
||
|
||
}
|
||
if (isEmpty) footcols.Clear();
|
||
return footcols;
|
||
}
|
||
wxString getKey(const wxString& name, int col) {
|
||
return wxString::Format("%s_%d", name,col);
|
||
}
|
||
|
||
};
|
||
|
||
|
||
|
||
frmReport::frmReport(wxWindow *p)
|
||
{
|
||
parent = p;
|
||
header = wxT("");
|
||
|
||
SetFont(settings->GetSystemFont());
|
||
LoadResource(p, wxT("frmReport"));
|
||
|
||
// Icon
|
||
appearanceFactory->SetIcons(this);
|
||
RestorePosition();
|
||
btnOK->Disable();
|
||
|
||
wxString val;
|
||
bool bVal;
|
||
|
||
// Output format
|
||
settings->Read(wxT("Reports/ReportFormat"), &val, wxT("h"));
|
||
if (val == wxT("x"))
|
||
{
|
||
rbHtml->SetValue(false);
|
||
rbXml->SetValue(true);
|
||
}
|
||
else
|
||
{
|
||
rbHtml->SetValue(true);
|
||
rbXml->SetValue(false);
|
||
}
|
||
|
||
// HTML Stylesheet
|
||
settings->Read(wxT("Reports/HtmlStylesheetMode"), &val, wxT("b"));
|
||
if (val == wxT("e"))
|
||
{
|
||
rbHtmlBuiltin->SetValue(false);
|
||
rbHtmlEmbed->SetValue(true);
|
||
rbHtmlLink->SetValue(false);
|
||
}
|
||
else if (val == wxT("l"))
|
||
{
|
||
rbHtmlBuiltin->SetValue(false);
|
||
rbHtmlEmbed->SetValue(false);
|
||
rbHtmlLink->SetValue(true);
|
||
}
|
||
else
|
||
{
|
||
rbHtmlBuiltin->SetValue(true);
|
||
rbHtmlEmbed->SetValue(false);
|
||
rbHtmlLink->SetValue(false);
|
||
}
|
||
|
||
// XML Stylesheet
|
||
settings->Read(wxT("Reports/XmlStylesheetMode"), &val, wxT("p"));
|
||
if (val == wxT("l"))
|
||
{
|
||
rbXmlPlain->SetValue(false);
|
||
rbXmlLink->SetValue(true);
|
||
rbXmlProcess->SetValue(false);
|
||
}
|
||
else if (val == wxT("r"))
|
||
{
|
||
rbXmlPlain->SetValue(false);
|
||
rbXmlLink->SetValue(false);
|
||
rbXmlProcess->SetValue(true);
|
||
}
|
||
else
|
||
{
|
||
rbXmlPlain->SetValue(true);
|
||
rbXmlLink->SetValue(false);
|
||
rbXmlProcess->SetValue(false);
|
||
}
|
||
|
||
// Default values
|
||
settings->Read(wxT("Reports/LastNotes"), &val, wxT(""));
|
||
txtNotes->SetValue(val);
|
||
|
||
settings->Read(wxT("Reports/LastHtmlStylesheet"), &val, wxEmptyString);
|
||
txtHtmlStylesheet->SetValue(val);
|
||
|
||
settings->Read(wxT("Reports/LastXmlStylesheet"), &val, wxEmptyString);
|
||
txtXmlStylesheet->SetValue(val);
|
||
|
||
settings->Read(wxT("Reports/LastHtmlFile"), &val, wxEmptyString);
|
||
txtHtmlFile->SetValue(val);
|
||
|
||
settings->Read(wxT("Reports/LastXmlFile"), &val, wxEmptyString);
|
||
txtXmlFile->SetValue(val);
|
||
|
||
settings->Read(wxT("Reports/IncludeSQL"), &bVal, true);
|
||
chkSql->SetValue(bVal);
|
||
chkSql->Disable();
|
||
|
||
settings->Read(wxT("Reports/OpenInBrowser"), &bVal, true);
|
||
chkBrowser->SetValue(bVal);
|
||
|
||
wxCommandEvent ev;
|
||
OnChange(ev);
|
||
|
||
txtTitle->SetFocus();
|
||
}
|
||
|
||
|
||
frmReport::~frmReport()
|
||
{
|
||
SavePosition();
|
||
}
|
||
|
||
|
||
void frmReport::OnHelp(wxCommandEvent &ev)
|
||
{
|
||
DisplayHelp(wxT("reports"), HELP_PGADMIN);
|
||
}
|
||
|
||
|
||
void frmReport::OnChange(wxCommandEvent &ev)
|
||
{
|
||
bool enable = true;
|
||
|
||
if (rbHtml->GetValue())
|
||
{
|
||
// Show/hide the appropriate controls
|
||
rbHtmlBuiltin->Show(true);
|
||
rbHtmlEmbed->Show(true);
|
||
rbHtmlLink->Show(true);
|
||
txtHtmlStylesheet->Show(true);
|
||
txtHtmlFile->Show(true);
|
||
|
||
rbXmlPlain->Show(false);
|
||
rbXmlLink->Show(false);
|
||
rbXmlProcess->Show(false);
|
||
txtXmlStylesheet->Show(false);
|
||
txtXmlFile->Show(false);
|
||
|
||
// Enable/disable as appropriate
|
||
if (txtHtmlFile->GetValue().IsEmpty())
|
||
enable = false;
|
||
|
||
if (rbHtmlBuiltin->GetValue())
|
||
{
|
||
txtHtmlStylesheet->Disable();
|
||
btnStylesheet->Disable();
|
||
}
|
||
else
|
||
{
|
||
txtHtmlStylesheet->Enable();
|
||
btnStylesheet->Enable();
|
||
}
|
||
|
||
if (rbHtmlEmbed->GetValue())
|
||
{
|
||
if (!wxFile::Exists(txtHtmlStylesheet->GetValue()))
|
||
enable = false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Show/hide the appropriate controls
|
||
rbHtmlBuiltin->Show(false);
|
||
rbHtmlEmbed->Show(false);
|
||
rbHtmlLink->Show(false);
|
||
txtHtmlStylesheet->Show(false);
|
||
txtHtmlFile->Show(false);
|
||
|
||
rbXmlPlain->Show(true);
|
||
rbXmlLink->Show(true);
|
||
rbXmlProcess->Show(true);
|
||
txtXmlStylesheet->Show(true);
|
||
txtXmlFile->Show(true);
|
||
|
||
// Enable/disable as appropriate
|
||
if (txtXmlFile->GetValue().IsEmpty())
|
||
enable = false;
|
||
|
||
if (rbXmlPlain->GetValue())
|
||
{
|
||
txtXmlStylesheet->Disable();
|
||
btnStylesheet->Disable();
|
||
}
|
||
else
|
||
{
|
||
txtXmlStylesheet->Enable();
|
||
btnStylesheet->Enable();
|
||
}
|
||
|
||
if (rbXmlProcess->GetValue())
|
||
{
|
||
if (!wxFile::Exists(txtXmlStylesheet->GetValue()))
|
||
enable = false;
|
||
}
|
||
}
|
||
|
||
btnOK->Enable(enable);
|
||
}
|
||
|
||
|
||
void frmReport::OnOK(wxCommandEvent &ev)
|
||
{
|
||
wxString filename;
|
||
if (rbHtml->GetValue())
|
||
filename = txtHtmlFile->GetValue();
|
||
else
|
||
filename = txtXmlFile->GetValue();
|
||
|
||
wxFileName fn(filename);
|
||
fn.MakeAbsolute();
|
||
|
||
// Check if the file exsits, and if so, whether to overwrite it
|
||
if (wxFileExists(fn.GetFullPath()))
|
||
{
|
||
wxString msg;
|
||
msg.Printf(_("The file: \n\n%s\n\nalready exists. Do you want to overwrite it?"), fn.GetFullPath().c_str());
|
||
|
||
if (wxMessageBox(msg, _("Overwrite file?"), wxYES_NO | wxICON_QUESTION) != wxYES)
|
||
{
|
||
if (rbHtml->GetValue())
|
||
txtHtmlFile->SetFocus();
|
||
else
|
||
txtXmlFile->SetFocus();
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Yield, to allow the messagebox to go
|
||
wxTheApp->Yield(true);
|
||
|
||
// Add the title, notes and SQL
|
||
if (txtTitle->GetValue() != wxT(""))
|
||
XmlAddHeaderValue(wxT("title"), txtTitle->GetValue());
|
||
|
||
if (txtNotes->GetValue() != wxT(""))
|
||
XmlAddHeaderValue(wxT("notes"), txtNotes->GetValue());
|
||
|
||
// Generate the report data
|
||
wxString report;
|
||
|
||
if (rbHtml->GetValue())
|
||
{
|
||
if (rbHtmlBuiltin->GetValue())
|
||
{
|
||
wxString xml = GetXmlReport(wxEmptyString);
|
||
wxString xsl = GetDefaultXsl(GetEmbeddedCss(GetDefaultCss()));
|
||
report = XslProcessReport(xml, xsl);
|
||
}
|
||
else if (rbHtmlEmbed->GetValue())
|
||
{
|
||
wxString xml = GetXmlReport(wxEmptyString);
|
||
wxString css = FileRead(txtHtmlStylesheet->GetValue());
|
||
if (css.IsEmpty())
|
||
{
|
||
wxLogError(_("No stylesheet data could be read from the file %s: Errcode=%d."), txtHtmlStylesheet->GetValue().c_str(), wxSysErrorCode());
|
||
return;
|
||
}
|
||
wxString xsl = GetDefaultXsl(GetEmbeddedCss(css));
|
||
report = XslProcessReport(xml, xsl);
|
||
}
|
||
else
|
||
{
|
||
wxString xml = GetXmlReport(wxEmptyString);
|
||
wxString xsl = GetDefaultXsl(GetCssLink(txtHtmlStylesheet->GetValue()));
|
||
report = XslProcessReport(xml, xsl);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (rbXmlPlain->GetValue())
|
||
{
|
||
report = GetXmlReport(wxEmptyString);
|
||
}
|
||
else if (rbXmlLink->GetValue())
|
||
{
|
||
report = GetXmlReport(txtXmlStylesheet->GetValue());
|
||
}
|
||
else
|
||
{
|
||
wxString xml = GetXmlReport(wxEmptyString);
|
||
wxString xsl = FileRead(txtXmlStylesheet->GetValue());
|
||
if (xsl.IsEmpty())
|
||
{
|
||
wxLogError(_("No stylesheet data could be read from the file %s: Errcode=%d."), txtXmlStylesheet->GetValue().c_str(), wxSysErrorCode());
|
||
return;
|
||
}
|
||
report = XslProcessReport(xml, xsl);
|
||
}
|
||
}
|
||
|
||
// If report is empty, an error must have occurred
|
||
if (report.IsEmpty())
|
||
return;
|
||
|
||
// Save it to disk
|
||
wxFile file(fn.GetFullPath(), wxFile::write);
|
||
if (!file.IsOpened())
|
||
{
|
||
wxLogError(_("Failed to open file %s."), fn.GetFullPath().c_str());
|
||
return;
|
||
}
|
||
file.Write(report, wxConvUTF8);
|
||
file.Close();
|
||
|
||
// Open the file in the default browser if required
|
||
if (chkBrowser->GetValue())
|
||
#ifdef __WXMSW__
|
||
wxLaunchDefaultBrowser(fn.GetFullPath());
|
||
#else
|
||
wxLaunchDefaultBrowser(wxT("file://") + fn.GetFullPath());
|
||
#endif
|
||
|
||
// Save the settings for next time round
|
||
settings->Write(wxT("Reports/LastNotes"), txtNotes->GetValue());
|
||
|
||
if (rbHtml->GetValue())
|
||
settings->Write(wxT("Reports/ReportFormat"), wxT("h"));
|
||
else
|
||
settings->Write(wxT("Reports/ReportFormat"), wxT("x"));
|
||
|
||
if (rbHtmlBuiltin->GetValue())
|
||
settings->Write(wxT("Reports/HtmlStylesheetMode"), wxT("b"));
|
||
else if (rbHtmlEmbed->GetValue())
|
||
settings->Write(wxT("Reports/HtmlStylesheetMode"), wxT("e"));
|
||
else
|
||
settings->Write(wxT("Reports/HtmlStylesheetMode"), wxT("l"));
|
||
|
||
if (rbXmlPlain->GetValue())
|
||
settings->Write(wxT("Reports/XmlStylesheetMode"), wxT("p"));
|
||
else if (rbXmlLink->GetValue())
|
||
settings->Write(wxT("Reports/XmlStylesheetMode"), wxT("l"));
|
||
else
|
||
settings->Write(wxT("Reports/XmlStylesheetMode"), wxT("r"));
|
||
|
||
settings->Write(wxT("Reports/LastHtmlStylesheet"), txtHtmlStylesheet->GetValue());
|
||
settings->Write(wxT("Reports/LastXmlStylesheet"), txtXmlStylesheet->GetValue());
|
||
|
||
|
||
settings->Write(wxT("Reports/LastHtmlFile"), txtHtmlFile->GetValue());
|
||
settings->Write(wxT("Reports/LastXmlFile"), txtXmlFile->GetValue());
|
||
|
||
settings->WriteBool(wxT("Reports/IncludeSQL"), chkSql->GetValue());
|
||
|
||
settings->WriteBool(wxT("Reports/OpenInBrowser"), chkBrowser->GetValue());
|
||
|
||
// Now go away
|
||
if (IsModal())
|
||
EndModal(wxID_OK);
|
||
else
|
||
Destroy();
|
||
}
|
||
|
||
void frmReport::OnCancel(wxCommandEvent &ev)
|
||
{
|
||
if (IsModal())
|
||
EndModal(wxID_CANCEL);
|
||
else
|
||
Destroy();
|
||
}
|
||
|
||
void frmReport::OnBrowseStylesheet(wxCommandEvent &ev)
|
||
{
|
||
if (rbHtml->GetValue())
|
||
{
|
||
wxString def = txtHtmlStylesheet->GetValue();
|
||
|
||
if (!wxFile::Exists(def))
|
||
def.Empty();
|
||
|
||
#ifdef __WXMSW__
|
||
wxFileDialog file(this, _("Select stylesheet filename"), wxGetHomeDir(), def, _("HTML Stylesheet files (*.css)|*.css|All files (*.*)|*.*"), wxFD_OPEN);
|
||
#else
|
||
wxFileDialog file(this, _("Select stylesheet filename"), wxGetHomeDir(), def, _("HTML Stylesheet files (*.css)|*.css|All files (*)|*"), wxFD_OPEN);
|
||
#endif
|
||
|
||
if (file.ShowModal() == wxID_OK)
|
||
{
|
||
txtHtmlStylesheet->SetValue(file.GetPath());
|
||
OnChange(ev);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
wxString def = txtXmlStylesheet->GetValue();
|
||
|
||
if (!wxFile::Exists(def))
|
||
def.Empty();
|
||
|
||
#ifdef __WXMSW__
|
||
wxFileDialog file(this, _("Select stylesheet filename"), wxGetHomeDir(), def, _("XML Stylesheet files (*.xsl)|*.xsl|All files (*.*)|*.*"), wxFD_OPEN);
|
||
#else
|
||
wxFileDialog file(this, _("Select stylesheet filename"), wxGetHomeDir(), def, _("XML Stylesheet files (*.xsl)|*.xsl|All files (*)|*"), wxFD_OPEN);
|
||
#endif
|
||
|
||
if (file.ShowModal() == wxID_OK)
|
||
{
|
||
txtXmlStylesheet->SetValue(file.GetPath());
|
||
OnChange(ev);
|
||
}
|
||
}
|
||
}
|
||
|
||
void frmReport::OnBrowseFile(wxCommandEvent &ev)
|
||
{
|
||
if (rbHtml->GetValue())
|
||
{
|
||
#ifdef __WXMSW__
|
||
wxFileDialog file(this, _("Select output filename"), wxGetHomeDir(), txtHtmlFile->GetValue(),
|
||
_("HTML files (*.html)|*.html|All files (*.*)|*.*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||
#else
|
||
wxFileDialog file(this, _("Select output filename"), wxGetHomeDir(), txtHtmlFile->GetValue(),
|
||
_("HTML files (*.html)|*.html|All files (*)|*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||
#endif
|
||
|
||
if (file.ShowModal() == wxID_OK)
|
||
{
|
||
txtHtmlFile->SetValue(file.GetPath());
|
||
OnChange(ev);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
#ifdef __WXMSW__
|
||
wxFileDialog file(this, _("Select output filename"), wxGetHomeDir(), txtXmlFile->GetValue(),
|
||
_("XML files (*.xml)|*.xml|All files (*.*)|*.*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||
#else
|
||
wxFileDialog file(this, _("Select output filename"), wxGetHomeDir(), txtXmlFile->GetValue(),
|
||
_("XML files (*.xml)|*.xml|All files (*)|*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||
#endif
|
||
|
||
if (file.ShowModal() == wxID_OK)
|
||
{
|
||
txtXmlFile->SetValue(file.GetPath());
|
||
OnChange(ev);
|
||
}
|
||
}
|
||
}
|
||
|
||
void frmReport::SetReportTitle(const wxString &t)
|
||
{
|
||
txtTitle->SetValue(t);
|
||
}
|
||
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// START STYLESHEET FUNCTIONS
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
wxString frmReport::GetCssLink(const wxString &file)
|
||
{
|
||
wxString data;
|
||
|
||
data = wxT(" <link rel=\"stylesheet\" href=\"") + file + wxT("\" type=\"text/css\" />\n");
|
||
|
||
return data;
|
||
}
|
||
|
||
wxString frmReport::GetEmbeddedCss(const wxString &css)
|
||
{
|
||
wxString data;
|
||
|
||
data = wxT(" <style type=\"text/css\">\n");
|
||
data += css;
|
||
data += wxT(" </style>\n");
|
||
|
||
return data;
|
||
}
|
||
|
||
const wxString frmReport::GetDefaultCss()
|
||
{
|
||
wxString data;
|
||
|
||
data = wxT(" body { font-family: verdana, helvetica, sans-serif; margin: 0px; padding: 0; }\n")
|
||
wxT(" h1 { font-weight: bold; font-size: 150%; border-bottom-style: solid; border-bottom-width: 2px; margin-top: 0px; padding-bottom: 0.5ex; color: #eeeeee; overflow: hidden; text-overflow: ellipsis; }\n")
|
||
wxT(" h2 { font-size: 130%; padding-bottom: 0.5ex; color: ") + appearanceFactory->GetReportKeyColour().GetAsString(wxC2S_HTML_SYNTAX) + wxT("; border-bottom-style: solid; border-bottom-width: 2px; }\n")
|
||
wxT(" h3 { font-size: 110%; padding-bottom: 0.5ex; color: #000000; }\n")
|
||
wxT(" th { text-align: left; background-color: ") + appearanceFactory->GetReportKeyColour().GetAsString(wxC2S_HTML_SYNTAX) + wxT("; color: #eeeeee; }\n")
|
||
wxT(" .tableFixHead {\n")
|
||
wxT(" overflow-y: auto; /* make the table scrollable if height is more than 200 px */\n")
|
||
wxT(" height: 300px; /* gives an initial height of 200px to the table */\n")
|
||
wxT(" }\n")
|
||
wxT(" .tableFixHead thead th {\n")
|
||
wxT(" position: sticky; /* make the table heads sticky */\n")
|
||
wxT(" top: 0px; /* table head will be placed from the top of the table and sticks to it */\n")
|
||
wxT(" }\n")
|
||
wxT(" #ReportHeader { padding: 10px; background-color: ") + appearanceFactory->GetReportKeyColour().GetAsString(wxC2S_HTML_SYNTAX) + wxT("; color: #eeeeee; border-bottom-style: solid; border-bottom-width: 2px; border-color: #999999; }\n")
|
||
wxT(" #ReportHeader th { width: 25%; white-space: nowrap; vertical-align: top; }\n")
|
||
wxT(" #ReportHeader td { vertical-align: top; color: #eeeeee; }\n")
|
||
wxT(" #ReportNotes { padding: 10px; background-color: #eeeeee; font-size: 80%; border-bottom-style: solid; border-bottom-width: 2px; border-color: #999999; }\n")
|
||
wxT(" .ReportSQL { margin-bottom: 10px; padding: 10px; display: block; background-color: #eeeeee; font-family: monospace; }\n")
|
||
wxT(" #ReportDetails { margin-left: 10px; margin-right: 10px; margin-bottom: 10px; }\n")
|
||
wxT(" #ReportDetails td, th { font-size: 80%; margin-left: 2px; margin-right: 2px; }\n")
|
||
wxT(" #ReportDetails th { border-bottom-color: #777777; border-bottom-style: solid; border-bottom-width: 2px; }\n")
|
||
wxT(" .ReportDetailsOddDataRow { background-color: #dddddd; }\n")
|
||
wxT(" .ReportDetailsEvenDataRow { background-color: #eeeeee; }\n")
|
||
wxT(" .ReportDetailsFootDataRow { font-weight: bold; background-color: #cccccc; }\n")
|
||
wxT(" .ReportTableHeaderCell { background-color: #dddddd; color: ") + appearanceFactory->GetReportKeyColour().GetAsString(wxC2S_HTML_SYNTAX) + wxT("; vertical-align: top; font-size: 80%; white-space: nowrap; }\n")
|
||
wxT(" .ReportTableValueCell { vertical-align: top; font-size: 80%; white-space: nowrap; padding: 2px; }\n")
|
||
wxT(" .ReportTableInfo { font-size: 80%; font-style: italic; }\n")
|
||
wxT(" #ReportFooter { font-weight: bold; font-size: 80%; text-align: right; background-color: ") + appearanceFactory->GetReportKeyColour().GetAsString(wxC2S_HTML_SYNTAX) + wxT("; color: #eeeeee; margin-top: 10px; padding: 2px; border-bottom-style: solid; border-bottom-width: 2px; border-top-style: solid; border-top-width: 2px; border-color: #999999; }\n")
|
||
wxT(" #ReportFooter a { color: #ffffff; text-decoration: none; }\n");
|
||
|
||
return data;
|
||
}
|
||
|
||
wxString frmReport::GetDefaultXsl(const wxString &css)
|
||
{
|
||
wxString data;
|
||
|
||
data = wxT("<?xml version=\"1.0\"?>\n")
|
||
wxT("<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n")
|
||
wxT("<xsl:output method=\"xml\" doctype-system=\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" doctype-public=\"-//W3C//DTD XHTML 1.0 Transitional//EN\" indent=\"yes\" encoding=\"utf-8\" />\n")
|
||
wxT("<xsl:template match=\"/report\">\n")
|
||
wxT("\n")
|
||
wxT("<html>\n")
|
||
wxT(" <head>\n")
|
||
wxT(" <xsl:if test=\"header/title != ''\">\n")
|
||
wxT(" <title><xsl:value-of select=\"header/title\" /></title>\n")
|
||
wxT(" </xsl:if>\n")
|
||
wxT(" <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n");
|
||
data += css;
|
||
data += wxT(" </head>\n")
|
||
wxT("\n")
|
||
wxT(" <body>\n")
|
||
wxT(" <div id=\"ReportHeader\">\n")
|
||
wxT("\n")
|
||
wxT(" <xsl:if test=\"header/title != ''\">\n")
|
||
wxT(" <h1><xsl:value-of select=\"header/title\" /></h1>\n")
|
||
wxT(" </xsl:if>\n")
|
||
wxT("\n")
|
||
wxT(" <xsl:if test=\"header/generated != ''\">\n")
|
||
wxT(" <b>");
|
||
data += _("Generated");
|
||
data += wxT(": </b><xsl:value-of select=\"header/generated\" /><br />\n")
|
||
wxT(" </xsl:if>\n")
|
||
wxT("\n")
|
||
wxT(" <xsl:if test=\"header/server != ''\">\n")
|
||
wxT(" <b>");
|
||
data += _("Server");
|
||
data += wxT(": </b><xsl:value-of select=\"header/server\" /><br />\n")
|
||
wxT(" </xsl:if>\n")
|
||
wxT("\n")
|
||
wxT(" <xsl:if test=\"header/database != ''\">\n")
|
||
wxT(" <b>");
|
||
data += _("Database");
|
||
data += wxT(": </b><xsl:value-of select=\"header/database\" /><br />\n")
|
||
wxT(" </xsl:if>\n")
|
||
wxT("\n")
|
||
wxT(" <xsl:if test=\"header/catalog != ''\">\n")
|
||
wxT(" <b>");
|
||
data += _("Catalog");
|
||
data += wxT(": </b><xsl:value-of select=\"header/catalog\" /><br />\n")
|
||
wxT(" </xsl:if>\n")
|
||
wxT("\n")
|
||
wxT(" <xsl:if test=\"header/schema != ''\">\n")
|
||
wxT(" <b>");
|
||
data += _("Schema");
|
||
data += wxT(": </b><xsl:value-of select=\"header/schema\" /><br />\n")
|
||
wxT(" </xsl:if>\n")
|
||
wxT("\n")
|
||
wxT(" <xsl:if test=\"header/table != ''\">\n")
|
||
wxT(" <b>");
|
||
data += _("Table");
|
||
data += wxT(": </b><xsl:value-of select=\"header/table\" /><br />\n")
|
||
wxT(" </xsl:if>\n")
|
||
wxT("\n")
|
||
wxT(" <xsl:if test=\"header/job != ''\">\n")
|
||
wxT(" <b>");
|
||
data += _("Job");
|
||
data += wxT(": </b><xsl:value-of select=\"header/job\" /><br />\n")
|
||
wxT(" </xsl:if>\n")
|
||
wxT("\n")
|
||
wxT(" </div>\n")
|
||
wxT("\n")
|
||
wxT(" <xsl:if test=\"header/notes != ''\">\n")
|
||
wxT(" <div id=\"ReportNotes\">\n")
|
||
wxT(" <b>");
|
||
data += _("Notes");
|
||
data += wxT(": </b><br /><br />\n")
|
||
wxT(" <xsl:call-template name=\"substitute\">\n")
|
||
wxT(" <xsl:with-param name=\"string\" select=\"header/notes\" />\n")
|
||
wxT(" </xsl:call-template>\n")
|
||
wxT(" </div>\n")
|
||
wxT(" </xsl:if>\n")
|
||
wxT("\n")
|
||
wxT(" <div id=\"ReportDetails\">\n")
|
||
wxT(" <xsl:apply-templates select=\"section\" >\n")
|
||
wxT(" <xsl:sort select=\"@number\" data-type=\"number\" order=\"ascending\" />\n")
|
||
wxT(" </xsl:apply-templates>\n")
|
||
wxT(" </div>\n")
|
||
wxT("\n")
|
||
wxT(" <div id=\"ReportFooter\">\n");
|
||
data += _("Report generated by");
|
||
data += wxT(" <a href=\"");
|
||
data += HtmlEntities(appearanceFactory->GetWebsiteUrl());
|
||
data += wxT("\">");
|
||
data += HtmlEntities(appearanceFactory->GetLongAppName());
|
||
data += wxT("</a>\n")
|
||
wxT(" </div>\n")
|
||
wxT("\n")
|
||
wxT(" <br />\n")
|
||
wxT(" </body>\n")
|
||
wxT("<script>\n")
|
||
wxT("<xsl:comment><xsl:text disable-output-escaping=\"yes\"> <![CDATA[\n")
|
||
wxT("function adjustTableBodyHeight() {\n")
|
||
wxT(" var cnt=document.querySelectorAll('.tableFixHead').length;\n")
|
||
wxT(" if (cnt!=1) {\n")
|
||
wxT(" [...document.querySelectorAll('.tableFixHead')].forEach(el => { el.classList.remove('tableFixHead'); });\n")
|
||
wxT(" } else {\n")
|
||
wxT(" \n")
|
||
wxT(" var tableBody = document.querySelector('.tableFixHead');\n")
|
||
wxT(" var rf=document.getElementById('ReportFooter');\n")
|
||
wxT(" var wh=window.innerHeight;\n")
|
||
wxT(" var availableHeight = wh - tableBody.getBoundingClientRect().top - rf.getBoundingClientRect().height;\n")
|
||
wxT(" tableBody.style.height = Math.max(availableHeight, 100) + 'px';\n")
|
||
wxT(" }\n")
|
||
wxT("}\n")
|
||
wxT("window.onload = adjustTableBodyHeight;\n")
|
||
wxT("window.onresize = adjustTableBodyHeight;\n")
|
||
wxT("]]></xsl:text></xsl:comment>\n")
|
||
wxT("</script>\n")
|
||
wxT("</html>\n")
|
||
wxT("\n")
|
||
wxT("</xsl:template>\n")
|
||
wxT("\n")
|
||
wxT("<xsl:template match=\"section\">\n")
|
||
wxT(" <xsl:if test=\"../section[@id = current()/@id]/@name != ''\">\n")
|
||
wxT(" <h2><xsl:value-of select=\"../section[@id = current()/@id]/@name\" /></h2>\n")
|
||
wxT(" </xsl:if>\n")
|
||
wxT("\n")
|
||
wxT(" <xsl:if test=\"count(../section[@id = current()/@id]/table/columns/column) > 0\">\n")
|
||
wxT(" <div class=\"tableFixHead\" >\n")
|
||
wxT(" <table>\n")
|
||
wxT(" <thead><tr>\n")
|
||
wxT(" <xsl:apply-templates select=\"../section[@id = current()/@id]/table/columns/column\">\n")
|
||
wxT(" <xsl:sort select=\"@number\" data-type=\"number\" order=\"ascending\" />\n")
|
||
wxT(" <xsl:with-param name=\"count\" select=\"count(../section[@id = current()/@id]/table/columns/column)\" />\n")
|
||
wxT(" </xsl:apply-templates>\n")
|
||
wxT(" </tr></thead><tbody>\n")
|
||
wxT(" <xsl:apply-templates select=\"../section[@id = current()/@id]/table/rows/*\" mode=\"rows\">\n")
|
||
wxT(" <xsl:sort select=\"@number\" data-type=\"number\" order=\"ascending\" />\n")
|
||
wxT(" <xsl:with-param name=\"column-meta\" select=\"../section[@id = current()/@id]/table/columns/column\" />\n")
|
||
wxT(" </xsl:apply-templates>\n")
|
||
wxT(" </tbody></table>\n")
|
||
wxT(" </div>\n")
|
||
wxT(" <br />\n")
|
||
wxT(" <xsl:if test=\"../section[@id = current()/@id]/table/info != ''\">\n")
|
||
wxT(" <p class=\"ReportTableInfo\"><xsl:value-of select=\"../section[@id = current()/@id]/table/info\" /></p>\n")
|
||
wxT(" </xsl:if>\n")
|
||
wxT(" </xsl:if>\n")
|
||
wxT("\n")
|
||
wxT(" <xsl:if test=\"../section[@id = current()/@id]/sql != ''\">\n")
|
||
wxT(" <pre class=\"ReportSQL\">\n")
|
||
wxT(" <xsl:call-template name=\"substitute\">\n")
|
||
wxT(" <xsl:with-param name=\"string\" select=\"../section[@id = current()/@id]/sql\" />\n")
|
||
wxT(" </xsl:call-template>\n")
|
||
wxT(" </pre>\n")
|
||
wxT(" </xsl:if>\n")
|
||
wxT("</xsl:template>\n")
|
||
wxT("\n")
|
||
wxT("<xsl:template match=\"column\">\n")
|
||
wxT(" <xsl:param name=\"count\" />\n")
|
||
wxT(" <th class=\"ReportTableHeaderCell\">\n")
|
||
wxT(" <xsl:attribute name=\"width\"><xsl:value-of select=\"100 div $count\" />%</xsl:attribute>\n")
|
||
wxT(" <xsl:call-template name=\"substitute\">\n")
|
||
wxT(" <xsl:with-param name=\"string\" select=\"@name\" />\n")
|
||
wxT(" </xsl:call-template>\n")
|
||
wxT(" </th>\n")
|
||
wxT("</xsl:template>\n")
|
||
wxT("\n")
|
||
wxT("<xsl:template match=\"*\" mode=\"rows\">\n")
|
||
wxT(" <xsl:param name=\"column-meta\" />\n")
|
||
wxT(" <tr>\n")
|
||
wxT(" <xsl:choose>\n")
|
||
wxT(" <xsl:when test=\"(@foot)\">\n")
|
||
wxT(" <xsl:attribute name=\"class\">ReportDetailsFootDataRow</xsl:attribute>\n")
|
||
wxT(" </xsl:when>\n")
|
||
wxT(" <xsl:when test=\"position() mod 2 != 1\">\n")
|
||
wxT(" <xsl:attribute name=\"class\">ReportDetailsOddDataRow</xsl:attribute>\n")
|
||
wxT(" </xsl:when>\n")
|
||
wxT(" <xsl:otherwise>\n")
|
||
wxT(" <xsl:attribute name=\"class\">ReportDetailsEvenDataRow</xsl:attribute>\n")
|
||
wxT(" </xsl:otherwise>\n")
|
||
wxT(" </xsl:choose>\n")
|
||
wxT(" <xsl:apply-templates select=\"$column-meta\" mode=\"cells\">\n")
|
||
wxT(" <xsl:with-param name=\"row\" select=\".\" />\n")
|
||
wxT(" </xsl:apply-templates>\n")
|
||
wxT(" </tr>\n")
|
||
wxT("</xsl:template>\n")
|
||
wxT("\n")
|
||
wxT("<xsl:template match=\"*\" mode=\"cells\">\n")
|
||
wxT(" <xsl:param name=\"row\" />\n")
|
||
wxT(" <td class=\"ReportTableValueCell\">\n")
|
||
wxT(" <xsl:choose>\n")
|
||
wxT(" <xsl:when test=\"$row/@*[name() = current()/@id]|$row/*[name() = current()/@id] != ''\">\n")
|
||
wxT(" <xsl:call-template name=\"substitute\">\n")
|
||
wxT(" <xsl:with-param name=\"string\" select=\"$row/@*[name() = current()/@id]|$row/*[name() = current()/@id]\" />\n")
|
||
wxT(" </xsl:call-template>\n")
|
||
wxT(" </xsl:when>\n")
|
||
wxT(" <xsl:otherwise>\n")
|
||
wxT(" <xsl:text> </xsl:text>\n")
|
||
wxT(" </xsl:otherwise>\n")
|
||
wxT(" </xsl:choose>\n")
|
||
wxT(" </td>\n")
|
||
wxT("</xsl:template>\n")
|
||
wxT("\n")
|
||
wxT("<xsl:template name=\"substitute\">\n")
|
||
wxT(" <xsl:param name=\"string\" />\n")
|
||
wxT(" <xsl:param name=\"from\" select=\"'
'\" />\n")
|
||
wxT(" <xsl:param name=\"to\">\n")
|
||
wxT(" <br />\n")
|
||
wxT(" </xsl:param>\n")
|
||
wxT(" <xsl:choose>\n")
|
||
wxT(" <xsl:when test=\"contains($string, $from)\">\n")
|
||
wxT(" <xsl:value-of select=\"substring-before($string, $from)\" />\n")
|
||
wxT(" <xsl:copy-of select=\"$to\" />\n")
|
||
wxT(" <xsl:call-template name=\"substitute\">\n")
|
||
wxT(" <xsl:with-param name=\"string\" select=\"substring-after($string, $from)\" />\n")
|
||
wxT(" <xsl:with-param name=\"from\" select=\"$from\" />\n")
|
||
wxT(" <xsl:with-param name=\"to\" select=\"$to\" />\n")
|
||
wxT(" </xsl:call-template>\n")
|
||
wxT(" </xsl:when>\n")
|
||
wxT(" <xsl:otherwise>\n")
|
||
wxT(" <xsl:value-of select=\"$string\" />\n")
|
||
wxT(" </xsl:otherwise>\n")
|
||
wxT(" </xsl:choose>\n")
|
||
wxT("</xsl:template>\n")
|
||
wxT("\n")
|
||
wxT("</xsl:stylesheet>\n");
|
||
|
||
return data;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// END STYLESHEET FUNCTIONS
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// START XML FUNCTIONS
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
void frmReport::XmlAddHeaderValue(const wxString &name, const wxString &value)
|
||
{
|
||
header += wxT(" <") + HtmlEntities(name) + wxT(">") + HtmlEntities(value) + wxT("</") + HtmlEntities(name) + wxT(">\n");
|
||
}
|
||
|
||
int frmReport::XmlCreateSection(const wxString &name)
|
||
{
|
||
int ind = sectionName.Add(HtmlEntities(name));
|
||
sectionData.Add(wxT(""));
|
||
sectionTableHeader.Add(wxT(""));
|
||
sectionTableRows.Add(wxT(""));
|
||
sectionTableInfo.Add(wxT(""));
|
||
sectionSql.Add(wxT(""));
|
||
return ind + 1;
|
||
}
|
||
|
||
void frmReport::XmlSetSectionTableHeader(const int section, int columns, const wxChar *name, ...)
|
||
{
|
||
va_list ap;
|
||
const wxChar *p = name;
|
||
wxString data;
|
||
|
||
va_start(ap, name);
|
||
|
||
for (int x = 0; x < columns; x++)
|
||
{
|
||
data += wxT(" <column id=\"c");
|
||
data += NumToStr((long)(x + 1));
|
||
data += wxT("\" number=\"");
|
||
data += NumToStr((long)(x + 1));
|
||
data += wxT("\" name=\"");
|
||
data += HtmlEntities(p);
|
||
data += wxT("\"/>\n");
|
||
p = va_arg(ap, wxChar *);
|
||
}
|
||
|
||
va_end(ap);
|
||
|
||
sectionTableHeader[section - 1] = data;
|
||
}
|
||
|
||
void frmReport::XmlAddSectionTableRow(const int section, int number, int columns, const wxChar *value, ...)
|
||
{
|
||
va_list ap;
|
||
const wxChar *p = value;
|
||
wxString data;
|
||
|
||
va_start(ap, value);
|
||
|
||
data = wxT(" <row id=\"r");
|
||
data += NumToStr((long)number);
|
||
data += wxT("\" number=\"");
|
||
data += NumToStr((long)number);
|
||
data += wxT("\"");
|
||
|
||
for (int x = 0; x < columns; x++)
|
||
{
|
||
data += wxT(" c");
|
||
data += NumToStr((long)(x + 1));
|
||
data += wxT("=\"");
|
||
data += HtmlEntities(p);
|
||
data += wxT("\"");
|
||
p = va_arg(ap, wxChar *);
|
||
}
|
||
|
||
|
||
data += wxT(" />\n");
|
||
|
||
va_end(ap);
|
||
|
||
sectionTableRows[section - 1] += data;
|
||
}
|
||
|
||
void frmReport::XmlAddSectionTableFromListView(const int section, ctlListView *list)
|
||
{
|
||
// Get the column headers
|
||
int cols = list->GetColumnCount();
|
||
wxString data;
|
||
wxListItem itm;
|
||
//double sum = 0;
|
||
//double max = -DBL_MAX;
|
||
//double min = -DBL_MAX;
|
||
std::vector<int> isTypeNumber;
|
||
// Build the columns
|
||
for (int x = 0; x < cols; x++)
|
||
{
|
||
itm.SetMask(wxLIST_MASK_TEXT);
|
||
list->GetColumn(x, itm);
|
||
|
||
wxString label = itm.GetText();
|
||
if (label == _("Size")) isTypeNumber.push_back(2);
|
||
else if (list->IsNumberColumn(label)) isTypeNumber.push_back(1);
|
||
else
|
||
isTypeNumber.push_back(0); // string
|
||
data += wxT(" <column id=\"c");
|
||
data += NumToStr((long)(x + 1));
|
||
data += wxT("\" number=\"");
|
||
data += NumToStr((long)(x + 1));
|
||
data += wxT("\" name=\"");
|
||
data += HtmlEntities(label);
|
||
data += wxT("\" />\n");
|
||
}
|
||
sectionTableHeader[section - 1] = data;
|
||
// Build the rows
|
||
int rows = list->GetItemCount();
|
||
wxArrayString footcols;
|
||
Stats st(rows, cols, isTypeNumber);
|
||
int footrows = st.getCountMetrics();
|
||
|
||
for (int y = 0; y < rows + footrows; y++)
|
||
{
|
||
if (y >= rows) {
|
||
if (y == rows) st.calc();
|
||
// foot rows
|
||
footcols = st.GetRowForTable(y - rows);
|
||
if (footcols.Count() == 0) continue;
|
||
}
|
||
data = wxT(" <row id=\"r");
|
||
data += NumToStr((long)(y + 1));
|
||
data += wxT("\" number=\"");
|
||
data += NumToStr((long)(y + 1));
|
||
data += wxT("\"");
|
||
for (int x = 0; x < cols; x++)
|
||
{
|
||
data += wxT(" c");
|
||
data += NumToStr((long)(x + 1));
|
||
data += wxT("=\"");
|
||
if (y >= rows)
|
||
data += HtmlEntities(footcols[x]);
|
||
else {
|
||
wxString val = list->GetText(y, x);
|
||
if (val == "NaN") val = "0";
|
||
double d = 0;
|
||
int t = isTypeNumber[x];
|
||
if (t == 2) { d = ConvertSizeToMB(val); }
|
||
else
|
||
{
|
||
if (val.ToCDouble(&d)) {
|
||
}
|
||
}
|
||
st.addValue(y, x, d);
|
||
data += HtmlEntities(val);
|
||
}
|
||
data += wxT("\"");
|
||
}
|
||
if (y >= rows) data = data + " foot=\"yes\"";
|
||
data += wxT(" />\n");
|
||
sectionTableRows[section - 1] += data;
|
||
}
|
||
}
|
||
|
||
void frmReport::XmlAddSectionTableFromGrid(const int section, ctlSQLResult *grid)
|
||
{
|
||
// Get the column headers
|
||
int cols = grid->GetNumberCols();
|
||
int shift = 0;
|
||
|
||
wxString data;
|
||
wxListItem itm;
|
||
|
||
if (grid->GetRowCountSuppressed())
|
||
shift = 1;
|
||
|
||
for (int x = 1; x <= cols; x++)
|
||
{
|
||
wxString label = grid->OnGetItemText(-1, x - shift);
|
||
data += wxT(" <column id=\"c");
|
||
data += NumToStr((long)(x));
|
||
data += wxT("\" number=\"");
|
||
data += NumToStr((long)(x));
|
||
data += wxT("\" name=\"");
|
||
data += HtmlEntities(label);
|
||
data += wxT("\" />\n");
|
||
}
|
||
sectionTableHeader[section - 1] = data;
|
||
|
||
// Build the rows
|
||
int rows = grid->NumRows();
|
||
|
||
for (int y = 0; y < rows; y++)
|
||
{
|
||
data = wxT(" <row id=\"r");
|
||
data += NumToStr((long)(y + 1));
|
||
data += wxT("\" number=\"");
|
||
data += NumToStr((long)(y + 1));
|
||
data += wxT("\"");
|
||
|
||
for (int x = 1; x <= cols; x++)
|
||
{
|
||
data += wxT(" c");
|
||
data += NumToStr((long)(x));
|
||
data += wxT("=\"");
|
||
data += HtmlEntities(grid->OnGetItemText(y, x - shift));
|
||
data += wxT("\"");
|
||
}
|
||
data += wxT(" />\n");
|
||
sectionTableRows[section - 1] += data;
|
||
}
|
||
}
|
||
|
||
void frmReport::XmlSetSectionSql(int section, const wxString &sql)
|
||
{
|
||
sectionSql[section - 1] = HtmlEntities(sql);
|
||
|
||
if (!sectionSql[section - 1].IsEmpty())
|
||
chkSql->Enable();
|
||
else
|
||
chkSql->Disable();
|
||
}
|
||
|
||
void frmReport::XmlAddSectionValue(const int section, const wxString &name, const wxString &value)
|
||
{
|
||
sectionData[section - 1] += wxT(" <") + HtmlEntities(name) + wxT(">") + HtmlEntities(value) + wxT("</") + HtmlEntities(name) + wxT(">\n");
|
||
}
|
||
|
||
wxString frmReport::GetSectionTableColumns(const int section)
|
||
{
|
||
wxString data;
|
||
|
||
data = wxT(" <columns>\n");
|
||
data += sectionTableHeader[section - 1];
|
||
data += wxT(" </columns>\n");
|
||
|
||
return data;
|
||
}
|
||
|
||
wxString frmReport::GetSectionTableRows(const int section)
|
||
{
|
||
wxString data;
|
||
|
||
data = wxT(" <rows>\n");
|
||
data += sectionTableRows[section - 1];
|
||
data += wxT(" </rows>\n");
|
||
|
||
return data;
|
||
}
|
||
|
||
wxString frmReport::GetSectionTable(const int section)
|
||
{
|
||
wxString data;
|
||
|
||
data = wxT(" <table>\n");
|
||
data += GetSectionTableColumns(section);
|
||
data += GetSectionTableRows(section);
|
||
|
||
if (!sectionTableInfo[section - 1].IsEmpty())
|
||
{
|
||
data += wxT(" <info>");
|
||
data += sectionTableInfo[section - 1];
|
||
data += wxT("</info>\n");
|
||
}
|
||
|
||
data += wxT(" </table>\n");
|
||
|
||
return data;
|
||
}
|
||
|
||
wxString frmReport::GetSection(const int section)
|
||
{
|
||
wxString data;
|
||
|
||
data = wxT(" <section id=\"s");
|
||
data += NumToStr((long)section);
|
||
data += wxT("\" number=\"");
|
||
data += NumToStr((long)section);
|
||
data += wxT("\" name=\"");
|
||
data += sectionName[section - 1];
|
||
data += wxT("\">\n");
|
||
data += GetSectionTable(section);
|
||
|
||
|
||
if (chkSql->GetValue() && !sectionSql[section - 1].IsEmpty())
|
||
{
|
||
data += wxT(" <sql>");
|
||
data += sectionSql[section - 1];
|
||
data += wxT("</sql>\n");
|
||
}
|
||
|
||
data += sectionData[section - 1];
|
||
data += wxT(" </section>\n");
|
||
|
||
return data;
|
||
}
|
||
|
||
wxString frmReport::GetXmlReport(const wxString &stylesheet = wxT(""))
|
||
{
|
||
wxString data;
|
||
|
||
data = wxT("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||
|
||
if (!stylesheet.IsEmpty())
|
||
{
|
||
data += wxT("<?xml-stylesheet type=\"text/xsl\" href=\"");
|
||
data += stylesheet;
|
||
data += wxT("\" ?>\n");
|
||
}
|
||
|
||
data += wxT("\n");
|
||
data += wxT("<report>\n\n");
|
||
|
||
data += wxT(" <header>\n");
|
||
data += header;
|
||
data += wxT(" </header>\n\n");
|
||
|
||
for (unsigned int x = 1; x <= sectionName.GetCount(); x++ )
|
||
{
|
||
data += GetSection(x);
|
||
data += wxT("\n");
|
||
}
|
||
|
||
data += wxT("</report>\n");
|
||
|
||
return data;
|
||
}
|
||
|
||
//
|
||
// libxml convenience macros
|
||
//
|
||
#define XML_FROM_WXSTRING(s) ((xmlChar *)(const char *)s.mb_str(wxConvUTF8))
|
||
#define WXSTRING_FROM_XML(s) wxString((char *)s, wxConvUTF8)
|
||
|
||
wxString frmReport::XslProcessReport(const wxString &xml, const wxString &xsl)
|
||
{
|
||
xmlChar *output = 0;
|
||
xmlDocPtr ssDoc = 0, xmlDoc = 0, resDoc = 0;
|
||
xsltStylesheetPtr ssPtr = 0;
|
||
int length;
|
||
|
||
wxBeginBusyCursor();
|
||
|
||
// Apply the stylesheet
|
||
xmlSubstituteEntitiesDefault (1); // Substitute entities
|
||
xmlLoadExtDtdDefaultValue = 1; // Load external entities
|
||
|
||
// Parse the stylesheet
|
||
ssDoc = xmlParseDoc(XML_FROM_WXSTRING(xsl));
|
||
if (!ssDoc)
|
||
{
|
||
wxEndBusyCursor();
|
||
wxLogError(_("Failed to parse the XML stylesheet!"));
|
||
goto cleanup;
|
||
}
|
||
|
||
ssPtr = xsltParseStylesheetDoc(ssDoc);
|
||
if (!ssPtr)
|
||
{
|
||
wxEndBusyCursor();
|
||
wxLogError(_("Failed to parse the XSL stylesheet!"));
|
||
goto cleanup;
|
||
}
|
||
|
||
// Parse the data
|
||
xmlDoc = xmlParseDoc(XML_FROM_WXSTRING(xml));
|
||
if (!xmlDoc)
|
||
{
|
||
wxEndBusyCursor();
|
||
wxLogError(_("Failed to parse the XML document!"));
|
||
goto cleanup;
|
||
}
|
||
|
||
// Apply the stylesheet
|
||
resDoc = xsltApplyStylesheet(ssPtr, xmlDoc, NULL);
|
||
if (!resDoc)
|
||
{
|
||
wxEndBusyCursor();
|
||
wxLogError(_("Failed to apply the XSL stylesheet to the XML document!"));
|
||
goto cleanup;
|
||
}
|
||
|
||
// Get the result
|
||
xsltSaveResultToString (&output, &length, resDoc, ssPtr);
|
||
if (!resDoc)
|
||
{
|
||
wxEndBusyCursor();
|
||
wxLogError(_("Failed to read the processed document!"));
|
||
goto cleanup;
|
||
}
|
||
|
||
cleanup:
|
||
|
||
// Cleanup
|
||
if (resDoc)
|
||
xmlFreeDoc(resDoc);
|
||
|
||
if (xmlDoc)
|
||
xmlFreeDoc(xmlDoc);
|
||
|
||
if (ssPtr)
|
||
xsltFreeStylesheet(ssPtr);
|
||
|
||
// This crashes - dunno why :-(
|
||
// if (ssDoc)
|
||
// xmlFreeDoc(ssDoc);
|
||
|
||
xsltCleanupGlobals();
|
||
|
||
wxEndBusyCursor();
|
||
|
||
if (output)
|
||
return WXSTRING_FROM_XML(output);
|
||
else
|
||
return wxEmptyString;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// END XML FUNCTIONS
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
SQL::SQL(const wxString &_sql, const wxString &_pathtree) {
|
||
// Construct a SQL with the specified operation and text.
|
||
sql=wxString(_sql);
|
||
pathtree=wxString(_pathtree) ;
|
||
countchild=0;
|
||
mode=0;
|
||
}
|
||
|
||
SQL::SQL() {
|
||
}
|
||
|
||
/**
|
||
* Display a human-readable version of this SQL.
|
||
* @return text version
|
||
*/
|
||
wxString SQL::toString() const {
|
||
wxString prettyText = sql;
|
||
// Replace linebreaks with Pilcrow signs.
|
||
//prettyText.replace('\n', L'\u00b6');
|
||
wxString c=wxEmptyString;
|
||
wxString cmp=wxEmptyString;
|
||
if (countchild>0) c.Printf(wxT("(%d)"), countchild);
|
||
bool r=false;
|
||
if (sql.Cmp(sql2)!=0) {
|
||
cmp="ne EQ ";
|
||
cmp.Printf(wxT("(ne EQ (%d, %d))"), sql.Len(),sql2.Len());
|
||
}
|
||
if (mode==__Remove) cmp=cmp+" - ";
|
||
if (mode==__Insert) cmp=cmp+" + ";
|
||
if (mode==__Equal) cmp=cmp+" = ";
|
||
return wxString(pathtree+c+"\n"+cmp+"SQL(" + wxString("\""))
|
||
+ prettyText + wxString("\")");
|
||
}
|
||
|
||
/**
|
||
* Is this SQL equivalent to another SQL?
|
||
* @param d Another SQL to compare against
|
||
* @return true or false
|
||
*/
|
||
bool SQL::operator==(const SQL &d) const {
|
||
return (d.sql == this->sql);
|
||
}
|
||
|
||
bool SQL::operator!=(const SQL &d) const {
|
||
return !(operator == (d));
|
||
}
|
||
#include <wx/listimpl.cpp>
|
||
WX_DEFINE_LIST(MyListSql);
|
||
#include "wx/arrimpl.cpp"
|
||
WX_DEFINE_OBJARRAY(ArraySQL)
|
||
wxString reportCompareFactory::GetNodePath(wxTreeItemId node) {
|
||
wxString path;
|
||
path = parent->GetBrowser()->GetItemText(node).Trim();
|
||
|
||
wxTreeItemId parent_id = parent->GetBrowser()->GetItemParent(node);
|
||
while (parent_id.IsOk())
|
||
{
|
||
path = parent->GetBrowser()->GetItemText(parent_id).BeforeFirst('(').Trim() + wxT("/") + path;
|
||
parent_id = parent->GetBrowser()->GetItemParent(parent_id);
|
||
}
|
||
|
||
return path;
|
||
|
||
}
|
||
wxString reportCompareFactory::ApplyCompareOpts(wxString sql,int metatype) {
|
||
if (sql.IsEmpty()) return "";
|
||
bool no_comment = parent->getChoiceSelect(1);
|
||
bool no_priv = parent->getChoiceSelect(0);
|
||
if (no_comment) {
|
||
int last = 0;
|
||
while (last!=-1) {
|
||
last = sql.Find("\nCOMMENT ON");
|
||
int start = last;
|
||
int end = -1;
|
||
while (last!=-1) {
|
||
last++;
|
||
if (sql.Length() == last) { last = -1; continue; }
|
||
wxChar c = sql.GetChar(last);
|
||
if (c == '\'') {
|
||
last++;
|
||
if (sql.GetChar(last) == '\'') { continue; }
|
||
if (sql.GetChar(last) == ';') { end = last; last = -1; continue; }
|
||
|
||
}
|
||
};
|
||
if (end != -1) {
|
||
sql=sql.Remove(start, end - start+1);
|
||
last = 0;
|
||
}
|
||
}
|
||
|
||
}
|
||
if (no_priv &&(metatype != PGM_ROLE)) {
|
||
int last = 0;
|
||
while (last != -1) {
|
||
last = sql.Find("\nGRANT ");
|
||
if (last==-1) last = sql.Find("\nREVOKE ");
|
||
int start = last;
|
||
int end = -1;
|
||
while (last != -1) {
|
||
last++;
|
||
if (sql.Length() == last) { last = -1; continue; }
|
||
wxChar c = sql.GetChar(last);
|
||
if (c == ';') {
|
||
end = last; last = -1;
|
||
}
|
||
};
|
||
if (end != -1) {
|
||
sql = sql.Remove(start, end - start + 1);
|
||
last = 0;
|
||
}
|
||
}
|
||
|
||
}
|
||
return sql;
|
||
|
||
}
|
||
void reportCompareFactory::GetExpandedChildNodes(wxTreeItemId node, wxArrayString &expandedNodes, ArraySQL &list, time_t *t, wxBusyInfo *w, MyHashSQL &h_path,int lvl)
|
||
{
|
||
wxTreeItemIdValue cookie;
|
||
ctlTree *browser=parent->GetBrowser();
|
||
wxTreeItemId child;
|
||
if (lvl==0) child = node;
|
||
else child = browser->GetFirstChild(node, cookie);
|
||
pgObject *obj;
|
||
wxString path;
|
||
int size=expandedNodes.Count();
|
||
while (child.IsOk())
|
||
{
|
||
obj=browser->GetObject(child);
|
||
if (obj && obj->GetMetaType()==PGM_FUNCTION && !browser->HasChildren(child))
|
||
path=GetNodePath(child);
|
||
else
|
||
path=parent->GetNodePath(child);
|
||
|
||
// tmp=wxDateTime::GetTimeNow();
|
||
// if (difftime(tmp,*t)>2.0) {
|
||
//w->~wxBusyInfo();
|
||
//wxSafeYield();
|
||
//wxMilliSleep(50);
|
||
//wxSafeYield();
|
||
//w = new wxBusyInfo(wxString::Format("Path = %s ,GetName() = %s, isCollection = %d",
|
||
// path.c_str(), obj->GetName().c_str(), obj->IsCollection()),parent);
|
||
//wxSafeYield();
|
||
//wxMilliSleep(50);
|
||
//wxSafeYield();
|
||
// *t=tmp;
|
||
// }
|
||
if (obj) {
|
||
// OutputDebugString(wxString::Format("Path = %s ,GetName() = %s, isCollection = %d\n",
|
||
// path.c_str(), obj->GetTypeName().c_str(), obj->IsCollection()));
|
||
if (obj->GetMetaType()==PGM_CATALOG
|
||
||obj->GetMetaType()==PGM_COLUMN
|
||
||obj->GetMetaType()==PGM_RULE
|
||
||obj->GetMetaType()==PGM_CATALOG
|
||
) {
|
||
child = browser->GetNextChild(node, cookie);
|
||
continue;
|
||
}
|
||
//obj->ShowTreeDetail(browser);
|
||
//obj->ShowTree(parent,browser);
|
||
// если надо объекто сложный и сас состоит из коллекций
|
||
if ((obj->GetMetaType()==PGM_SCHEMA
|
||
||obj->GetMetaType()==PGM_DATABASE
|
||
||obj->GetMetaType()==PGM_TABLE
|
||
||obj->GetMetaType()==PGM_FOREIGNTABLE
|
||
)&&!obj->IsCollection()) {
|
||
obj->ShowTreeDetail(browser);
|
||
//obj->ShowTree(parent,browser);
|
||
} else
|
||
{
|
||
if (obj->GetMetaType() == PGM_VIEW) obj->ShowTreeDetail(browser); // только для того чтобы получить инфу о триггерах
|
||
if (obj->GetMetaType() == PGM_EVENTTRIGGER) // получаем инфу о тригеррах по событиям
|
||
obj->ShowTreeDetail(browser);
|
||
}
|
||
}
|
||
|
||
//if (browser->IsExpanded(child))
|
||
if (browser->HasChildren(child))
|
||
{
|
||
bool rec=true;
|
||
if (obj && (obj->GetMetaType()==PGM_TABLE
|
||
//||obj->GetMetaType()==PGM_VIEW
|
||
)) {
|
||
wxTreeItemId Item = browser->GetItemParent(child);
|
||
obj=browser->GetObject(Item); // Tables
|
||
wxTreeItemId Item2 = browser->GetItemParent(obj->GetId());
|
||
obj=browser->GetObject(Item2); // Schemes
|
||
if (obj && obj->GetMetaType()==PGM_SCHEMA&& !obj->IsCollection()) {
|
||
rec=false; // не собираем инфу по сек. таблицам и секциям, и во внутрь не заходим
|
||
obj=browser->GetObject(child);
|
||
obj->ShowTreeDetail(browser);
|
||
} else obj=browser->GetObject(child);
|
||
}
|
||
if (obj && (obj->GetMetaType()==PGM_VIEW && !obj->IsCollection())) rec=false;
|
||
if (rec) {
|
||
GetExpandedChildNodes(child, expandedNodes,list,t,w,h_path,lvl+1);
|
||
//expandedNodes.Add(parent->GetNodePath(child));
|
||
obj=browser->GetObject(child);
|
||
}
|
||
|
||
}
|
||
if (obj ) {
|
||
wxString s=obj->GetSql(browser);
|
||
if (obj->GetMetaType()==PGM_SEQUENCE) s="";
|
||
s = ApplyCompareOpts(s, obj->GetMetaType());
|
||
int c=browser->GetChildrenCount(child,false);
|
||
if (size>0) {
|
||
wxString srcpath(path);
|
||
SQL *sq;
|
||
srcpath.Replace(expandedNodes[0],expandedNodes[2],false);
|
||
srcpath.Replace(expandedNodes[1],expandedNodes[3],false);
|
||
MyHashSQL::iterator it=h_path.find(srcpath);
|
||
if (h_path.end()==it) {
|
||
// не найдено в первой БД
|
||
|
||
if (s!=wxEmptyString) {
|
||
sq =new SQL(wxEmptyString,srcpath);
|
||
sq->sql2=s;
|
||
sq->mode=__Remove;
|
||
list.Add(sq);
|
||
|
||
} else
|
||
{
|
||
sq =new SQL(wxEmptyString,srcpath);
|
||
sq->sql2=wxEmptyString;
|
||
sq->countchild=c;
|
||
sq->mode=__Remove;
|
||
list.Add(sq);
|
||
|
||
}
|
||
|
||
h_path[srcpath]=list.GetCount()-1;
|
||
}
|
||
else {
|
||
int i=it->second;
|
||
SQL& sql=list.Item(i);
|
||
sql.mode=__Equal;
|
||
sql.sql2=s;
|
||
}
|
||
} else {
|
||
if (s!=wxEmptyString) {
|
||
SQL *sq =new SQL(s,path);
|
||
sq->mode=__Insert;
|
||
list.Add(sq);
|
||
} else
|
||
{
|
||
SQL *sq =new SQL(s,path);
|
||
sq->countchild=c;
|
||
sq->mode=__Insert;
|
||
list.Add(sq);
|
||
}
|
||
h_path[path]=list.GetCount()-1;
|
||
}
|
||
}
|
||
|
||
child = browser->GetNextChild(node, cookie);
|
||
}
|
||
|
||
}
|
||
|
||
wxWindow *reportCompareFactory::StartDialog(frmMain *form, pgObject *obj)
|
||
{
|
||
parent = form;
|
||
// std::wstring r;
|
||
// std::wstring str1=L"";
|
||
// std::wstring str2=L"line#\nline2\nADD line 3\nline4\n";
|
||
// r=printdiff(str1,str2);
|
||
// return 0;
|
||
|
||
// wxBeginBusyCursor();
|
||
//frmReport *report = new frmReport(GetFrmMain());
|
||
//wxBusyInfo *waiting;
|
||
wxString msg;
|
||
// Generate the report header
|
||
wxDateTime now = wxDateTime::Now();
|
||
|
||
ctlTree *browser=form->GetBrowser();
|
||
wxTreeItemIdValue foldercookie;
|
||
wxTreeItemId folderitem = browser->GetFirstChild(browser->GetRootItem(), foldercookie);
|
||
wxString path(form->GetNodePath(obj->GetId()));
|
||
wxString src_serverGroupname=path.AfterFirst('/').BeforeFirst('/');
|
||
wxString trg_serverGroupname;
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>/<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>/serverN/Datebases/dbname
|
||
// p1 p2 p3
|
||
wxString p_db;
|
||
int p1=path.Find('/');
|
||
if (p1<0) return 0;
|
||
int p2=path.find('/',p1+1);
|
||
if (p2<0) return 0;
|
||
startpathpos=p2;
|
||
int p3=path.find('/',p2+1);
|
||
wxString p_pref=path.substr(0,p2);
|
||
wxString p_server_obj;
|
||
if (!obj->GetConnection()) return 0;
|
||
if (obj && obj->GetDatabase() && obj->GetDatabase()->GetConnected())
|
||
p_db=obj->GetDatabase()->GetName();
|
||
else
|
||
p_db=obj->GetServer()->GetDatabaseName();
|
||
|
||
if (p3<0) {
|
||
// select server
|
||
//if (wxMessageBox(wxString::Format("Path = %s ,GetName() = %s, isCollection = %d",
|
||
// path.c_str(), obj->GetTypeName().c_str(), obj->IsCollection()), _("Close"), wxYES_NO | wxICON_QUESTION) != wxYES)
|
||
//{
|
||
// return 0;
|
||
//}
|
||
//Группы серверов/Серверы/PostgreSQL 9.6
|
||
// используем первую попавшуюся открытую БД
|
||
p_server_obj = path.substr(p2); // с /серверN/
|
||
|
||
}
|
||
else
|
||
{
|
||
// Группы серверов/Серверы/PostgreSQL 9.6/Базы данных/postgres
|
||
p_server_obj=path.substr(p2,p3-p2); // <20> /<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>N/
|
||
}
|
||
wxString p_db_replace=_("Databases")+"/"+p_db+"/";
|
||
wxString p_server_replace=src_serverGroupname+p_server_obj;
|
||
pgServer *server;
|
||
pgDatabase *db=NULL,*lastdb=NULL;
|
||
wxString trg_db_replace;
|
||
wxString trg_server_replace;
|
||
|
||
wxTreeItemId srvitem = obj->GetServer()->GetId();
|
||
|
||
while (folderitem)
|
||
{
|
||
if (browser->ItemHasChildren(folderitem))
|
||
{
|
||
wxTreeItemIdValue servercookie;
|
||
wxTreeItemId serveritem = browser->GetFirstChild(folderitem, servercookie);
|
||
while (serveritem)
|
||
{
|
||
server = (pgServer *)browser->GetItemData(serveritem);
|
||
if (server != NULL && server->IsCreatedBy(serverFactory))
|
||
{
|
||
trg_server_replace=browser->GetItemText(server->GetId()).BeforeFirst('(').Trim();
|
||
if (srvitem!=server->GetId() && server->GetConnected()) {
|
||
// наше соединение не нужно нужно другое и активное
|
||
pgCollection *coll = browser->FindCollection(databaseFactory, server->GetId());
|
||
if (coll)
|
||
{
|
||
treeObjectIterator dbs(browser, coll);
|
||
while ((db = (pgDatabase *)dbs.GetNextObject()) != 0)
|
||
{
|
||
wxString path(form->GetNodePath(server->GetId()));
|
||
trg_serverGroupname=path.AfterFirst('/').BeforeFirst('/');
|
||
// есть открытая БД
|
||
lastdb=db;
|
||
if (db->GetConnected()) {
|
||
|
||
//if (db->GetName()!=wxT("postgres")) goto ex_out;
|
||
goto ex_out;
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
serveritem = browser->GetNextChild(folderitem, servercookie);
|
||
}
|
||
}
|
||
folderitem = browser->GetNextChild(browser->GetRootItem(), foldercookie);
|
||
}
|
||
ex_out:
|
||
pgObject *trgobj;
|
||
wxString newpath;
|
||
if (lastdb!=NULL) {
|
||
p_db=browser->GetItemText(lastdb->GetId()).BeforeFirst('(').Trim();
|
||
trg_db_replace=_("Databases")+"/"+p_db+"/";
|
||
trg_server_replace=trg_serverGroupname+'/'+trg_server_replace;
|
||
|
||
newpath=path;
|
||
|
||
//if (newpath.Replace(p_server_replace,trg_server_replace,false)==0)
|
||
newpath.Replace(p_server_replace,trg_server_replace,false);
|
||
newpath.Replace(p_db_replace,trg_db_replace,false);
|
||
if (!parent->SetCurrentNode(parent->GetBrowser()->GetRootItem(),newpath)) {
|
||
msg.Printf("Not found object %s in other DB.",newpath);
|
||
wxMessageBox(msg, _("Error"), wxOK | wxICON_INFORMATION);
|
||
return 0;
|
||
}
|
||
trgobj=browser->GetObject(browser->GetSelection());
|
||
} else
|
||
{
|
||
msg="Not open connecting, compare stop.";
|
||
// msg.Printf("В установленном соединении %s, нет подходящих БД.",browser->GetItemText(lastdb->GetServer()->GetId()).BeforeFirst('(').Trim());
|
||
wxMessageBox(msg, _("Error"), wxOK | wxICON_INFORMATION);
|
||
return 0;
|
||
}
|
||
if (!parent->StartChoiceDialog()) return 0;
|
||
time_t timer=wxDateTime::GetTimeNow();
|
||
wxArrayString expandedNodes;
|
||
ArraySQL list;
|
||
MyHashSQL h_path;
|
||
|
||
wxWindowDisabler disableAll;
|
||
{
|
||
#ifndef DEBUG
|
||
wxBusyInfo waiting(wxString::Format(" Reading source DB Path = %s ,Start object = %s",
|
||
browser->GetItemText(obj->GetServer()->GetId()).c_str(), obj->GetName().c_str(),parent));
|
||
// Give the UI a chance to redraw
|
||
wxSafeYield();
|
||
wxMilliSleep(50);
|
||
wxSafeYield();
|
||
#endif
|
||
// waiting->~wxBusyInfo();
|
||
GetExpandedChildNodes(obj->GetId(),expandedNodes,list,&timer, NULL,h_path,0);
|
||
}
|
||
//waiting->~wxBusyInfo();
|
||
|
||
wxFileName fn("");
|
||
fn.MakeAbsolute();
|
||
wxSafeYield();
|
||
wxMilliSleep(50);
|
||
wxSafeYield();
|
||
// return 0;
|
||
|
||
{
|
||
wxBusyInfo waiting(wxString::Format(" Reading target DB Path = %s\n, Start object = %s",
|
||
browser->GetItemText(trgobj->GetId()).c_str(), trgobj->GetName().c_str(),parent));
|
||
// Give the UI a chance to redraw
|
||
wxSafeYield();
|
||
wxMilliSleep(50);
|
||
wxSafeYield();
|
||
timer=wxDateTime::GetTimeNow();
|
||
wxArrayString expandedNodes2;
|
||
expandedNodes2.Add(trg_server_replace);
|
||
expandedNodes2.Add(trg_db_replace);
|
||
expandedNodes2.Add(p_server_replace);
|
||
expandedNodes2.Add(p_db_replace);
|
||
GetExpandedChildNodes(trgobj->GetId(),expandedNodes2,list,&timer, NULL,h_path,0);
|
||
}
|
||
//waiting->~wxBusyInfo();
|
||
|
||
int e_count=list.GetCount();
|
||
int linecount=0;
|
||
int nstart=0,lastpos=0;
|
||
int c,minlen=100000,root=0;
|
||
wxArrayInt* child;
|
||
wxArrayPtrVoid level(15);
|
||
wxHashTable htab(wxKEY_STRING);
|
||
wxString key;
|
||
int npos=p2;
|
||
for (int i = 0; i < e_count; ++i)
|
||
{
|
||
SQL& sq = list.Item(i);
|
||
c=0;
|
||
nstart=0;
|
||
if ((nstart=sq.pathtree.rfind('/'))!=wxNOT_FOUND ) {
|
||
key=sq.pathtree.SubString(npos,nstart-1);
|
||
if (minlen>key.Len()) {minlen=key.Len(); root=i;}
|
||
}
|
||
child=(wxArrayInt *)htab.Get(key);
|
||
if (child==NULL) {
|
||
child=new wxArrayInt;
|
||
htab.Put(key,(wxObject *)child);
|
||
}
|
||
child->Add(i);
|
||
|
||
linecount++;
|
||
}
|
||
|
||
MyListSql::iterator iter2;
|
||
//e_count=list.GetCount();
|
||
//for (int i = 0; i < e_count; ++i)
|
||
//{
|
||
// SQL& sq = list.Item(i);
|
||
//}
|
||
// file2.Write(report, wxConvUTF8);
|
||
// file2.Close();
|
||
|
||
|
||
//return 0;
|
||
titleline=wxEmptyString;
|
||
list_head=wxEmptyString;
|
||
rowlist=wxEmptyString;
|
||
list_end=wxEmptyString;
|
||
tableheader=wxEmptyString;
|
||
head=wxEmptyString;
|
||
tableheader2=wxEmptyString;
|
||
tableshtml=wxEmptyString;
|
||
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
#ifndef _DEBUG
|
||
wxString fDir=wxStandardPaths::Get().GetExecutablePath().BeforeLast('\\')+wxT("\\");
|
||
#else
|
||
wxString fDir=wxStandardPaths::Get().GetExecutablePath().BeforeLast('\\')+wxT("\\");
|
||
#endif
|
||
|
||
wxString f=fDir+"textcompare_report.template";
|
||
wxString buffer;
|
||
if (!wxFileExists(f)) {
|
||
f=dataDir+wxFileName::GetPathSeparator()+"textcompare_report.template";
|
||
}
|
||
wxUtfFile file3(f, wxFile::read, wxFONTENCODING_UTF8);
|
||
if (file3.IsOpened())
|
||
{
|
||
file3.Read(buffer);
|
||
file3.Close();
|
||
} else
|
||
{
|
||
wxLogError(_("Failed to open file %s."), f.c_str());
|
||
return 0;
|
||
}
|
||
buffer.Replace('\r',wxEmptyString,true);
|
||
wxStringTokenizer lines(buffer, wxT("\n"));
|
||
Diff_EditCost=4;
|
||
Match_Threshold=0.5;
|
||
Match_Distance=1000;
|
||
|
||
bool mline=false;
|
||
while (lines.HasMoreTokens())
|
||
{
|
||
wxString tmp = lines.GetNextToken();
|
||
|
||
//if (tmp.length()>0 && tmp[tmp.length()-1]=='\r') tmp.RemoveLast();
|
||
wxString line = tmp.Strip(wxString::both);
|
||
if (tmp.StartsWith("@end@")) break;
|
||
int l=wxString("@titleline").Len();
|
||
if (tmp.StartsWith("@titleline")) {titleline=tmp.Mid(l)+"\n"; continue;}
|
||
l=wxString("@[list").Len();
|
||
if (tmp.StartsWith("@[list")) {list_head=tmp.Mid(l)+"\n"; continue;}
|
||
l=wxString("@]list").Len();
|
||
if (tmp.StartsWith("@]list")) {list_end=tmp.Mid(l)+"\n"; continue;}
|
||
l=wxString("@rowlist").Len();
|
||
if (tmp.StartsWith("@rowlist")) {rowlist=tmp.Mid(l)+"\n"; continue;}
|
||
l=wxString("@tableheader2").Len();
|
||
if (tmp.StartsWith("@tableheader2")) {tableheader2=tmp.Mid(l)+"\n"; continue;}
|
||
|
||
if (tmp==wxString("@tableheader@")) {mline=!mline; continue;}
|
||
if (mline) { tableheader+=tmp+"\n";continue;}
|
||
// diff config
|
||
if (tmp.StartsWith("@Match_Distance=")) {Match_Distance=(int)StrToDouble(tmp.After('=')); continue;}
|
||
if (tmp.StartsWith("@Diff_EditCost=")) {Diff_EditCost=StrToDouble(tmp.After('=')); continue;}
|
||
if (tmp.StartsWith("@Match_Threshold=")) {Match_Threshold=(float)StrToDouble(tmp.After('=')); continue;}
|
||
|
||
head+=tmp+"\n";
|
||
}
|
||
//
|
||
{
|
||
wxBusyInfo waiting(wxString::Format(" Search for differences ...",0));
|
||
// Give the UI a chance to redraw
|
||
wxSafeYield();
|
||
wxMilliSleep(50);
|
||
wxSafeYield();
|
||
|
||
wxString content=printlvl(root,0,list,htab);
|
||
wxString tmp(titleline);
|
||
tmp.Replace("$titleline$","Left : "+path);
|
||
wxString tmp2(titleline);
|
||
tmp2.Replace("$titleline$","Right: "+newpath);
|
||
head.Replace("$titleline$",tmp+tmp2);
|
||
|
||
head.Replace("$list$",content);
|
||
head.Replace("$tables$",tableshtml);
|
||
tableshtml=wxEmptyString;
|
||
content=wxEmptyString;
|
||
}
|
||
|
||
//head+="</div></body></html>";
|
||
fDir=wxStandardPaths::Get().GetTempDir()+wxFileName::GetPathSeparator()+wxT("cmp.html");
|
||
//fn="D:\\PostgreSQL\\cmp.html";
|
||
fn=fDir;
|
||
fn.MakeAbsolute();
|
||
|
||
wxString html(head);
|
||
wxFile file4(fn.GetFullPath(), wxFile::write);
|
||
if (!file4.IsOpened())
|
||
{
|
||
wxLogError(_("Failed to open file %s."), fn.GetFullPath().c_str());
|
||
return 0;
|
||
}
|
||
file4.Write(html, wxConvUTF8);
|
||
file4.Close();
|
||
head=wxEmptyString;
|
||
|
||
list.RemoveAt(0,list.GetCount());
|
||
e_count=list.GetCount();
|
||
for (int i = 0; i < e_count; ++i)
|
||
{
|
||
//SQL *p=&sq;
|
||
//list.RemoveAt(0,list.GetCount());
|
||
// SQL *p = list.Detach(i);
|
||
// delete p;
|
||
}
|
||
//htab.DeleteContents(true);
|
||
h_path.clear();
|
||
//MyHashSQL::iterator it, en;
|
||
//for( it = h_path.begin(), en = h_path.end(); it != en; ++it )
|
||
//{
|
||
// h_path.erase(it);
|
||
//}
|
||
//htab.Clear();
|
||
//list.Empty();
|
||
|
||
#ifdef __WXMSW__
|
||
wxLaunchDefaultBrowser(fn.GetFullPath());
|
||
#else
|
||
wxLaunchDefaultBrowser(wxT("file://") + fn.GetFullPath());
|
||
#endif
|
||
|
||
return 0;
|
||
}
|
||
#if defined(DELETE)
|
||
#undef DELETE
|
||
#endif // DUMMYSTRUCTNAME
|
||
|
||
std::wstring reportCompareFactory::printdiff(std::wstring str1, std::wstring str2 )
|
||
{
|
||
Diff_EditCost = 4;
|
||
Match_Threshold = 0.5;
|
||
Match_Distance = 1000;
|
||
diff_match_patch dmp(Diff_EditCost,Match_Threshold,Match_Distance);
|
||
std::list<Diff> diffs;
|
||
if (str1==str2) {
|
||
return L"";
|
||
}
|
||
diffs=dmp.diff_main(str1,str2,true);
|
||
int nstart=0;
|
||
int pos=0;
|
||
countdiffline=0;
|
||
std::wstring cur_l;
|
||
std::wstring ncur_l; std::wstring p_ncur_l;std::wstring p_ncur_r;
|
||
std::wstring cur_r;
|
||
std::wstring ncur_r;
|
||
std::wstring tex;
|
||
std::wstring t;
|
||
std::wstring tableline;
|
||
int rline=1,lline=1;
|
||
std::list<Diff>::const_iterator it; // объявляем итератор
|
||
it = diffs.begin(); // присваиваем ему начало списка
|
||
Diff aDiff;
|
||
bool modify = false;
|
||
bool oneline = false;
|
||
nstart = 0;
|
||
while (it != diffs.end()) // пока итератор не достигнет конца
|
||
{
|
||
aDiff=*it;
|
||
tex=aDiff.text;
|
||
nstart=0;
|
||
while (nstart<tex.length()) {
|
||
pos=tex.find('\n',nstart);
|
||
if (pos==-1) {t.assign(tex,nstart,tex.length());nstart=tex.length();} else {t.assign(tex,nstart,pos-nstart);nstart=pos;}
|
||
if (t.length()>0) {
|
||
// это всё ещё одна строка
|
||
if (aDiff.operation == Operation::INSERT) cur_r += L"<span class=\"differencei\">" + t + L"</span>";
|
||
if (aDiff.operation == Operation::DELETE) cur_l += L"<span class=\"differenced\">" + t + L"</span>";
|
||
if (aDiff.operation == Operation::EQUAL) {
|
||
cur_r += t;
|
||
cur_l += t;
|
||
}
|
||
else modify = true;
|
||
// пока не встретим перевод строки считаем что это всё одна строка
|
||
oneline = true;
|
||
}
|
||
else
|
||
{
|
||
// дошли до перевода \n
|
||
nstart=pos+1;
|
||
ncur_l=std::to_wstring(lline);
|
||
ncur_l=L""; ncur_r=L"";
|
||
// if (p_ncur_r==ncur_r) { ncur_r=L""; modify=true;}
|
||
// if (p_ncur_l==ncur_l) { ncur_l=L""; modify=true;}
|
||
|
||
std::wstring t_cur_l=cur_l;
|
||
std::wstring t_cur_r=cur_r;
|
||
if (aDiff.operation==Operation::DELETE) {
|
||
t_cur_r=L"";
|
||
ncur_l=std::to_wstring(lline);
|
||
modify=true;
|
||
lline++;
|
||
} else if (aDiff.operation==Operation::INSERT) {
|
||
t_cur_l=L"";
|
||
ncur_r=std::to_wstring(rline);
|
||
modify=true;
|
||
rline++;
|
||
} else if (aDiff.operation==Operation::EQUAL) {
|
||
ncur_r=std::to_wstring(rline);
|
||
ncur_l=std::to_wstring(lline);
|
||
rline++; lline++;
|
||
}
|
||
if (modify) countdiffline++;
|
||
// if (( (ncur_r.empty()&&(!ncur_l.empty()))
|
||
// ||(ncur_l.empty()&&(!ncur_r.empty()))
|
||
// )&&(!modify)) modify=true;
|
||
// create columns
|
||
//left
|
||
tableline+=L"<tr><td class=\"diff_next\" onclick=\"c(this)\"></td>";
|
||
tableline+= modify ? L"<td class=\"has_difference\" onclick=\"d(this)\">"+ncur_l+"</td>" : L"<td class=\"diff_header\" onclick=\"d(this)\">"+ncur_l+"</td>";
|
||
tableline+=L"<td class=\"lineContent\"><pre>"+t_cur_l+"</pre></td>";
|
||
// right
|
||
tableline+=L"<td class=\"diff_next\" onclick=\"c(this)\"></td>";
|
||
tableline+= modify ? L"<td class=\"has_difference\" onclick=\"d(this)\">"+ncur_r+"</td>" : L"<td class=\"diff_header\" onclick=\"d(this)\">"+ncur_r+"</td>";
|
||
tableline+=L"<td class=\"lineContent\"><pre>"+t_cur_r+"</pre></td>";
|
||
tableline+=L"</tr>";
|
||
|
||
|
||
|
||
if (ncur_r.length()>0) p_ncur_r=ncur_r;
|
||
if (ncur_l.length()>0) p_ncur_l=ncur_l;
|
||
|
||
if (aDiff.operation==Operation::DELETE) {
|
||
cur_l=L"";
|
||
} else if (aDiff.operation==Operation::INSERT) {
|
||
cur_r=L"";
|
||
} else if (aDiff.operation==Operation::EQUAL) {
|
||
cur_r=L"";cur_l=L"";
|
||
}
|
||
|
||
//
|
||
modify=false;
|
||
oneline=false;
|
||
}
|
||
} // цикл по строкам внутри одного Diff
|
||
++it;
|
||
}
|
||
#ifdef _DEBUG
|
||
wxFileName fn("cmp_debug.txt");
|
||
fn.MakeAbsolute();
|
||
|
||
wxFile file(fn.GetFullPath(), wxFile::write);
|
||
if (!file.IsOpened())
|
||
{
|
||
wxLogError(_("Failed to open file %s."), fn.GetFullPath().c_str());
|
||
}
|
||
|
||
wxString report;
|
||
report.Append(wxString::Format(" Table\n%s\n",tableline));
|
||
file.Write(report, wxConvUTF8);
|
||
file.Close();
|
||
|
||
#endif
|
||
|
||
return tableline;
|
||
}
|
||
wxString reportCompareFactory::printlvl(int element,int lvl,ArraySQL &list, wxHashTable &htab)
|
||
{
|
||
wxString l(list_head);
|
||
|
||
wxString r=wxEmptyString;
|
||
wxArrayInt* child;
|
||
wxString key,name;
|
||
int e=element;
|
||
int nstart;
|
||
SQL& sq=list.Item(e);
|
||
if ((nstart=sq.pathtree.rfind('/'))!=wxNOT_FOUND ) {
|
||
name=sq.pathtree.AfterLast('/');
|
||
key=sq.pathtree.Mid(startpathpos);
|
||
|
||
}
|
||
wxString tid=wxString::Format("id%d",e);
|
||
wxString rlist=HtmlEntities(name);
|
||
wxString cdiff=wxEmptyString;
|
||
// таблица различий
|
||
countdiffline = 0;
|
||
if (sq.sql.length() + sq.sql2.length() > 0) {
|
||
// для одинаковых не будем таблицу формировать
|
||
std::wstring t1=sq.sql.wc_str();
|
||
std::wstring t2(sq.sql2.wc_str());
|
||
t1.erase(std::remove(t1.begin(), t1.end(), '\r'), t1.end());
|
||
t2.erase(std::remove(t2.begin(), t2.end(), '\r'), t2.end());
|
||
std::wstring rez=printdiff(t1,t2);
|
||
if (rez.length()>0) {
|
||
//L"</tbody></table>"
|
||
//tableshtml
|
||
wxString tmp(tableheader);
|
||
tmp.Replace("@idtablecmp@",tid);
|
||
tmp.Replace("$rowlist$",rlist);
|
||
tableshtml+=tmp+"\n";
|
||
tmp=tableheader2;
|
||
tmp.Replace("@idtablecmp@",tid);
|
||
tmp.Replace("$rowlist$",rlist);
|
||
tableshtml+=tmp;
|
||
tableshtml+=rez;
|
||
tableshtml+="</tbody></table>\n";
|
||
cdiff=wxString::Format(" ( %d )",countdiffline);
|
||
}
|
||
}
|
||
|
||
// список объектов
|
||
child=(wxArrayInt *)htab.Get(key);
|
||
r=rowlist;
|
||
r.Replace("$rowlist$",rlist+cdiff);
|
||
if (sq.mode==__Insert) r.Replace("@color@","insert");
|
||
else if (sq.mode==__Remove) r.Replace("@color@","remove");
|
||
else if (countdiffline>0) r.Replace("@color@","ne"); else {
|
||
if (child!=NULL) r.Replace("@color@","eq"); else r.Replace("@color@","eqhidden");
|
||
}
|
||
r.Replace("@idtablecmp@",tid);
|
||
l+=r;
|
||
if (child!=NULL) {
|
||
for (int j=0;j<child->GetCount();j++) {
|
||
l+=printlvl(child->Item(j),lvl+1,list,htab);
|
||
}
|
||
}
|
||
l+=list_end;
|
||
return l;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////
|
||
// Report base
|
||
///////////////////////////////////////////////////////
|
||
wxWindow *reportBaseFactory::StartDialog(frmMain *form, pgObject *obj)
|
||
{
|
||
parent = form;
|
||
|
||
wxBeginBusyCursor();
|
||
frmReport *report = new frmReport(GetFrmMain());
|
||
|
||
// Generate the report header
|
||
wxDateTime now = wxDateTime::Now();
|
||
report->XmlAddHeaderValue(wxT("generated"), now.Format(wxT("%c")));
|
||
if (obj->GetServer())
|
||
report->XmlAddHeaderValue(wxT("server"), obj->GetServer()->GetFullIdentifier());
|
||
if (obj->GetDatabase())
|
||
report->XmlAddHeaderValue(wxT("database"), obj->GetDatabase()->GetName());
|
||
if (obj->GetSchema())
|
||
{
|
||
if (obj->GetSchema()->GetMetaType() == PGM_CATALOG)
|
||
report->XmlAddHeaderValue(wxT("catalog"), obj->GetSchema()->GetDisplayName());
|
||
else
|
||
report->XmlAddHeaderValue(wxT("schema"), obj->GetSchema()->GetName());
|
||
}
|
||
if (obj->GetJob())
|
||
report->XmlAddHeaderValue(wxT("job"), obj->GetJob()->GetName());
|
||
if (obj->GetTable())
|
||
report->XmlAddHeaderValue(wxT("table"), obj->GetTable()->GetName());
|
||
|
||
GenerateReport(report, obj);
|
||
wxEndBusyCursor();
|
||
|
||
report->ShowModal();
|
||
return 0;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////
|
||
// Properties report
|
||
///////////////////////////////////////////////////////
|
||
reportObjectPropertiesFactory::reportObjectPropertiesFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar)
|
||
: reportBaseFactory(list)
|
||
{
|
||
mnu->Append(id, _("&Properties Report"), _("Generate a Properties report for this object."));
|
||
}
|
||
|
||
bool reportObjectPropertiesFactory::CheckEnable(pgObject *obj)
|
||
{
|
||
if (obj)
|
||
{
|
||
if (obj->IsCollection())
|
||
return false;
|
||
else
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void reportObjectPropertiesFactory::GenerateReport(frmReport *report, pgObject *object)
|
||
{
|
||
report->SetReportTitle(object->GetTranslatedMessage(PROPERTIESREPORT));
|
||
|
||
int section = report->XmlCreateSection(object->GetTranslatedMessage(PROPERTIES));
|
||
|
||
ctlListView *list = GetFrmMain()->GetProperties();
|
||
object->ShowProperties();
|
||
|
||
report->XmlAddSectionTableFromListView(section, list);
|
||
|
||
report->XmlSetSectionSql(section, object->GetSql(NULL));
|
||
}
|
||
|
||
///////////////////////////////////////////////////////
|
||
// DDL report
|
||
///////////////////////////////////////////////////////
|
||
reportObjectDdlFactory::reportObjectDdlFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar)
|
||
: reportBaseFactory(list)
|
||
{
|
||
mnu->Append(id, _("&DDL Report"), _("Generate a DDL report for this object."));
|
||
}
|
||
|
||
bool reportObjectDdlFactory::CheckEnable(pgObject *obj)
|
||
{
|
||
if (obj)
|
||
{
|
||
if (obj->GetMetaType() == PGM_SERVER || obj->GetMetaType() == PGM_CATALOGOBJECT || obj->IsCollection())
|
||
return false;
|
||
else
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void reportObjectDdlFactory::GenerateReport(frmReport *report, pgObject *object)
|
||
{
|
||
report->SetReportTitle(object->GetTranslatedMessage(DDLREPORT));
|
||
|
||
int section = report->XmlCreateSection(object->GetTranslatedMessage(DDL));
|
||
|
||
report->XmlSetSectionSql(section, object->GetSql(NULL));
|
||
}
|
||
|
||
///////////////////////////////////////////////////////
|
||
// Data dictionary report
|
||
///////////////////////////////////////////////////////
|
||
reportObjectDataDictionaryFactory::reportObjectDataDictionaryFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar)
|
||
: reportBaseFactory(list)
|
||
{
|
||
mnu->Append(id, _("&Data Dictionary Report"), _("Generate a Data Dictionary report for this object."));
|
||
}
|
||
|
||
bool reportObjectDataDictionaryFactory::CheckEnable(pgObject *obj)
|
||
{
|
||
if (obj)
|
||
{
|
||
if ((obj->GetMetaType() == PGM_TABLE || obj->GetMetaType() == GP_PARTITION) && !obj->IsCollection())
|
||
return true;
|
||
else
|
||
return false;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void reportObjectDataDictionaryFactory::GenerateReport(frmReport *report, pgObject *object)
|
||
{
|
||
|
||
pgTable *table = (pgTable *)object;
|
||
|
||
report->SetReportTitle(object->GetTranslatedMessage(DATADICTIONNARYREPORT));
|
||
|
||
// Columns
|
||
int section = report->XmlCreateSection(_("Columns"));
|
||
|
||
report->XmlSetSectionTableHeader(section, 6, (const wxChar *) _("Name"), (const wxChar *) _("Data type"), (const wxChar *) _("Not Null?"), (const wxChar *) _("Primary key?"), (const wxChar *) _("Default"), (const wxChar *) _("Comment"));
|
||
|
||
ctlTree *browser = GetFrmMain()->GetBrowser();
|
||
pgCollection *columns = table->GetColumnCollection(browser);
|
||
|
||
treeObjectIterator colIt(browser, columns);
|
||
|
||
pgColumn *column;
|
||
bool haveInherit = false;
|
||
wxString colName;
|
||
while ((column = (pgColumn *)colIt.GetNextObject()) != 0)
|
||
{
|
||
column->ShowTreeDetail(browser);
|
||
if (column->GetColNumber() > 0)
|
||
{
|
||
colName = column->GetName();
|
||
if (column->GetInheritedCount() > 0)
|
||
{
|
||
colName += _("*");
|
||
haveInherit = true;
|
||
}
|
||
|
||
report->XmlAddSectionTableRow(section,
|
||
column->GetColNumber(),
|
||
6,
|
||
(const wxChar *) colName,
|
||
(const wxChar *) column->GetVarTypename(),
|
||
(const wxChar *) BoolToYesNo(column->GetNotNull()),
|
||
(const wxChar *) BoolToYesNo(column->GetIsPK()),
|
||
(const wxChar *) column->GetDefault(),
|
||
(const wxChar *) column->GetComment());
|
||
}
|
||
}
|
||
if (haveInherit)
|
||
{
|
||
wxString info;
|
||
info.Printf(_("* Inherited columns from %s."), table->GetInheritedTables().c_str());
|
||
report->XmlSetSectionTableInfo(section, info);
|
||
}
|
||
|
||
// Constraints
|
||
pgCollection *constraints = table->GetConstraintCollection(browser);
|
||
|
||
treeObjectIterator conIt(browser, constraints);
|
||
|
||
pgObject *constraint;
|
||
long x = 1;
|
||
wxString definition, type;
|
||
while ((constraint = (pgObject *)conIt.GetNextObject()) != 0)
|
||
{
|
||
if (x == 1)
|
||
{
|
||
section = report->XmlCreateSection(_("Constraints"));
|
||
report->XmlSetSectionTableHeader(section, 4, (const wxChar *) _("Name"), (const wxChar *) _("Type"), (const wxChar *) _("Definition"), (const wxChar *) _("Comment"));
|
||
}
|
||
|
||
constraint->ShowTreeDetail(browser);
|
||
|
||
switch (constraint->GetMetaType())
|
||
{
|
||
case PGM_PRIMARYKEY:
|
||
type = _("Primary key");
|
||
definition = ((pgIndexConstraint *)constraint)->GetDefinition();
|
||
break;
|
||
case PGM_UNIQUE:
|
||
type = _("Unique");
|
||
definition = ((pgIndexConstraint *)constraint)->GetDefinition();
|
||
break;
|
||
case PGM_FOREIGNKEY:
|
||
type = _("Foreign key");
|
||
definition = ((pgForeignKey *)constraint)->GetDefinition(PGM_FOREIGNKEY);
|
||
break;
|
||
case PGM_EXCLUDE:
|
||
type = _("Exclude");
|
||
definition = ((pgIndexConstraint *)constraint)->GetDefinition();
|
||
break;
|
||
case PGM_CHECK:
|
||
type = _("Check");
|
||
definition = wxT("(") + ((pgCheck *)constraint)->GetDefinition() + wxT(")");
|
||
break;
|
||
}
|
||
|
||
report->XmlAddSectionTableRow(section,
|
||
x,
|
||
4,
|
||
(const wxChar *) constraint->GetName(),
|
||
(const wxChar *) type,
|
||
(const wxChar *) definition,
|
||
(const wxChar *) constraint->GetComment());
|
||
x++;
|
||
}
|
||
}
|
||
|
||
///////////////////////////////////////////////////////
|
||
// Statistics report
|
||
///////////////////////////////////////////////////////
|
||
reportObjectStatisticsFactory::reportObjectStatisticsFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar)
|
||
: reportBaseFactory(list)
|
||
{
|
||
mnu->Append(id, _("&Statistics Report"), _("Generate a Statistics report for this object."));
|
||
}
|
||
|
||
bool reportObjectStatisticsFactory::CheckEnable(pgObject *obj)
|
||
{
|
||
if (obj)
|
||
{
|
||
if (!obj->HasStats())
|
||
{
|
||
if (obj->IsCollection())
|
||
{
|
||
pgaFactory *f = obj->GetFactory();
|
||
|
||
if (f)
|
||
{
|
||
if (f->GetMetaType() == PGM_TABLE ||
|
||
f->GetMetaType() == GP_PARTITION ||
|
||
f->GetMetaType() == PGM_TABLESPACE ||
|
||
f->GetMetaType() == PGM_DATABASE)
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
return false;
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
void reportObjectStatisticsFactory::GenerateReport(frmReport *report, pgObject *object)
|
||
{
|
||
report->SetReportTitle(object->GetTranslatedMessage(STATISTICSREPORT));
|
||
|
||
int section = report->XmlCreateSection(object->GetTranslatedMessage(OBJSTATISTICS));
|
||
|
||
ctlListView *list = GetFrmMain()->GetStatistics();
|
||
object->ShowStatistics(GetFrmMain(), list);
|
||
|
||
report->XmlAddSectionTableFromListView(section, list);
|
||
}
|
||
|
||
///////////////////////////////////////////////////////
|
||
// Dependencies report
|
||
///////////////////////////////////////////////////////
|
||
reportObjectDependenciesFactory::reportObjectDependenciesFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar)
|
||
: reportBaseFactory(list)
|
||
{
|
||
mnu->Append(id, _("&Dependencies Report"), _("Generate a Dependencies report for this object."));
|
||
}
|
||
|
||
bool reportObjectDependenciesFactory::CheckEnable(pgObject *obj)
|
||
{
|
||
if (obj)
|
||
{
|
||
if (!obj->HasDepends())
|
||
return false;
|
||
else
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void reportObjectDependenciesFactory::GenerateReport(frmReport *report, pgObject *object)
|
||
{
|
||
report->SetReportTitle(object->GetTranslatedMessage(DEPENDENCIESREPORT));
|
||
|
||
int section = report->XmlCreateSection(object->GetTranslatedMessage(DEPENDENCIES));
|
||
|
||
ctlListView *list = GetFrmMain()->GetDependencies();
|
||
object->ShowDependencies(parent, list);
|
||
|
||
report->XmlAddSectionTableFromListView(section, list);
|
||
}
|
||
|
||
///////////////////////////////////////////////////////
|
||
// Dependents report
|
||
///////////////////////////////////////////////////////
|
||
reportObjectDependentsFactory::reportObjectDependentsFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar)
|
||
: reportBaseFactory(list)
|
||
{
|
||
mnu->Append(id, _("&Dependents Report"), _("Generate a Dependents report for this object."));
|
||
}
|
||
|
||
bool reportObjectDependentsFactory::CheckEnable(pgObject *obj)
|
||
{
|
||
if (obj)
|
||
{
|
||
if (!obj->HasReferences())
|
||
return false;
|
||
else
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void reportObjectDependentsFactory::GenerateReport(frmReport *report, pgObject *object)
|
||
{
|
||
report->SetReportTitle(object->GetTranslatedMessage(DEPENDENTSREPORT));
|
||
|
||
int section = report->XmlCreateSection(object->GetTranslatedMessage(DEPENDENTS));
|
||
|
||
ctlListView *list = GetFrmMain()->GetReferencedBy();
|
||
object->ShowDependents(parent, list);
|
||
|
||
report->XmlAddSectionTableFromListView(section, list);
|
||
}
|
||
|
||
///////////////////////////////////////////////////////
|
||
// Object list report
|
||
///////////////////////////////////////////////////////
|
||
reportObjectListFactory::reportObjectListFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar)
|
||
: reportBaseFactory(list)
|
||
{
|
||
mnu->Append(id, _("&Object List Report"), _("Generate an Object List report for this collection."));
|
||
}
|
||
|
||
bool reportObjectListFactory::CheckEnable(pgObject *obj)
|
||
{
|
||
if (obj)
|
||
{
|
||
if (!obj->IsCollection())
|
||
{
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
pgaFactory *f = obj->GetFactory();
|
||
if (obj)
|
||
{
|
||
if (f->GetMetaType() == PGM_SERVER)
|
||
return false;
|
||
}
|
||
else
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void reportObjectListFactory::GenerateReport(frmReport *report, pgObject *object)
|
||
{
|
||
report->SetReportTitle(object->GetTranslatedMessage(OBJECTSLISTREPORT));
|
||
|
||
int section = report->XmlCreateSection(object->GetFullIdentifier());
|
||
|
||
ctlListView *list = GetFrmMain()->GetProperties();
|
||
object->ShowProperties();
|
||
|
||
report->XmlAddSectionTableFromListView(section, list);
|
||
}
|
||
|
||
|