mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 06:05:49 -06:00
1539 lines
32 KiB
C++
1539 lines
32 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// pgAdmin III - PostgreSQL Tools
|
|
//
|
|
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
|
// This software is released under the PostgreSQL Licence
|
|
//
|
|
// misc.cpp - Miscellaneous Utilities
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "pgAdmin3.h"
|
|
|
|
// wxWindows headers
|
|
#include <wx/wx.h>
|
|
#include <wx/app.h>
|
|
#include <wx/timer.h>
|
|
#include <wx/xrc/xmlres.h>
|
|
#include <wx/dir.h>
|
|
#include <wx/file.h>
|
|
#include <wx/textbuf.h>
|
|
#include <wx/help.h>
|
|
#include <wx/html/helpctrl.h>
|
|
#include <wx/fontenc.h>
|
|
#include <wx/display.h>
|
|
|
|
#include "utils/utffile.h"
|
|
#include <locale.h>
|
|
|
|
#ifdef __WXMSW__
|
|
#include <wx/msw/helpchm.h>
|
|
#endif
|
|
|
|
// Standard headers
|
|
#include <stdlib.h>
|
|
|
|
// App headers
|
|
#include "utils/misc.h"
|
|
|
|
#if !defined(PGSCLI)
|
|
|
|
// App headers
|
|
#include "frm/frmMain.h"
|
|
|
|
wxImageList *imageList = 0;
|
|
|
|
#endif // PGSCLI
|
|
|
|
extern "C"
|
|
{
|
|
#define YYSTYPE_IS_DECLARED
|
|
#define DECIMAL DECIMAL_P
|
|
typedef int YYSTYPE;
|
|
#include "parser/keywords.h"
|
|
}
|
|
|
|
// we dont have an appropriate wxLongLong method
|
|
#ifdef __WIN32__
|
|
#define atolonglong _atoi64
|
|
#else
|
|
#ifdef __WXMAC__
|
|
#define atolonglong(str) strtoll(str, (char **)NULL, 10)
|
|
#else
|
|
#ifdef __FreeBSD__
|
|
#define atolonglong(str) strtoll(str, (char **)NULL, 10)
|
|
#else
|
|
#define atolonglong atoll
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
// Conversions
|
|
|
|
|
|
wxString BoolToYesNo(bool value)
|
|
{
|
|
return value ? _("Yes") : _("No");
|
|
}
|
|
|
|
|
|
wxString BoolToStr(bool value)
|
|
{
|
|
return value ? wxT("true") : wxT("false");
|
|
}
|
|
|
|
wxString escapeHtml(wxString text, bool pre) {
|
|
text.Replace("&", "&");
|
|
text.Replace("<", "<");
|
|
text.Replace(">", ">");
|
|
if (!pre) text.Replace("\n", "¶<br>");
|
|
return text;
|
|
}
|
|
|
|
|
|
bool StrToBool(const wxString &value)
|
|
{
|
|
if (value.StartsWith(wxT("t")))
|
|
{
|
|
return true;
|
|
}
|
|
else if (value.StartsWith(wxT("T")))
|
|
{
|
|
return true;
|
|
}
|
|
else if (value.StartsWith(wxT("1")))
|
|
{
|
|
return true;
|
|
}
|
|
else if (value.StartsWith(wxT("Y")))
|
|
{
|
|
return true;
|
|
}
|
|
else if (value.StartsWith(wxT("y")))
|
|
{
|
|
return true;
|
|
}
|
|
else if (value == wxT("on"))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
wxString NumToStr(long value)
|
|
{
|
|
wxString result;
|
|
result.Printf(wxT("%ld"), value);
|
|
return result;
|
|
}
|
|
wxString NumToStr(long long value)
|
|
{
|
|
wxString result;
|
|
result.Printf(wxT("%lld"), value);
|
|
return result;
|
|
}
|
|
|
|
|
|
wxString NumToStr(OID value)
|
|
{
|
|
wxString result;
|
|
result.Printf(wxT("%lu"), (long)value);
|
|
return result;
|
|
}
|
|
|
|
|
|
long StrToLong(const wxString &value)
|
|
{
|
|
return atol(value.ToAscii());
|
|
}
|
|
long long StrTolonglong(const wxString &value)
|
|
{
|
|
return atoll(value.ToAscii());
|
|
}
|
|
|
|
|
|
OID StrToOid(const wxString &value)
|
|
{
|
|
return (OID)strtoul(value.ToAscii(), 0, 10);
|
|
}
|
|
|
|
wxString generate_spaces(int length)
|
|
{
|
|
return wxString().Pad(length);
|
|
}
|
|
|
|
wxString NumToStr(double value)
|
|
{
|
|
wxString result;
|
|
static wxString decsep;
|
|
|
|
if (decsep.Length() == 0)
|
|
{
|
|
decsep.Printf(wxT("%lf"), 1.2);
|
|
decsep = decsep[(unsigned int)1];
|
|
}
|
|
|
|
result.Printf(wxT("%lf"), value);
|
|
result.Replace(decsep, wxT("."));
|
|
|
|
// Get rid of excessive decimal places
|
|
if (result.Contains(wxT(".")))
|
|
while (result.Right(1) == wxT("0"))
|
|
result.RemoveLast();
|
|
if (result.Right(1) == wxT("."))
|
|
result.RemoveLast();
|
|
|
|
return result;
|
|
}
|
|
wxString NumToStrHuman(wxLongLong value) {
|
|
// billion
|
|
long ddiv = 1000000000;
|
|
wxLongLong r = 0;
|
|
wxString s;
|
|
while ((r=value/ddiv)==0) {
|
|
ddiv = ddiv / 1000;
|
|
if (ddiv == 0) return wxEmptyString;
|
|
}
|
|
if (ddiv == 1000000000) s = _("Bi");
|
|
else if (ddiv == 1000000) s = _("Mi");
|
|
else if (ddiv == 1000) s = _("ths");
|
|
else return wxEmptyString;
|
|
|
|
wxLongLong m = value % ddiv;
|
|
m = m / (ddiv / 10);
|
|
wxString rez;
|
|
rez = NumToStr(r) + (m == 0 ? "": "." + NumToStr(m))+" "+s;
|
|
|
|
return rez;
|
|
}
|
|
|
|
wxString NumToStr(wxLongLong value)
|
|
{
|
|
wxString str;
|
|
#if wxCHECK_VERSION(2, 9, 0)
|
|
str.Printf("%" wxLongLongFmtSpec "d", value.GetValue());
|
|
#else
|
|
str.Printf(wxT("%") wxLongLongFmtSpec wxT("d"), value.GetValue());
|
|
#endif
|
|
return str;
|
|
}
|
|
|
|
|
|
double StrToDouble(const wxString &value)
|
|
{
|
|
wxCharBuffer buf = value.ToAscii();
|
|
char *p = (char *)strchr(buf, '.');
|
|
if (p)
|
|
*p = localeconv()->decimal_point[0];
|
|
|
|
return strtod(buf, 0);
|
|
}
|
|
|
|
|
|
wxLongLong StrToLongLong(const wxString &value)
|
|
{
|
|
return atolonglong(value.ToAscii());
|
|
}
|
|
|
|
|
|
wxString DateToAnsiStr(const wxDateTime &datetime)
|
|
{
|
|
if (!datetime.IsValid())
|
|
return wxEmptyString;
|
|
|
|
return datetime.FormatISODate() + wxT(" ") + datetime.FormatISOTime();
|
|
}
|
|
|
|
|
|
wxString DateToStr(const wxDateTime &datetime)
|
|
{
|
|
if (!datetime.IsValid())
|
|
return wxEmptyString;
|
|
|
|
return datetime.FormatDate() + wxT(" ") + datetime.FormatTime();
|
|
}
|
|
|
|
wxString ContrastColorBlackOrWhite(wxColour &bgColor) {
|
|
float k = bgColor.GetRed() * 0.299 + bgColor.GetGreen() * 0.587 + bgColor.GetBlue() * 0.114;
|
|
if (k <= 150) return "#FFFFFF"; else return "#000000";
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="sourcestringsize"> NN MB , N bytes, N kB, N GB</param>
|
|
/// <returns> size MB</returns>
|
|
double ConvertSizeToMB(const wxString& sourcestringsize) {
|
|
double d = 0;
|
|
if (sourcestringsize.ToCDouble(&d)) {
|
|
d = 0;
|
|
}
|
|
else {
|
|
if (sourcestringsize.Right(2) == "kB") d = d / 1024; //kB => MB
|
|
else if (sourcestringsize.Right(2) == "GB") d = d * 1024;//GB => MB
|
|
else if (sourcestringsize.Right(2) == "TB") d = d * 1024 * 1024;//TB => MB
|
|
else if (sourcestringsize.Right(5) == "bytes") d = d / 1024 / 1024; //bytes => MB
|
|
}
|
|
return d;
|
|
}
|
|
wxString ElapsedTimeToStr(wxLongLong msec)
|
|
{
|
|
wxTimeSpan tsMsec(0, 0, 0, msec);
|
|
|
|
int days = tsMsec.GetDays();
|
|
int hours = (wxTimeSpan(tsMsec.GetHours(), 0, 0, 0) - wxTimeSpan(days * 24)).GetHours();
|
|
int minutes = (wxTimeSpan(0, tsMsec.GetMinutes(), 0, 0) - wxTimeSpan(hours)).GetMinutes();
|
|
long seconds = (wxTimeSpan(0, 0, tsMsec.GetSeconds(), 0) - wxTimeSpan(hours, minutes)).GetSeconds().ToLong();
|
|
long milliseconds = (wxTimeSpan(0, 0, 0, tsMsec.GetMilliseconds()) - wxTimeSpan(0, 0, seconds)).GetMilliseconds().ToLong();
|
|
|
|
if (days > 0)
|
|
return wxString::Format(
|
|
wxT("%d %s, %02d:%02d:%02ld hours"),
|
|
days, wxT("days"), hours, minutes, seconds
|
|
);
|
|
else if (hours > 0)
|
|
return wxString::Format(
|
|
wxT("%02d:%02d:%02ld hours"), hours, minutes, seconds
|
|
);
|
|
else if (msec >= 1000 * 60)
|
|
return wxString::Format(wxT("%02d:%02ld minutes"), minutes, seconds);
|
|
else if (msec >= 1000)
|
|
return wxString::Format(
|
|
wxT("%ld.%ld secs"), seconds, milliseconds / 100
|
|
);
|
|
else
|
|
return msec.ToString() + wxT(" msec");
|
|
}
|
|
|
|
wxDateTime StrToDateTime(const wxString &value)
|
|
{
|
|
wxDateTime dt;
|
|
/* This hasn't just been used. ( Is not infinity ) */
|
|
if ( !value.IsEmpty() )
|
|
dt.ParseDateTime(value);
|
|
return dt;
|
|
}
|
|
|
|
#if !defined(PGSCLI)
|
|
|
|
void CheckOnScreen(wxWindow *win, wxPoint &pos, wxSize &size, const int w0, const int h0)
|
|
{
|
|
wxRect rect;
|
|
int scrH, scrW;
|
|
|
|
wxSize screenSize = wxGetDisplaySize();
|
|
scrW = screenSize.x;
|
|
scrH = screenSize.y;
|
|
|
|
if (pos.x > scrW - w0)
|
|
pos.x = scrW - w0;
|
|
if (pos.y > scrH - h0)
|
|
pos.y = scrH - h0;
|
|
|
|
if (pos.x < 0)
|
|
pos.x = 0;
|
|
if (pos.y < 0)
|
|
pos.y = 0;
|
|
|
|
if (size.GetWidth() < w0)
|
|
size.SetWidth(w0);
|
|
if (size.GetHeight() < h0)
|
|
size.SetHeight(h0);
|
|
|
|
if (size.GetWidth() > scrW)
|
|
size.SetWidth(scrW);
|
|
if (size.GetHeight() > scrH)
|
|
size.SetHeight(scrH);
|
|
}
|
|
|
|
#endif // PGSCLI
|
|
|
|
wxString qtConnString(const wxString &value)
|
|
{
|
|
wxString result = value;
|
|
|
|
result.Replace(wxT("\\"), wxT("\\\\"));
|
|
result.Replace(wxT("'"), wxT("\\'"));
|
|
result.Append(wxT("'"));
|
|
result.Prepend(wxT("'"));
|
|
|
|
return result;
|
|
}
|
|
|
|
#if !defined(PGSCLI)
|
|
|
|
wxString IdAndName(long id, const wxString &name)
|
|
{
|
|
wxString str;
|
|
str.Printf(wxT("%ld - %s"), id, name.BeforeFirst('\n').c_str());
|
|
return str;
|
|
}
|
|
|
|
|
|
wxString qtDbStringDollar(const wxString &value)
|
|
{
|
|
wxString qtDefault = wxT("BODY");
|
|
wxString qt = qtDefault;
|
|
int counter = 1;
|
|
if (value.Find('\'') < 0 && value.Find('\n') < 0 && value.Find('\r') < 0)
|
|
{
|
|
wxString ret = value;
|
|
ret.Replace(wxT("\\"), wxT("\\\\"));
|
|
ret.Replace(wxT("'"), wxT("''"));
|
|
ret.Append(wxT("'"));
|
|
ret.Prepend(wxT("'"));
|
|
return ret;
|
|
}
|
|
|
|
while (value.Find(wxT("$") + qt + wxT("$")) >= 0)
|
|
qt.Printf(wxT("%s%d"), qtDefault.c_str(), counter++);
|
|
|
|
|
|
return wxT("$") + qt + wxT("$")
|
|
+ value
|
|
+ wxT("$") + qt + wxT("$");
|
|
}
|
|
|
|
|
|
void FillKeywords(wxString &str)
|
|
{
|
|
// unfortunately, the keyword list is static.
|
|
int i;
|
|
|
|
str = wxString();
|
|
|
|
for (i = 0; i < NumScanKeywords; i++)
|
|
{
|
|
str += wxT(" ") + wxString::FromAscii(ScanKeywords[i].name);
|
|
}
|
|
for (i = 0; i < NumScanKeywordsExtra; i++)
|
|
{
|
|
str += wxT(" ") + wxString::FromAscii(ScanKeywordsExtra[i].name);
|
|
}
|
|
}
|
|
|
|
#endif // PGSCLI
|
|
|
|
static bool needsQuoting(wxString &value, bool forTypes)
|
|
{
|
|
// Is it a number?
|
|
if (value.IsNumber())
|
|
return true;
|
|
else
|
|
{
|
|
// certain types should not be quoted even though it contains a space. Evilness.
|
|
wxString valNoArray;
|
|
if (forTypes && value.Right(2) == wxT("[]"))
|
|
valNoArray = value.Mid(0, value.Len() - 2);
|
|
else
|
|
valNoArray = value;
|
|
|
|
if (forTypes &&
|
|
(!valNoArray.CmpNoCase(wxT("character varying")) ||
|
|
!valNoArray.CmpNoCase(wxT("\"char\"")) ||
|
|
!valNoArray.CmpNoCase(wxT("bit varying")) ||
|
|
!valNoArray.CmpNoCase(wxT("double precision")) ||
|
|
!valNoArray.CmpNoCase(wxT("timestamp without time zone")) ||
|
|
!valNoArray.CmpNoCase(wxT("timestamp with time zone")) ||
|
|
!valNoArray.CmpNoCase(wxT("time without time zone")) ||
|
|
!valNoArray.CmpNoCase(wxT("time with time zone")) ||
|
|
!valNoArray.CmpNoCase(wxT("\"trigger\"")) ||
|
|
!valNoArray.CmpNoCase(wxT("\"unknown\""))))
|
|
return false;
|
|
|
|
int pos = 0;
|
|
while (pos < (int)valNoArray.length())
|
|
{
|
|
wxChar c = valNoArray.GetChar(pos);
|
|
if (!((pos > 0) && (c >= '0' && c <= '9')) &&
|
|
!(c >= 'a' && c <= 'z') &&
|
|
!(c == '_'))
|
|
{
|
|
return true;
|
|
}
|
|
pos++;
|
|
}
|
|
}
|
|
|
|
// is it a keyword?
|
|
const ScanKeyword *sk = ScanKeywordLookup(value.ToAscii());
|
|
if (!sk)
|
|
return false;
|
|
if (sk->category == UNRESERVED_KEYWORD)
|
|
return false;
|
|
if (forTypes && sk->category == COL_NAME_KEYWORD)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
wxString qtTypeIdent(const wxString &value)
|
|
{
|
|
if (value.Length() == 0)
|
|
return value;
|
|
|
|
wxString result = value;
|
|
|
|
if (needsQuoting(result, true))
|
|
{
|
|
result.Replace(wxT("\""), wxT("\"\""));
|
|
return wxT("\"") + result + wxT("\"");
|
|
}
|
|
else
|
|
return result;
|
|
}
|
|
|
|
|
|
wxString qtIdent(const wxString &value)
|
|
{
|
|
if (value.Length() == 0)
|
|
return value;
|
|
|
|
wxString result = value;
|
|
|
|
if (needsQuoting(result, false))
|
|
{
|
|
result.Replace(wxT("\""), wxT("\"\""));
|
|
return wxT("\"") + result + wxT("\"");
|
|
}
|
|
else
|
|
return result;
|
|
}
|
|
|
|
#if !defined(PGSCLI)
|
|
|
|
wxString qtStrip(const wxString &str)
|
|
{
|
|
if (str.Left(2) == wxT("\\\""))
|
|
return str.Mid(2, str.Length() - 4);
|
|
else
|
|
return str;
|
|
}
|
|
|
|
|
|
wxString TransformToNewDatconfig(const wxString &list)
|
|
{
|
|
const wxChar *cp = list.c_str();
|
|
|
|
wxString config = wxEmptyString;
|
|
wxString str = wxEmptyString;
|
|
bool quote = false;
|
|
|
|
while (*cp)
|
|
{
|
|
switch (*cp)
|
|
{
|
|
case '\\':
|
|
{
|
|
if (cp[1] == '"')
|
|
{
|
|
str.Append(*cp);
|
|
cp++;
|
|
str.Append(*cp);
|
|
}
|
|
break;
|
|
}
|
|
case '"':
|
|
case '\'':
|
|
{
|
|
quote = !quote;
|
|
str.Append(*cp);
|
|
break;
|
|
}
|
|
case ',':
|
|
{
|
|
if (!quote)
|
|
{
|
|
if (!config.IsEmpty())
|
|
config += wxT(",");
|
|
config += wxT("=") + str;
|
|
str = wxEmptyString;
|
|
break;
|
|
}
|
|
}
|
|
default:
|
|
{
|
|
str.Append(*cp);
|
|
break;
|
|
}
|
|
}
|
|
cp++;
|
|
}
|
|
|
|
if (!str.IsEmpty())
|
|
{
|
|
if (!config.IsEmpty())
|
|
config += wxT(",");
|
|
config += wxT("=") + str;
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
|
|
void FillArray(wxArrayString &array, const wxString &list)
|
|
{
|
|
const wxChar *cp = list.c_str();
|
|
|
|
wxString str;
|
|
bool quote = false;
|
|
|
|
while (*cp)
|
|
{
|
|
switch (*cp)
|
|
{
|
|
case '\\':
|
|
{
|
|
if (cp[1] == '"')
|
|
{
|
|
cp++;
|
|
str.Append(*cp);
|
|
}
|
|
break;
|
|
}
|
|
case '"':
|
|
case '\'':
|
|
{
|
|
quote = !quote;
|
|
break;
|
|
}
|
|
case ',':
|
|
{
|
|
if (!quote)
|
|
{
|
|
array.Add(str);
|
|
str = wxEmptyString;
|
|
break;
|
|
}
|
|
}
|
|
default:
|
|
{
|
|
str.Append(*cp);
|
|
break;
|
|
}
|
|
}
|
|
cp++;
|
|
}
|
|
if (!str.IsEmpty())
|
|
array.Add(str);
|
|
}
|
|
|
|
|
|
queryTokenizer::queryTokenizer(const wxString &str, const wxChar delim)
|
|
: wxStringTokenizer()
|
|
{
|
|
if (delim == (wxChar)' ')
|
|
SetString(str, wxT(" \n\r\t"), wxTOKEN_RET_EMPTY_ALL);
|
|
else
|
|
SetString(str, delim, wxTOKEN_RET_EMPTY_ALL);
|
|
delimiter = delim;
|
|
}
|
|
|
|
|
|
void AppendIfFilled(wxString &str, const wxString &delimiter, const wxString &what)
|
|
{
|
|
if (!what.IsNull())
|
|
str += delimiter + what;
|
|
}
|
|
|
|
|
|
wxString queryTokenizer::GetNextToken()
|
|
{
|
|
// we need to override wxStringTokenizer, because we have to handle quotes
|
|
wxString str;
|
|
|
|
bool foundQuote = false;
|
|
do
|
|
{
|
|
wxString s = wxStringTokenizer::GetNextToken();
|
|
str.Append(s);
|
|
int quotePos;
|
|
do
|
|
{
|
|
quotePos = s.Find('"');
|
|
if (quotePos >= 0)
|
|
{
|
|
foundQuote = !foundQuote;
|
|
s = s.Mid(quotePos + 1);
|
|
}
|
|
}
|
|
while (quotePos >= 0);
|
|
|
|
if (foundQuote)
|
|
str.Append(delimiter);
|
|
}
|
|
while (foundQuote && HasMoreTokens());
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
wxString FileRead(const wxString &filename, int format)
|
|
{
|
|
wxString str;
|
|
|
|
wxFontEncoding encoding;
|
|
if (format > 0)
|
|
encoding = wxFONTENCODING_UTF8;
|
|
else
|
|
encoding = wxFONTENCODING_DEFAULT;
|
|
|
|
wxUtfFile file(filename, wxFile::read, encoding);
|
|
|
|
if (file.IsOpened())
|
|
file.Read(str);
|
|
|
|
return wxTextBuffer::Translate(str, wxTextFileType_Unix); // we want only \n
|
|
}
|
|
|
|
|
|
bool FileWrite(const wxString &filename, const wxString &data, int format)
|
|
{
|
|
wxFontEncoding encoding = wxFONTENCODING_DEFAULT;
|
|
wxUtfFile file;
|
|
|
|
if (format < 0)
|
|
{
|
|
if (wxFile::Access(filename, wxFile::read))
|
|
{
|
|
file.Open(filename);
|
|
encoding = file.GetEncoding();
|
|
file.Close();
|
|
}
|
|
if (encoding == wxFONTENCODING_DEFAULT)
|
|
encoding = settings->GetUnicodeFile() ? wxFONTENCODING_UTF8 : wxFONTENCODING_SYSTEM;
|
|
}
|
|
else
|
|
encoding = format ? wxFONTENCODING_UTF8 : wxFONTENCODING_SYSTEM;
|
|
|
|
|
|
file.Open(filename, wxFile::write, wxS_DEFAULT, encoding);
|
|
|
|
if (file.IsOpened())
|
|
return file.Write(wxTextBuffer::Translate(data));
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
wxString CleanHelpPath(const wxString &path)
|
|
{
|
|
wxString thePath = path;
|
|
|
|
if (thePath.IsEmpty())
|
|
return wxEmptyString;
|
|
|
|
// If the filename ends in .chm, .hhp, .pdf or .zip, it's
|
|
// a file based help system and we can assumes it's
|
|
// correct.
|
|
if (thePath.Lower().EndsWith(wxT(".hhp")) ||
|
|
#if defined (__WXMSW__) || wxUSE_LIBMSPACK
|
|
thePath.Lower().EndsWith(wxT(".chm")) ||
|
|
#endif
|
|
thePath.Lower().EndsWith(wxT(".pdf")) ||
|
|
thePath.Lower().EndsWith(wxT(".zip")))
|
|
return thePath;
|
|
|
|
// In all other cases we must have a directory
|
|
wxString sep;
|
|
|
|
// Figure out the appropriate seperator
|
|
if (thePath.Lower().Contains(wxT("://")))
|
|
sep = wxT("/");
|
|
else
|
|
sep = wxFileName::GetPathSeparator();
|
|
|
|
// First, correct the path separators
|
|
thePath.Replace(wxT("/"), sep);
|
|
thePath.Replace(wxT("\\"), sep);
|
|
|
|
// Now, append the seperator if it's not there
|
|
if (!thePath.EndsWith(sep))
|
|
thePath += sep;
|
|
|
|
return thePath;
|
|
}
|
|
|
|
bool HelpPathValid(const wxString &path)
|
|
{
|
|
if (path.IsEmpty())
|
|
return true;
|
|
|
|
// If the filename contains any of the sensible URL schemes
|
|
// we just assume it's correct if the end also looks right.
|
|
if ((path.Lower().StartsWith(wxT("http://")) ||
|
|
path.Lower().StartsWith(wxT("https://")) ||
|
|
path.Lower().StartsWith(wxT("file://")) ||
|
|
path.Lower().StartsWith(wxT("ftp://"))) &&
|
|
(path.Lower().EndsWith(wxT(".hhp")) ||
|
|
#if defined (__WXMSW__) || wxUSE_LIBMSPACK
|
|
path.Lower().EndsWith(wxT(".chm")) ||
|
|
#endif
|
|
path.Lower().EndsWith(wxT(".pdf")) ||
|
|
path.Lower().EndsWith(wxT(".zip")) ||
|
|
path.Lower().EndsWith(wxT("/"))))
|
|
return true;
|
|
|
|
// Otherwise, we're looking for a file (or directory) that
|
|
// actually exists.
|
|
wxString sep = wxFileName::GetPathSeparator();
|
|
if (path.Lower().EndsWith(wxT(".hhp")) ||
|
|
#if defined (__WXMSW__) || wxUSE_LIBMSPACK
|
|
path.Lower().EndsWith(wxT(".chm")) ||
|
|
#endif
|
|
path.Lower().EndsWith(wxT(".pdf")) ||
|
|
path.Lower().EndsWith(wxT(".zip")))
|
|
return wxFile::Exists(path);
|
|
else if (path.Lower().EndsWith(sep))
|
|
return wxDir::Exists(path);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void DisplayHelp(const wxString &helpTopic, const HelpType helpType)
|
|
{
|
|
static wxHelpControllerBase *pgHelpCtl = 0;
|
|
static wxHelpControllerBase *edbHelpCtl = 0;
|
|
static wxHelpControllerBase *greenplumHelpCtl = 0;
|
|
static wxHelpControllerBase *slonyHelpCtl = 0;
|
|
static wxString pgInitPath = wxEmptyString;
|
|
static wxString edbInitPath = wxEmptyString;
|
|
static wxString gpInitPath = wxEmptyString;
|
|
static wxString slonyInitPath = wxEmptyString;
|
|
|
|
switch (helpType)
|
|
{
|
|
case HELP_PGADMIN:
|
|
DisplayPgAdminHelp(helpTopic);
|
|
break;
|
|
|
|
case HELP_POSTGRESQL:
|
|
DisplayExternalHelp(helpTopic, settings->GetPgHelpPath(), pgHelpCtl, (pgInitPath != settings->GetPgHelpPath() ? true : false));
|
|
pgInitPath = settings->GetPgHelpPath();
|
|
break;
|
|
|
|
case HELP_ENTERPRISEDB:
|
|
DisplayExternalHelp(helpTopic, settings->GetEdbHelpPath(), edbHelpCtl, (edbInitPath != settings->GetEdbHelpPath() ? true : false));
|
|
edbInitPath = settings->GetEdbHelpPath();
|
|
break;
|
|
|
|
case HELP_GREENPLUM:
|
|
{
|
|
// the old help path (stored in the settings) is no longer working
|
|
static wxString gpHelpPath = settings->GetGpHelpPath();
|
|
|
|
// Note: never end the URL on "index.html"
|
|
// InitHelp() does obscure magic with this ending
|
|
if (gpHelpPath.CmpNoCase(wxT("http://docs.gopivotal.com/gpdb/")) == 0)
|
|
{
|
|
gpHelpPath = wxT("http://gpdb.docs.pivotal.io/");
|
|
// Replace the path to the old domain with the link to
|
|
// the new documentation path
|
|
// The old link is working for now, but there is no guarantee
|
|
// that it will stay this way
|
|
// Also the new link automatically redirects to the latest version
|
|
settings->SetGpHelpPath(gpHelpPath);
|
|
}
|
|
|
|
if (gpHelpPath.CmpNoCase(wxT("http://www.greenplum.com/docs/3300/")) == 0)
|
|
{
|
|
gpHelpPath = wxT("http://gpdb.docs.pivotal.io/");
|
|
// this is the old link, update the link to the new documentation link
|
|
// problem: this saves the link into the configuration file
|
|
settings->SetGpHelpPath(gpHelpPath);
|
|
}
|
|
DisplayExternalHelp(helpTopic, settings->GetGpHelpPath(), greenplumHelpCtl, (gpInitPath != settings->GetGpHelpPath() ? true : false));
|
|
gpInitPath = settings->GetGpHelpPath();
|
|
}
|
|
break;
|
|
|
|
case HELP_SLONY:
|
|
DisplayExternalHelp(helpTopic, settings->GetSlonyHelpPath(), slonyHelpCtl, (slonyInitPath != settings->GetSlonyHelpPath() ? true : false));
|
|
slonyInitPath = settings->GetSlonyHelpPath();
|
|
break;
|
|
|
|
default:
|
|
DisplayPgAdminHelp(helpTopic);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DisplayPgAdminHelp(const wxString &helpTopic)
|
|
{
|
|
static wxHelpControllerBase *helpCtl = 0;
|
|
static bool firstCall = true;
|
|
|
|
// Startup the main help system
|
|
if (firstCall)
|
|
{
|
|
firstCall = false;
|
|
wxString helpdir = docPath + wxT("/") + settings->GetCanonicalLanguageName();
|
|
|
|
if (!wxFile::Exists(helpdir + wxT("/pgadmin3.hhp")) &&
|
|
#if defined(__WXMSW__) || wxUSE_LIBMSPACK
|
|
!wxFile::Exists(helpdir + wxT("/pgadmin3.chm")) &&
|
|
#endif
|
|
!wxFile::Exists(helpdir + wxT("/pgadmin3.zip")))
|
|
helpdir = docPath + wxT("/en_US");
|
|
|
|
#ifdef __WXMSW__
|
|
#ifndef __WXDEBUG__
|
|
if (wxFile::Exists(helpdir + wxT("/pgadmin3.chm")))
|
|
{
|
|
helpCtl = new wxCHMHelpController();
|
|
helpCtl->Initialize(helpdir + wxT("/pgadmin3"));
|
|
}
|
|
else
|
|
#endif
|
|
#endif
|
|
#if wxUSE_LIBMSPACK
|
|
if (wxFile::Exists(helpdir + wxT("/pgadmin3.chm")) ||
|
|
wxFile::Exists(helpdir + wxT("/pgadmin3.hhp")) || wxFile::Exists(helpdir + wxT("/pgadmin3.zip")))
|
|
#else
|
|
if (wxFile::Exists(helpdir + wxT("/pgadmin3.hhp")) || wxFile::Exists(helpdir + wxT("/pgadmin3.zip")))
|
|
#endif
|
|
{
|
|
helpCtl = new wxHtmlHelpController();
|
|
helpCtl->Initialize(helpdir + wxT("/pgadmin3"));
|
|
}
|
|
}
|
|
|
|
wxString page;
|
|
int hashPos = helpTopic.Find('#');
|
|
if (hashPos < 0)
|
|
page = helpTopic + wxT(".html");
|
|
else
|
|
page = helpTopic.Left(hashPos) + wxT(".html") + helpTopic.Mid(hashPos);
|
|
|
|
if (helpCtl)
|
|
{
|
|
if (helpTopic == wxT("index.html"))
|
|
helpCtl->DisplayContents();
|
|
else
|
|
helpCtl->DisplaySection(page);
|
|
}
|
|
else
|
|
{
|
|
wxLaunchDefaultBrowser(page);
|
|
}
|
|
}
|
|
|
|
void DisplayExternalHelp(const wxString &helpTopic, const wxString &docPath, wxHelpControllerBase *helpCtl, const bool init)
|
|
{
|
|
// Build the page name
|
|
wxString page;
|
|
int hashPos = helpTopic.Find('#');
|
|
if (hashPos < 0)
|
|
page = helpTopic + wxT(".html");
|
|
else
|
|
page = helpTopic.Left(hashPos) + wxT(".html") + helpTopic.Mid(hashPos);
|
|
|
|
// If the docPath ends in .pdf, then open the file in the browser. No
|
|
// bookmarks though :-(
|
|
if (docPath.Lower().EndsWith(wxT(".pdf")))
|
|
{
|
|
wxLaunchDefaultBrowser(docPath);
|
|
return;
|
|
}
|
|
|
|
// If the docPath doesn't end in .chm, .zip or .hhp, then we must be using
|
|
// plain HTML files, so just fire off the browser and be done with it.
|
|
if (!docPath.Lower().EndsWith(wxT(".hhp")) &&
|
|
#if defined (__WXMSW__) || wxUSE_LIBMSPACK
|
|
!docPath.Lower().EndsWith(wxT(".chm")) &&
|
|
#endif
|
|
!docPath.Lower().EndsWith(wxT(".zip")))
|
|
{
|
|
wxLaunchDefaultBrowser(docPath + page);
|
|
return;
|
|
}
|
|
|
|
// We must be using HTML Help, so init the appropriate help controller
|
|
// Note the path that we init for - if it changes, we need to init a
|
|
// new controller in case it's no longer the same type
|
|
if (init || !helpCtl)
|
|
{
|
|
// Get shot of the old help controller if there is one.
|
|
if (helpCtl)
|
|
delete helpCtl;
|
|
|
|
#ifdef __WXMSW__
|
|
// For Windows builds we us the MS HTML Help viewer for .chm files
|
|
if (docPath.Lower().EndsWith(wxT(".chm")) && wxFile::Exists(docPath))
|
|
{
|
|
helpCtl = new wxCHMHelpController();
|
|
helpCtl->Initialize(docPath);
|
|
}
|
|
else
|
|
#endif
|
|
#if wxUSE_LIBMSPACK
|
|
// If we can use a .chm file...
|
|
if ((docPath.Lower().EndsWith(wxT(".chm")) && wxFile::Exists(docPath)) ||
|
|
(docPath.Lower().EndsWith(wxT(".hhp")) && wxFile::Exists(docPath)) ||
|
|
(docPath.Lower().EndsWith(wxT(".zip")) && wxFile::Exists(docPath)))
|
|
#else
|
|
// Otherwise...
|
|
if ((docPath.Lower().EndsWith(wxT(".hhp")) && wxFile::Exists(docPath)) ||
|
|
(docPath.Lower().EndsWith(wxT(".zip")) && wxFile::Exists(docPath)))
|
|
#endif
|
|
{
|
|
helpCtl = new wxHtmlHelpController();
|
|
helpCtl->Initialize(docPath);
|
|
}
|
|
}
|
|
|
|
// Display the page using the help controller
|
|
// If it's foobar'ed, use the browser.
|
|
if (helpCtl)
|
|
{
|
|
if (helpTopic == wxT("index.html"))
|
|
helpCtl->DisplayContents();
|
|
else
|
|
helpCtl->DisplaySection(page);
|
|
}
|
|
else
|
|
{
|
|
wxLogError(_("The help source (\"%s\") could not be opened. Please check the help configuration options."), docPath.c_str());
|
|
}
|
|
}
|
|
|
|
wxString GetHtmlEntity(const wxChar ch)
|
|
{
|
|
// REWRITE THIS - IT'S (STILL) BLOODY INEFFICIENT!!
|
|
|
|
// Quick bailout
|
|
if ((ch >= 'a' && ch <= 'z') ||
|
|
(ch >= 'A' && ch <= 'Z') ||
|
|
(ch >= '0' && ch <= '9'))
|
|
return wxString(ch);
|
|
|
|
unsigned short ents[] =
|
|
{
|
|
34,
|
|
39,
|
|
38,
|
|
60,
|
|
62,
|
|
160,
|
|
161,
|
|
164,
|
|
162,
|
|
163,
|
|
165,
|
|
166,
|
|
167,
|
|
168,
|
|
169,
|
|
170,
|
|
171,
|
|
172,
|
|
173,
|
|
174,
|
|
8482,
|
|
175,
|
|
176,
|
|
177,
|
|
178,
|
|
179,
|
|
180,
|
|
181,
|
|
182,
|
|
183,
|
|
184,
|
|
185,
|
|
186,
|
|
187,
|
|
188,
|
|
189,
|
|
190,
|
|
191,
|
|
215,
|
|
247,
|
|
192,
|
|
193,
|
|
194,
|
|
195,
|
|
196,
|
|
197,
|
|
198,
|
|
199,
|
|
200,
|
|
201,
|
|
202,
|
|
203,
|
|
204,
|
|
205,
|
|
206,
|
|
207,
|
|
208,
|
|
209,
|
|
210,
|
|
211,
|
|
212,
|
|
213,
|
|
214,
|
|
216,
|
|
217,
|
|
218,
|
|
219,
|
|
220,
|
|
221,
|
|
222,
|
|
223,
|
|
224,
|
|
225,
|
|
226,
|
|
227,
|
|
228,
|
|
229,
|
|
230,
|
|
231,
|
|
232,
|
|
233,
|
|
234,
|
|
235,
|
|
236,
|
|
237,
|
|
238,
|
|
239,
|
|
240,
|
|
241,
|
|
242,
|
|
243,
|
|
244,
|
|
245,
|
|
246,
|
|
248,
|
|
249,
|
|
250,
|
|
251,
|
|
252,
|
|
253,
|
|
254,
|
|
255,
|
|
338,
|
|
339,
|
|
352,
|
|
353,
|
|
376,
|
|
710,
|
|
732,
|
|
8194,
|
|
8195,
|
|
8201,
|
|
8204,
|
|
8205,
|
|
8206,
|
|
8207,
|
|
8211,
|
|
8212,
|
|
8216,
|
|
8217,
|
|
8218,
|
|
8220,
|
|
8221,
|
|
8222,
|
|
8224,
|
|
8225,
|
|
8230,
|
|
8240,
|
|
8249,
|
|
8250,
|
|
8364,
|
|
0
|
|
};
|
|
|
|
int elem = 0;
|
|
|
|
while (ents[elem] != 0)
|
|
{
|
|
if (ch == ents[elem])
|
|
{
|
|
wxString ret = wxT("&#");
|
|
ret += NumToStr((long)ents[elem]);
|
|
ret += wxT(";");
|
|
return ret;
|
|
}
|
|
elem++;
|
|
}
|
|
|
|
return wxString(ch);
|
|
}
|
|
|
|
wxString HtmlEntities(const wxString &str)
|
|
{
|
|
wxString ret;
|
|
|
|
for (unsigned int x = 0; x < str.Length(); x++)
|
|
{
|
|
if (str[x] != 13)
|
|
ret += GetHtmlEntity(str[x]);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifndef WIN32
|
|
wxString ExecProcess(const wxString &cmd)
|
|
{
|
|
wxString res;
|
|
FILE *f = popen(cmd.ToAscii(), "r");
|
|
|
|
if (f)
|
|
{
|
|
char buffer[1024];
|
|
int cnt;
|
|
while ((cnt = fread(buffer, 1, 1024, f)) > 0)
|
|
{
|
|
res += wxString::FromAscii(buffer);
|
|
}
|
|
pclose(f);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int ExecProcess(const wxString &command, wxArrayString &result)
|
|
{
|
|
FILE *fp_command;
|
|
char buf[4098];
|
|
|
|
fp_command = popen(command.mb_str(wxConvUTF8), "r");
|
|
|
|
if (!fp_command)
|
|
return -1;
|
|
|
|
while(!feof(fp_command))
|
|
{
|
|
if (fgets(buf, 4096, fp_command) != NULL)
|
|
result.Add(wxString::FromAscii(buf));
|
|
}
|
|
|
|
return pclose(fp_command);
|
|
}
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#if (_MSC_VER < 1300)
|
|
/* _ftol2 is more than VC7. */
|
|
/*
|
|
extern "C" long _ftol( double );
|
|
extern "C" long _ftol2( double dblSource )
|
|
{
|
|
return _ftol( dblSource );
|
|
}
|
|
*/
|
|
#endif
|
|
#endif
|
|
|
|
wxString firstLineOnly(const wxString &str)
|
|
{
|
|
wxString ip, tmp;
|
|
ip = str;
|
|
ip = ip.Trim(true).Trim(false);
|
|
|
|
if (ip.Contains(wxT("\r\n")) && (ip.First(wxT("\r")) < ip.First(wxT("\n"))))
|
|
{
|
|
tmp = ip.BeforeFirst('\r');
|
|
if (ip.BeforeFirst('\r').Length() != ip.Length())
|
|
tmp += wxT("...");
|
|
}
|
|
else
|
|
{
|
|
tmp = ip.BeforeFirst('\n');
|
|
if (ip.BeforeFirst('\n').Length() != ip.Length())
|
|
tmp += wxT("...");
|
|
}
|
|
|
|
return tmp;
|
|
}
|
|
|
|
bool pgAppMinimumVersion(const wxString &cmd, const int majorVer, const int minorVer)
|
|
{
|
|
wxArrayString output;
|
|
bool isEnterpriseDB = false;
|
|
|
|
#ifdef __WXMSW__
|
|
if (wxExecute(cmd + wxT(" --version"), output, 0) != 0)
|
|
#else
|
|
if (ExecProcess(cmd + wxT(" --version"), output) != 0)
|
|
#endif
|
|
{
|
|
wxLogError(_("Failed to execute: %s --version"), cmd.c_str());
|
|
return false;
|
|
}
|
|
|
|
// Is this an EDB utility?
|
|
if (output[0].Contains(wxT("EnterpriseDB")))
|
|
isEnterpriseDB = true;
|
|
|
|
wxString version = output[0].AfterLast(' ');
|
|
int len=version.Length();
|
|
long actualMajor = 0, actualMinor = 0;
|
|
|
|
wxString tmp = wxT("");
|
|
int x = 0;
|
|
while(x<len&&(version[x] == '0' || version[x] == '1' || version[x] == '2' || version[x] == '3' || version[x] == '4' ||
|
|
version[x] == '5' || version[x] == '6' || version[x] == '7' || version[x] == '8' || version[x] == '9'))
|
|
{
|
|
tmp += version[x];
|
|
x++;
|
|
}
|
|
tmp.ToLong(&actualMajor);
|
|
x++;
|
|
|
|
tmp = wxT("");
|
|
while(x<len&&(version[x] == '0' || version[x] == '1' || version[x] == '2' || version[x] == '3' || version[x] == '4' ||
|
|
version[x] == '5' || version[x] == '6' || version[x] == '7' || version[x] == '8' || version[x] == '9'))
|
|
{
|
|
tmp += version[x];
|
|
x++;
|
|
}
|
|
|
|
tmp.ToLong(&actualMinor);
|
|
|
|
// EnterpriseDB's 8.3R1 utilties are based on PG8.2, so correct the version number here.
|
|
// This will need more work when 8.3R2 is released :-(
|
|
if (isEnterpriseDB && actualMajor == 8 && actualMinor == 3)
|
|
actualMinor = 2;
|
|
|
|
if (actualMajor > majorVer)
|
|
return true;
|
|
|
|
if (actualMajor == majorVer && actualMinor >= minorVer)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isPgApp(const wxString &app)
|
|
{
|
|
if (!wxFile::Exists(app))
|
|
return false;
|
|
|
|
wxArrayString output;
|
|
|
|
#ifdef __WXMSW__
|
|
if (wxExecute(app + wxT(" --version"), output, 0) != 0)
|
|
#else
|
|
if (ExecProcess(app + wxT(" --version"), output) != 0)
|
|
#endif
|
|
{
|
|
wxLogError(_("Failed to execute: %s --version"), app.c_str());
|
|
return false;
|
|
}
|
|
|
|
if (output[0].Contains(wxT("PostgreSQL")))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isEdbApp(const wxString &app)
|
|
{
|
|
if (!wxFile::Exists(app))
|
|
return false;
|
|
|
|
wxArrayString output;
|
|
|
|
#ifdef __WXMSW__
|
|
if (wxExecute(app + wxT(" --version"), output, 0) != 0)
|
|
#else
|
|
if (ExecProcess(app + wxT(" --version"), output) != 0)
|
|
#endif
|
|
{
|
|
wxLogError(_("Failed to execute: %s --version"), app.c_str());
|
|
return false;
|
|
}
|
|
|
|
if (output[0].Contains(wxT("EnterpriseDB")))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isGpApp(const wxString &app)
|
|
{
|
|
if (!wxFile::Exists(app))
|
|
return false;
|
|
|
|
wxArrayString output;
|
|
|
|
#ifdef __WXMSW__
|
|
if (wxExecute(app + wxT(" --version"), output, 0) != 0)
|
|
#else
|
|
if (ExecProcess(app + wxT(" --version"), output) != 0)
|
|
#endif
|
|
{
|
|
wxLogError(_("Failed to execute: %s --version"), app.c_str());
|
|
return false;
|
|
}
|
|
|
|
if (output[0].Contains(wxT("8.2"))) // Ugly... No way to tell Greenplum app from PostgreSQL 8.2 app
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
wxString sanitizePath(const wxString &path)
|
|
{
|
|
if (path.Length())
|
|
{
|
|
wxFileName fn = path;
|
|
fn.Normalize();
|
|
return fn.GetLongPath();
|
|
}
|
|
|
|
return wxEmptyString;
|
|
}
|
|
|
|
/**
|
|
* FUNCTION: commandLineCleanOption
|
|
* INPUTS:
|
|
* option - input string needs to be reformatted
|
|
* schemaObject - Is this an object related to schema?
|
|
* PURPOSE:
|
|
* - Fixup a (double-quoted) string for use on the command line
|
|
*/
|
|
wxString commandLineCleanOption(const wxString &option, bool schemaObject)
|
|
{
|
|
wxString tmp = option;
|
|
|
|
if (schemaObject)
|
|
{
|
|
// Replace double-quote with slash & double-quote
|
|
tmp.Replace(wxT("\""), wxT("\\\""));
|
|
}
|
|
else
|
|
{
|
|
// If required, clean the string to know the real object name
|
|
if (option.StartsWith(wxT("\"")) && option.EndsWith(wxT("\"")))
|
|
tmp = option.AfterFirst((wxChar)'"').BeforeLast((wxChar)'"');
|
|
|
|
// Replace single splash to double-splash
|
|
tmp.Replace(wxT("\\"), wxT("\\\\"));
|
|
|
|
// Replace double-quote with slash & double-quote
|
|
tmp.Replace(wxT("\""), wxT("\\\""));
|
|
|
|
// Replace double (slash & double-quote) combination to single (slash & double-quote) combination
|
|
tmp.Replace(wxT("\\\"\\\""), wxT("\\\""));
|
|
|
|
// Add the double quotes
|
|
tmp = wxT("\"") + tmp + wxT("\"");
|
|
}
|
|
|
|
return tmp;
|
|
}
|
|
|
|
#endif // PGSCLI
|
|
|
|
// Get an array from a comma separated list
|
|
bool getArrayFromCommaSeparatedList(const wxString &str, wxArrayString &res)
|
|
{
|
|
size_t len = str.Len(), index = 0, nBracketLevel = 0, startArray = 0;
|
|
bool inSingleQuote = false, inDoubleQuote = false;
|
|
|
|
if (len == 0)
|
|
return true;
|
|
|
|
for(; index < len; index++)
|
|
{
|
|
wxChar curr = str.GetChar(index);
|
|
if (!inDoubleQuote && curr == (wxChar)'\'')
|
|
inSingleQuote = !inSingleQuote;
|
|
else if (!inSingleQuote && curr == (wxChar)'"')
|
|
inDoubleQuote = !inDoubleQuote;
|
|
else if (!inDoubleQuote && !inSingleQuote && curr == (wxChar)'(')
|
|
nBracketLevel++;
|
|
else if (!inDoubleQuote && !inSingleQuote && curr == (wxChar)')')
|
|
nBracketLevel--;
|
|
else if (!inDoubleQuote && !inSingleQuote && nBracketLevel == 0 && curr == (wxChar)',')
|
|
{
|
|
if (index != startArray)
|
|
res.Add(str.SubString(startArray, index - 1).Trim(true).Trim(false));
|
|
else
|
|
res.Add(wxEmptyString);
|
|
startArray = index + 1;
|
|
}
|
|
}
|
|
if (inDoubleQuote || inSingleQuote || nBracketLevel != 0)
|
|
return false;
|
|
|
|
// Add last value to array
|
|
res.Add(str.SubString(startArray, index).Trim(true).Trim(false));
|
|
|
|
return true;
|
|
}
|
|
bool make_identifier(const wxString &strname, wxString &s, wxString &n, bool islower) {
|
|
s=strname.BeforeFirst('.');
|
|
if (s==strname) {
|
|
n=strname;
|
|
s="";
|
|
} else {
|
|
// full name
|
|
n=strname.AfterFirst('.');
|
|
}
|
|
//
|
|
if (n.Find('"')>=0) {
|
|
n.Replace("\"","");
|
|
} else {if (islower) n=n.MakeLower();}
|
|
if (s.Find('"')>=0) {
|
|
s.Replace("\"","");
|
|
} else {if (islower) s=s.MakeLower();}
|
|
return true;
|
|
}
|
|
|
|
// Get display for ScreenPoint
|
|
wxSize getScreenSizeForPoint(const wxPoint screenPos) {
|
|
// screen size
|
|
wxPoint posScreen;
|
|
wxSize sizeScreen;
|
|
const int displayNum = wxDisplay::GetFromPoint(screenPos);
|
|
if (displayNum != wxNOT_FOUND)
|
|
{
|
|
const wxRect rectScreen = wxDisplay(displayNum).GetGeometry();
|
|
posScreen = rectScreen.GetPosition();
|
|
sizeScreen = rectScreen.GetSize();
|
|
}
|
|
else // outside of any display?
|
|
{
|
|
// just use the primary one then
|
|
posScreen = wxPoint(0, 0);
|
|
sizeScreen = wxGetDisplaySize();
|
|
}
|
|
return sizeScreen;
|
|
}
|
|
//show help window
|
|
void showHelpHtml(wxWindow *parent, const wxString &htmlHelp,wxPoint screenPos, wxSize size) {
|
|
FunctionPGHelper fh(htmlHelp);
|
|
wxString key = "content";
|
|
|
|
wxSize sizeScreen=getScreenSizeForPoint(screenPos);
|
|
wxSize rr(350, 70);
|
|
if (size.x!=-1) rr=size;
|
|
popuphelp *m_Popup = new popuphelp(parent, key, &fh, screenPos, rr);
|
|
if (m_Popup && m_Popup->IsValid() && rr != m_Popup->GetSizePopup()) {
|
|
// recreate with new size
|
|
rr = m_Popup->GetSizePopup();
|
|
delete m_Popup;
|
|
m_Popup = new popuphelp((wxWindow*)winMain, key, &fh, screenPos, rr);
|
|
}
|
|
if (m_Popup && m_Popup->IsValid()) {
|
|
wxSize top_sz = m_Popup->GetSizePopup();
|
|
wxSize top_new(top_sz);
|
|
wxPoint p(screenPos);
|
|
wxPoint oldp(p);
|
|
if (p.x + top_new.x > sizeScreen.x) p.x = sizeScreen.x - top_new.x - 20;
|
|
if (p.y + top_new.y > sizeScreen.y) p.y = sizeScreen.y - top_new.y - 20;
|
|
if (oldp == p) p.x = p.x + 20;
|
|
m_Popup->Move(p);
|
|
wxRect r = m_Popup->GetScreenRect();
|
|
m_Popup->Popup();
|
|
}
|
|
//m_Popup->SetFocus();
|
|
|
|
}
|
|
|
|
|
|
|