meos-2024/code/gdioutput.cpp
2023-05-14 22:03:57 +02:00

7582 lines
202 KiB
C++

/************************************************************************
MeOS - Orienteering Software
Copyright (C) 2009-2023 Melin Software HB
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Melin Software HB - software@melin.nu - www.melin.nu
Eksoppsvägen 16, SE-75646 UPPSALA, Sweden
************************************************************************/
// gdioutput.cpp: implementation of the gdioutput class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "gdioutput.h"
#include "gdiconstants.h"
#include "meosException.h"
#include "process.h"
#include "meos.h"
#include <commctrl.h>
#include <commdlg.h>
#include <shellapi.h>
#include <objbase.h>
#include <shlobj.h>
#include <cassert>
#include <cmath>
#include <sstream>
#include "meos_util.h"
#include "Table.h"
#define _USE_MATH_DEFINES
#include "math.h"
#include "Localizer.h"
#include "TabBase.h"
#include "toolbar.h"
#include "gdiimpl.h"
#include "Printer.h"
#include "recorder.h"
#include "animationdata.h"
#include "image.h"
#include "autocomplete.h"
extern Image image;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
//Fulhack...
#ifndef IDC_HAND
#define IDC_HAND MAKEINTRESOURCE(32649)
#endif
//#define DEBUGRENDER
#ifdef DEBUGRENDER
static int counterRender = 0;
static bool breakRender = false;
static int debugDrawColor = 0;
#endif
extern int defaultCodePage;
GuiHandler &BaseInfo::getHandler() const {
if (managedHandler)
return *managedHandler;
if (handler == 0)
throw meosException("Handler not definied.");
return *handler;
}
void GuiHandler::handle(gdioutput &gdi, BaseInfo &info, GuiEventType type) {
throw meosException("Handler not definied.");
}
InputInfo::InputInfo() : hWnd(0), callBack(0), ignoreCheck(false),
isEditControl(true), bgColor(colorDefault), fgColor(colorDefault),
writeLock(false), updateLastData(0) {}
EventInfo::EventInfo() : callBack(0), keyEvent(KC_NONE) {}
/** Return true if rendering text should be skipped for
this format. */
bool gdioutput::skipTextRender(int format) {
format &= 0xFF | hiddenText;
return format == pageNewPage ||
format == pagePageInfo ||
format == pageNewChapter ||
(format & hiddenText) == hiddenText;
}
#ifndef MEOSDB
gdioutput::gdioutput(const string &_tag, double _scale) :
recorder((Recorder *)0, false) {
tag = _tag;
po_default = new PrinterObject();
tabs = 0;
hasAnyTimer = false;
constructor(_scale);
isTestMode = false;
}
extern gdioutput *gdi_main;
gdioutput::gdioutput(double _scale, HWND hWnd, const PrinterObject &prndef) :
recorder((Recorder *)0, false) {
hasAnyTimer = false;
po_default = new PrinterObject(prndef);
tabs = 0;
setWindow(hWnd);
constructor(_scale);
if (gdi_main) {
isTestMode = gdi_main->isTestMode;
if (isTestMode)
cmdAnswers.swap(gdi_main->cmdAnswers);
}
else isTestMode = false;
}
void gdioutput::constructor(double _scale)
{
currentFontSet = 0;
commandLock = false;
commandUnlockTime = 0;
lockUpDown = false;
Background = 0;
backgroundColor1 = -1;
backgroundColor2 = -1;
foregroundColor = -1;
backgroundImage = -1;
toolbar = 0;
initCommon(_scale, L"Arial");
OffsetY=0;
OffsetX=0;
manualUpdate = false;
itTL = TL.end();
hWndTarget = 0;
hWndToolTip = 0;
hWndAppMain = 0;
onClear = 0;
postClear = 0;
clearPage(true);
hasCleared = false;
highContrast = false;
hideBG = false;
fullScreen = false;
lockRefresh = 0;
autoSpeed = 0;
autoPos = 0;
lastSpeed = 0;
autoCounter = 0;
}
#endif
void gdioutput::setFont(int size, const wstring &font)
{
double ss = size * sqrt(size);
double s = 1 + double(ss)*0.25;
initCommon(s, font);
}
void gdioutput::setFontCtrl(HWND hWnd) {
SendMessage(hWnd, WM_SETFONT, (WPARAM) getGUIFont(), MAKELPARAM(TRUE, 0));
}
static void scaleWindow(HWND hWnd, double scale, int &w, int &h) {
RECT rc;
GetWindowRect(hWnd, &rc);
w = rc.right - rc.left;
h = rc.bottom - rc.top;
w = int(w * scale + 0.5);
h = int(h * scale + 0.5);
}
int transformX(int x, double scale) {
if (x<40)
return int(x * scale + 0.5);
else
return int((x-40) * scale + 0.5) + 40;
}
void gdioutput::scaleSize(double scale_, bool allowSmallScale, ScaleOperation op) {
if (fabs(scale_ - 1.0) < 1e-4)
return; // No scaling
double ns = scale*scale_;
if (!allowSmallScale && ns + 1e-6 < 1.0 ) {
ns = 1.0;
scale_ = 1.0;
}
initCommon(ns, currentFont);
if (op == ScaleOperation::NoUpdate)
return;
for (list<TextInfo>::iterator it = TL.begin(); it!=TL.end(); ++it) {
it->xlimit = int(it->xlimit * scale_ + 0.5);
it->xp = transformX(it->xp, scale_);
it->yp = int(it->yp * scale_ + 0.5);
}
int w, h;
OffsetY = int (OffsetY * scale_ + 0.5);
OffsetX = int (OffsetX * scale_ + 0.5);
for (list<ButtonInfo>::iterator it = BI.begin(); it!=BI.end(); ++it) {
if (it->fixedRightTop)
it->xp = int(scale_ * it->xp + 0.5);
else
it->xp = transformX(it->xp, scale_);
it->yp = int(it->yp * scale_ + 0.5);
if (it->isCheckbox)
scaleWindow(it->hWnd, 1.0, w, h);
else
scaleWindow(it->hWnd, scale_, w, h);
setFontCtrl(it->hWnd);
MoveWindow(it->hWnd, it->xp-OffsetX, it->yp-OffsetY, w, h, true);
}
for (list<InputInfo>::iterator it = II.begin(); it!=II.end(); ++it) {
it->xp = transformX(it->xp, scale_);
it->yp = int(it->yp * scale_ + 0.5);
it->height *= scale_;
it->width *= scale_;
setFontCtrl(it->hWnd);
MoveWindow(it->hWnd, it->xp-OffsetX, it->yp-OffsetY, int(it->width+0.5), int(it->height+0.5), true);
}
for (list<ListBoxInfo>::iterator it = LBI.begin(); it!=LBI.end(); ++it) {
it->xp = transformX(it->xp, scale_);
it->yp = int(it->yp * scale_ + 0.5);
it->height *= scale_;
it->width *= scale_;
setFontCtrl(it->hWnd);
MoveWindow(it->hWnd, it->xp-OffsetX, it->yp-OffsetY, int(it->width+0.5), int(it->height+0.5), true);
}
for (list<RectangleInfo>::iterator it = Rectangles.begin(); it!=Rectangles.end(); ++it) {
it->rc.bottom = int(it->rc.bottom * scale_ + 0.5);
it->rc.top = int(it->rc.top * scale_ + 0.5);
it->rc.right = transformX(it->rc.right, scale_);
it->rc.left = transformX(it->rc.left, scale_);
}
for (list<TableInfo>::iterator it = Tables.begin(); it != Tables.end(); ++it) {
it->xp = transformX(it->xp, scale_);
it->yp = int(it->yp * scale_ + 0.5);
}
MaxX = transformX(MaxX, scale_);
MaxY = int (MaxY * scale_ + 0.5);
CurrentX = transformX(CurrentX, scale_);
CurrentY = int (CurrentY * scale_ + 0.5);
SX = transformX(SX, scale_);
SY = int (SY * scale_ + 0.5);
for (map<string, RestoreInfo>::iterator it = restorePoints.begin(); it != restorePoints.end(); ++it) {
RestoreInfo &r = it->second;
r.sMX = transformX(r.sMX, scale_);
r.sMY = int (r.sMY * scale_ + 0.5);
r.sCX = transformX(r.sCX, scale_);
r.sCY = int (r.sCY * scale_ + 0.5);
r.sOX = transformX(r.sOX, scale_);
r.sOY = int (r.sOY * scale_ + 0.5);
}
if (op == ScaleOperation::Refresh) {
refresh();
}
else {
HDC hDC = GetDC(hWndTarget);
for (auto &ti : TL) {
calcStringSize(ti, hDC);
}
ReleaseDC(hWndTarget, hDC);
}
}
void gdioutput::initCommon(double _scale, const wstring &font)
{
guiMeasure.reset();
dbErrorState = false;
currentFontSet = 0;
scale = _scale;
currentFont = font;
deleteFonts();
enableTables();
lineHeight = int(scale*14);
Background=CreateSolidBrush(GetSysColor(COLOR_WINDOW));
fontHeightCache.clear();
fonts[currentFont].init(scale, currentFont, L"");
}
double getLocalScale(const wstring &fontName, wstring &faceName) {
double locScale = 1.0;
vector<wstring> res;
split(fontName, L";", res);
if (res.empty() || res.size() > 2)
throw meosException(L"Cannot load font: " + fontName);
if (res.size() == 2) {
locScale = _wtof(res[1].c_str());
if (!(locScale>0.001 && locScale < 100))
throw meosException(L"Cannot scale font with factor: " + res[1]);
}
faceName = res[0];
return locScale;
}
const GDIImplFontSet & gdioutput::loadFont(const wstring &font) {
currentFontSet = 0;
vector< pair<wstring, size_t> > fontIx;
getEnumeratedFonts(fontIx);
double relScale = 1.0;
for (size_t k = 0; k < fontIx.size(); k++) {
if (stringMatch(fontIx[k].first, font)) {
relScale = enumeratedFonts[fontIx[k].second].getRelScale();
}
}
wstring faceName;
double locScale = getLocalScale(font, faceName);
if (faceName.empty())
faceName = currentFont;
fonts[font].init(scale * relScale * locScale, faceName, font);
return fonts[font];
}
void gdioutput::deleteFonts() {
if (Background)
DeleteObject(Background);
Background = 0;
currentFontSet = 0;
fonts.clear();
}
#ifndef MEOSDB
gdioutput::~gdioutput()
{
while(!timers.empty()) {
KillTimer(hWndTarget, (UINT_PTR)&timers.back());
timers.back().setWnd = 0;
timers.back().parent = 0;
timers.pop_back();
}
animationData.reset();
deleteFonts();
if (toolbar)
delete toolbar;
toolbar = 0;
Tables.clear();
if (tabs) {
delete tabs;
tabs = 0;
}
initRecorder(0);
delete po_default;
po_default = 0;
}
#endif
FixedTabs &gdioutput::getTabs() {
#ifndef MEOSDB
if (!tabs)
tabs = new FixedTabs();
#endif
return *tabs;
}
void gdioutput::fetchPrinterSettings(PrinterObject &po) const {
po = *po_default;
}
void gdioutput::drawBackground(HDC hDC, RECT &rc)
{
if (backgroundColor1 != -1) {
SelectObject(hDC, GetStockObject(NULL_PEN));
SelectObject(hDC, GetStockObject(DC_BRUSH));
SetDCBrushColor(hDC, backgroundColor1);
Rectangle(hDC, -1, -1, rc.right + 1, rc.bottom + 1);
return;
}
else if (!backgroundImage.empty()) {
// TODO
}
GRADIENT_RECT gr[1];
SelectObject(hDC, GetStockObject(NULL_PEN));
SelectObject(hDC, Background);
if (highContrast) {
Rectangle(hDC, -1, -1, rc.right + 1, rc.bottom + 1);
HFONT hInfo = CreateFont(min(30, int(scale*22)), 0, 900, 900, FW_LIGHT, false, false, false, DEFAULT_CHARSET,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY,
DEFAULT_PITCH|FF_ROMAN, L"Arial");
SelectObject(hDC, hInfo);
RECT mrc;
mrc.left = 0;
mrc.right = 0;
mrc.top = 0;
mrc.bottom = 0;
DrawText(hDC, listDescription.c_str(), listDescription.length(), &mrc, DT_LEFT|DT_CALCRECT|DT_NOPREFIX);
int height = mrc.right + mrc.right / 3;
SetBkMode(hDC, TRANSPARENT);
for (int k = height; k < MaxY; k += height) {
mrc.left = 5 - OffsetX;
mrc.right = 1000;
mrc.top = k - OffsetY;
mrc.bottom = MaxY;
SetTextColor(hDC, RGB(192, 192, 192));
DrawText(hDC, listDescription.c_str(), listDescription.length(), &mrc, DT_LEFT|DT_NOCLIP|DT_NOPREFIX);
mrc.top -= 1;
mrc.left -= 1;
SetTextColor(hDC, RGB(92, 32, 32));
DrawText(hDC, listDescription.c_str(), listDescription.length(), &mrc, DT_LEFT|DT_NOCLIP|DT_NOPREFIX);
}
SelectObject(hDC, GetStockObject(ANSI_FIXED_FONT));
DeleteObject(hInfo);
return;
}
if (!hideBG) {
Rectangle(hDC, -1, -1, rc.right-OffsetX+1, 10-OffsetY+1);
Rectangle(hDC, -1, -1, 11-OffsetX, rc.bottom+1);
Rectangle(hDC, MaxX+10-OffsetX, 0, rc.right+1, rc.bottom+1);
Rectangle(hDC, 10-OffsetX, MaxY+13-OffsetY, MaxX+11-OffsetX, rc.bottom+1);
}
if (dbErrorState) {
SelectObject(hDC, GetStockObject(DC_BRUSH));
SetDCBrushColor(hDC, RGB(255, 100, 100));
Rectangle(hDC, -1, -1, rc.right+1, rc.bottom+1);
HFONT hInfo = CreateFont(30, 0, 900, 900, FW_BOLD, false, false, false, DEFAULT_CHARSET,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY,
DEFAULT_PITCH|FF_ROMAN, L"Arial");
wstring err = lang.tl(L"DATABASE ERROR");
SelectObject(hDC, hInfo);
RECT mrc;
mrc.left = 0;
mrc.right = 0;
mrc.top = 0;
mrc.bottom = 0;
DrawText(hDC, err.c_str(), err.length(), &mrc, DT_LEFT|DT_CALCRECT|DT_NOPREFIX);
int width = mrc.bottom + mrc.bottom / 4;
int height = mrc.right + mrc.right / 4;
SetBkMode(hDC, TRANSPARENT);
SetTextColor(hDC, RGB(64, 0, 0));
for (int k = height; k < max<int>(MaxY, rc.bottom + height); k += height) {
mrc.left = rc.right - 50 - OffsetX;
mrc.right = mrc.left + 1000;
mrc.top = k - OffsetY;
mrc.bottom = MaxY;
DrawText(hDC, err.c_str(), err.length(), &mrc, DT_LEFT|DT_NOCLIP|DT_NOPREFIX);
mrc.left -= width;
mrc.top -= height / 2;
DrawText(hDC, err.c_str(), err.length(), &mrc, DT_LEFT|DT_NOCLIP|DT_NOPREFIX);
}
SelectObject(hDC, GetStockObject(ANSI_FIXED_FONT));
DeleteObject(hInfo);
}
/*
DWORD c=GetSysColor(COLOR_3DFACE);
double red = double(GetRValue(c)) *0.9;
double green = double(GetGValue(c)) * 0.85;
double blue = min(255.0, double(GetBValue(c)) * 1.05);
if (blue<100) {
//Invert
red = 255-red;
green = 255-green;
blue = 255-blue;
}
double blue1=min(255., blue*1.3);
double green1=min(255., green*1.3);
double red1=min(255., red*1.3);
*/
double red = 242.0;
double green = 247.0;
double blue = 254.0;
double blue1 = 250.0;
double green1 = 232.0;
double red1 = 223.0;
TRIVERTEX vert[2];
if (hideBG) {
vert [0] .x = 0;
vert [0] .y = 0;
}
else {
vert [0] .x = 10-OffsetX;
vert [0] .y = 10-OffsetY;
}
vert [0] .Red = 0xff00&DWORD(red1*256);
vert [0] .Green = 0xff00&DWORD(green1*256);
vert [0] .Blue = 0xff00&DWORD(blue1*256);
vert [0] .Alpha = 0x0000;
if (hideBG) {
vert [1] .x = rc.right + 1;
vert [1] .y = rc.bottom + 1;
}
else {
vert [1] .x = MaxX+10-OffsetX;
vert [1] .y = MaxY+13-OffsetY;
}
vert [1] .Red = 0xff00&DWORD(red*256);
vert [1] .Green = 0xff00&DWORD(green*256);
vert [1] .Blue = 0xff00&DWORD(blue*256);
vert [1] .Alpha = 0x0000;
gr[0].UpperLeft=0;
gr[0].LowerRight=1;
if (MaxY>max(800, MaxX) || hideBG)
GradientFill(hDC,vert, 2, gr, 1,GRADIENT_FILL_RECT_H);
else
GradientFill(hDC,vert, 2, gr, 1,GRADIENT_FILL_RECT_V);
if (!hideBG) {
SelectObject(hDC, GetSysColorBrush(COLOR_3DSHADOW));
Rectangle(hDC, vert[0].x+3, vert[1].y, vert[1].x+1, vert[1].y+3);
Rectangle(hDC, vert[1].x, vert[0].y+3, vert[1].x+3, vert[1].y+3);
SelectObject(hDC, GetStockObject(NULL_BRUSH));
SelectObject(hDC, GetStockObject(DC_PEN));
SetDCPenColor(hDC, RGB(DWORD(red*0.4), DWORD(green*0.4), DWORD(blue*0.4)));
Rectangle(hDC, vert[0].x, vert[0].y, vert[1].x, vert[1].y);
}
}
void gdioutput::setDBErrorState(bool state) {
if (dbErrorState != state) {
dbErrorState = state;
refresh();
}
}
void gdioutput::draw(HDC hDC, RECT& rc, RECT& drawArea) {
#ifdef DEBUGRENDER
if (debugDrawColor) {
string ds = "DebugDraw" + itos(drawArea.left) + "-" + itos(drawArea.right) + ", " + itos(drawArea.top) + "-" + itos(drawArea.bottom) + "\n";
OutputDebugString(ds.c_str());
SelectObject(hDC, GetStockObject(DC_BRUSH));
SetDCBrushColor(hDC, debugDrawColor);
Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
return;
}
#endif
if (highContrast)
drawBackground(hDC, drawArea);
else
drawBackground(hDC, rc);
if (drawArea.left > MaxX - OffsetX + 15) {
drawBoxes(hDC, rc);
return;
}
if (animationData) {
int page = 0;
animationData->renderPage(hDC, *this, GetTickCount());
return;
}
SelectObject(hDC, GetStockObject(DC_BRUSH));
for (auto& rit : Rectangles)
renderRectangle(hDC, 0, rit);
if (useTables)
for (list<TableInfo>::iterator tit = Tables.begin(); tit != Tables.end(); ++tit) {
tit->table->draw(*this, hDC, tit->xp, tit->yp, rc);
}
resetLast();
TIList::iterator it;
int BoundYup = OffsetY - maxTextBlockHeight - 2 + drawArea.top;
int BoundYupTight = OffsetY - 2 + drawArea.top;
int BoundYdown = OffsetY + drawArea.bottom + 2;
for (auto imgTL : imageReferences) {
RenderString(*imgTL, hDC);
}
if (!renderOptimize || itTL == TL.end()) {
#ifdef DEBUGRENDER
//if (breakRender)
// DebugBreak();
OutputDebugString(("Raw render" + itos(size_t(this)) + "\n").c_str());
#endif
for (it = TL.begin(); it != TL.end(); ++it) {
TextInfo& ti = *it;
if ((ti.format & 0xFF) == textImage)
continue;
if ((ti.yp > BoundYup || ti.textRect.bottom > BoundYupTight) && ti.yp < BoundYdown)
RenderString(*it, hDC);
}
}
else {
#ifdef DEBUGRENDER
OutputDebugString((itos(++counterRender) + " opt render " + itos(size_t(this)) + "\n").c_str());
#endif
while (itTL != TL.end() && itTL->yp < BoundYup)
++itTL;
if (itTL != TL.end())
while (itTL != TL.begin() && itTL->yp > BoundYup)
--itTL;
it = itTL;
while (it != TL.end() && it->yp < BoundYdown) {
if ((it->format & 0xFF) != textImage)
RenderString(*it, hDC);
++it;
}
}
updateStringPosCache();
drawBoxes(hDC, rc);
}
void gdioutput::renderRectangle(HDC hDC, RECT *clipRegion, const RectangleInfo &ri) {
if (ri.drawBorder) {
SelectObject(hDC, GetStockObject(DC_PEN));
SetDCPenColor(hDC, RGB(40,40,60));
}
else
SelectObject(hDC, GetStockObject(NULL_PEN));
if (ri.color == colorTransparent)
SelectObject(hDC, GetStockObject(NULL_BRUSH));
else {
SetDCBrushColor(hDC, ri.color);
}
RECT rect_rc=ri.rc;
OffsetRect(&rect_rc, -OffsetX, -OffsetY);
Rectangle(hDC, rect_rc.left, rect_rc.top, rect_rc.right, rect_rc.bottom);
if (ri.color == colorTransparent)
SelectObject(hDC, GetStockObject(DC_BRUSH));
}
void gdioutput::updateStringPosCache() {
RECT rc;
GetClientRect(hWndTarget, &rc);
int BoundYup = OffsetY-100;
int BoundYdown = OffsetY+rc.bottom+10;
shownStrings.clear();
TIList::iterator it;
if (!renderOptimize || itTL == TL.end()) {
for (it=TL.begin();it!=TL.end(); ++it) {
TextInfo &ti=*it;
if ( ti.yp > BoundYup && ti.yp < BoundYdown) {
if (ti.textRect.top != ti.yp - OffsetY) {
int diff = it->textRect.top - (ti.yp - OffsetY);
ti.textRect.top -= diff;
ti.textRect.bottom -= diff;
}
shownStrings.push_back(&ti);
}
}
}
else {
TIList::iterator itC = itTL;
while( itC != TL.end() && itC->yp < BoundYup)
++itC;
if (itC!=TL.end())
while( itC != TL.begin() && itC->yp > BoundYup)
--itC;
it=itC;
while( it != TL.end() && it->yp < BoundYdown) {
shownStrings.push_back(&*it);
if (it->textRect.top != it->yp - OffsetY) {
int diff = it->textRect.top - (it->yp - OffsetY);
it->textRect.top -= diff;
it->textRect.bottom -= diff;
}
++it;
}
}
}
TextInfo &gdioutput::addTimer(int yp, int xp, int format, DWORD zeroTime, int xlimit,
GUICALLBACK cb, int timeOut, const wchar_t *fontFace) {
hasAnyTimer = true;
DWORD zt=GetTickCount()-1000*zeroTime;
wstring text = getTimerText(zeroTime, format, true);
addStringUT(yp, xp, format, text, xlimit, cb, fontFace);
TextInfo &ti=TL.back();
ti.hasTimer=true;
ti.zeroTime=zt;
if (timeOut != NOTIMEOUT)
ti.timeOut = ti.zeroTime + timeOut*1000;
return ti;
}
TextInfo &gdioutput::addTimeout(int TimeOut, GUICALLBACK cb) {
addStringUT(0, 0, 0, "", 0, cb);
TextInfo &ti=TL.back();
ti.hasTimer=true;
ti.zeroTime=GetTickCount();
if (TimeOut!=NOTIMEOUT)
ti.timeOut=ti.zeroTime+(TimeOut)*1000;
return ti;
}
void CALLBACK gdiTimerProc(HWND hWnd, UINT a, UINT_PTR ptr, DWORD b) {
wstring msg;
KillTimer(hWnd, ptr);
TimerInfo *it = (TimerInfo *)ptr;
it->setWnd = 0;
try {
if (it->parent) {
it->parent->timerProc(*it, b);
}
}
catch (const meosCancel&) {
return;
}
catch (meosException &ex) {
msg = ex.wwhat();
}
catch(std::exception &ex) {
string2Wide(ex.what(), msg);
if (msg.empty())
msg = L"Ett okänt fel inträffade.";
}
catch(...) {
msg = L"Unexpected error";
}
if (!msg.empty()) {
MessageBox(hWnd, msg.c_str(), L"MeOS", MB_OK|MB_ICONEXCLAMATION);
}
}
int TimerInfo::globalTimerId = 0;
void gdioutput::timerProc(TimerInfo &timer, DWORD timeout) {
int timerId = timer.timerId;
if (timer.handler)
timer.handler->handle(*this, timer, GUI_TIMER);
if (timer.managedHandler)
timer.managedHandler->handle(*this, timer, GUI_TIMER);
else if (timer.callBack)
timer.callBack(this, GUI_TIMER, &timer);
for (auto it = timers.begin(); it != timers.end(); ++it) {
if (it->getId() == timerId) {
timers.erase(it);
break;
}
}
//timers.erase(remove_if(timers.begin(), timers.end(), [timerId](TimerInfo &x) {return x.getId() == timerId; }), timers.end());
}
void gdioutput::removeHandler(GuiHandler *h) {
for (auto &it : timers) {
if (it.handler == h)
it.handler = 0;
}
for (auto &it : BI) {
if (it.handler == h)
it.handler = 0;
}
for (auto &it : II) {
if (it.handler == h)
it.handler = 0;
}
for (auto &it : TL) {
if (it.handler == h)
it.handler = 0;
}
for (auto &it : LBI) {
if (it.handler == h)
it.handler = 0;
}
}
void gdioutput::removeTimeoutMilli(const string &id) {
for (list<TimerInfo>::iterator it = timers.begin(); it != timers.end(); ++it) {
if (it->id == id) {
timers.erase(it);
return;
}
}
}
TimerInfo &gdioutput::addTimeoutMilli(int timeOut, const string &id, GUICALLBACK cb)
{
removeTimeoutMilli(id);
timers.emplace_back(this, cb);
timers.back().id = id;
SetTimer(hWndTarget, (UINT_PTR)&timers.back(), timeOut, gdiTimerProc);
timers.back().setWnd = hWndTarget;
return timers.back();
}
TimerInfo:: ~TimerInfo() {
handler = 0;
callBack = 0;
if (setWnd)
KillTimer(setWnd, (UINT_PTR)this);
}
TextInfo& gdioutput::addImage(const string& id, int yp, int xp, int format,
const wstring& imageId, int width, int height, GUICALLBACK cb) {
bool skipBBCalc = (format & skipBoundingBox) == skipBoundingBox;
format &= ~skipBoundingBox;
int oldYP = TL.empty() ? -1 : TL.back().yp;
TL.emplace_back();
TextInfo& TI = TL.back();
itTL = TL.begin();
imageReferences.push_back(&TI);
TI.format = format | textImage;
TI.xp = xp;
TI.yp = yp;
TI.text = L"L" + imageId;
TI.callBack = cb;
if (width == 0 || height == 0) {
uint64_t imgId = _wcstoui64(imageId.c_str(), nullptr, 10);
width = image.getWidth(imgId);
height = image.getHeight(imgId);
}
//if (skipBBCalc) {
TI.textRect.left = xp;
TI.textRect.top = yp;
TI.textRect.right = xp + width;
TI.textRect.bottom = yp + height;
TI.realWidth = width;
FlowDirection oldDir = flowDirection;
if (format & imageNoUpdatePos)
flowDirection = FlowDirection::None;
updatePos(TI.xp, TI.yp, width + scaleLength(10),
height + scaleLength(2));
flowDirection = oldDir;
if (oldYP > TI.yp)
renderOptimize = false;
return TL.back();
}
TextInfo &gdioutput::addStringUT(int yp, int xp, int format, const string &text,
int xlimit, GUICALLBACK cb, const wchar_t *fontFace) {
return addStringUT(yp, xp, format, widen(text), xlimit, cb, fontFace);
}
int gdioutput::getFontHeight(int format, const wstring &fontFace) const {
format = format & 0xFF;
auto res = fontHeightCache.find(make_pair(format, fontFace));
if (res != fontHeightCache.end())
return res->second;
TextInfo TI;
TI.format = format;
TI.xp = 0;
TI.yp = 0;
TI.text = L"M1y|";
TI.xlimit = 100;
TI.callBack = 0;
TI.font = fontFace;
calcStringSize(TI);
int h = TI.textRect.bottom - TI.textRect.top;
fontHeightCache.emplace(make_pair(format, fontFace), h);
return h;
}
TextInfo& gdioutput::addStringUT(int yp, int xp, int format, const wstring& text,
int xlimit, GUICALLBACK cb, const wchar_t* fontFace)
{
bool skipBBCalc = (format & skipBoundingBox) == skipBoundingBox;
format &= ~skipBoundingBox;
int oldYP = TL.empty() ? -1 : TL.back().yp;
TL.emplace_back();
TextInfo& TI = TL.back();
itTL = TL.begin();
if ((format & 0xFF) == textImage)
imageReferences.push_back(&TI);
TI.format = format;
TI.xp = xp;
TI.yp = yp;
TI.text = text;
TI.xlimit = xlimit;
TI.callBack = cb;
if (fontFace)
TI.font = fontFace;
if (!skipTextRender(format)) {
if (skipBBCalc) {
assert(xlimit > 0);
int h = getFontHeight(format, fontFace);
TI.textRect.left = xp;
TI.textRect.top = yp;
TI.textRect.right = xp + xlimit;
TI.textRect.bottom = yp + h;
TI.realWidth = xlimit;
updatePos(TI.xp, TI.yp, TI.realWidth + scaleLength(10),
TI.textRect.bottom - TI.textRect.top + scaleLength(2));
maxTextBlockHeight = max(maxTextBlockHeight, h + 1);
}
else {
HDC hDC = GetDC(hWndTarget);
if (hWndTarget && !manualUpdate)
RenderString(TI, hDC);
else
calcStringSize(TI, hDC);
if (xlimit == 0 || (format & (textRight | textCenter)) == 0) {
updatePosTight(TI.textRect.left, TI.yp,
TI.realWidth, TI.textRect.bottom - TI.textRect.top,
scaleLength(10), scaleLength(2));
}
else {
updatePosTight(TI.xp, TI.yp,
TI.realWidth, TI.textRect.bottom - TI.textRect.top,
scaleLength(10), scaleLength(2));
}
ReleaseDC(hWndTarget, hDC);
maxTextBlockHeight = max<int>(maxTextBlockHeight, 1 + TI.textRect.bottom - TI.textRect.top);
}
if (oldYP > TI.yp)
renderOptimize = false;
}
else {
TI.textRect.left = xp;
TI.textRect.right = xp;
TI.textRect.bottom = yp;
TI.textRect.top = yp;
}
return TL.back();
}
TextInfo &gdioutput::addString(const char *id, int yp, int xp, int format, const string &text,
int xlimit, GUICALLBACK cb, const wchar_t *fontFace)
{
return addString(id, yp, xp, format, widen(text), xlimit, cb, fontFace);
}
TextInfo& gdioutput::addString(const char* id, int yp, int xp, int format, const wstring& text,
int xlimit, GUICALLBACK cb, const wchar_t* fontFace)
{
int oldYP = TL.empty() ? -1 : TL.back().yp;
TL.emplace_back();
itTL = TL.begin();
TextInfo& TI = TL.back();
if ((format & 0xFF) == textImage)
imageReferences.push_back(&TI);
TI.format = format;
TI.xp = xp;
TI.yp = yp;
TI.text = lang.tl(text);
if ((format & Capitalize) == Capitalize && lang.capitalizeWords())
capitalizeWords(TI.text);
TI.id = id;
TI.xlimit = xlimit;
TI.callBack = cb;
if (fontFace)
TI.font = fontFace;
if (!skipTextRender(format)) {
HDC hDC = GetDC(hWndTarget);
if (hWndTarget && !manualUpdate)
RenderString(TI, hDC);
else
calcStringSize(TI, hDC);
if (xlimit == 0 || (format & (textRight | textCenter)) == 0) {
updatePos(TI.textRect.right + OffsetX, yp, scaleLength(10),
TI.textRect.bottom - TI.textRect.top + scaleLength(2));
}
else {
updatePos(TI.xp, TI.yp, TI.realWidth + scaleLength(10),
TI.textRect.bottom - TI.textRect.top + scaleLength(2));
}
ReleaseDC(hWndTarget, hDC);
maxTextBlockHeight = max<int>(maxTextBlockHeight, TI.textRect.bottom - TI.textRect.top + 1);
if (oldYP > TI.yp)
renderOptimize = false;
}
else {
TI.textRect.left = xp;
TI.textRect.right = xp;
TI.textRect.bottom = yp;
TI.textRect.top = yp;
}
return TL.back();
}
TextInfo &gdioutput::addString(const string &id, int format, const string &text, GUICALLBACK cb) {
return addString(id.c_str(), CurrentY, CurrentX, format, text, 0, cb);
}
TextInfo &gdioutput::addString(const string &id, int format, const wstring &text, GUICALLBACK cb) {
return addString(id.c_str(), CurrentY, CurrentX, format, text, 0, cb);
}
TextInfo &gdioutput::addString(const string &id, int yp, int xp, int format, const string &text,
int xlimit, GUICALLBACK cb, const wchar_t *fontFace) {
return addString(id.c_str(), yp, xp, format, text, xlimit, cb, fontFace);
}
TextInfo &gdioutput::addString(const string &id, int yp, int xp, int format, const wstring &text,
int xlimit, GUICALLBACK cb, const wchar_t *fontFace) {
return addString(id.c_str(), yp, xp, format, text, xlimit, cb, fontFace);
}
TextInfo &gdioutput::addString(const char *id, int format, const string &text, GUICALLBACK cb)
{
return addString(id, CurrentY, CurrentX, format, text, 0, cb);
}
TextInfo &gdioutput::addString(const char *id, int format, const wstring &text, GUICALLBACK cb)
{
return addString(id, CurrentY, CurrentX, format, text, 0, cb);
}
TextInfo &gdioutput::addStringUT(int format, const string &text, GUICALLBACK cb)
{
return addStringUT(CurrentY, CurrentX, format, text, 0, cb);
}
TextInfo &gdioutput::addStringUT(int format, const wstring &text, GUICALLBACK cb)
{
return addStringUT(CurrentY, CurrentX, format, text, 0, cb);
}
ButtonInfo &gdioutput::addButton(const string &id, const string &text, GUICALLBACK cb,
const string &tooltip)
{
return addButton(CurrentX, CurrentY, id, text, cb, tooltip);
}
ButtonInfo &gdioutput::addButton(const string &id, const wstring &text, GUICALLBACK cb,
const wstring &tooltip)
{
return addButton(CurrentX, CurrentY, id, text, cb, tooltip);
}
ButtonInfo &gdioutput::addButton(int x, int y, const string &id, const string &text, GUICALLBACK cb,
const string &tooltip)
{
return addButton(x,y, id, widen(text), cb, widen(tooltip));
}
ButtonInfo &gdioutput::addButton(int x, int y, const string &id, const wstring &text, GUICALLBACK cb,
const wstring &tooltip)
{
HANDLE bm = 0;
int width = 0;
if (text[0] == '@') {
HINSTANCE hInst = GetModuleHandle(0); int ir = _wtoi(text.c_str() + 1);
bm = LoadBitmap(hInst, MAKEINTRESOURCE(ir));
SIZE size;
size.cx = 24;
width = size.cx+4;
}
else {
SIZE size;
HDC hDC = GetDC(hWndTarget);
SelectObject(hDC, getGUIFont());
wstring ttext = lang.tl(text);
int tts = ttext.size();
if (tts > 2 && ttext[0] == '<' && ttext[1] == '<') {
ttext = L"" + ttext.substr(2);
}
else if (tts > 2 && ttext[tts-1] == '>' && ttext[tts-2] == '>') {
ttext = ttext.substr(0, tts-2) + L"";
}
if (lang.capitalizeWords())
capitalizeWords(ttext);
GetTextExtentPoint32(hDC, ttext.c_str(), ttext.length(), &size);
ReleaseDC(hWndTarget, hDC);
width = size.cx + scaleLength(30);
if (text != L"...")
width = max<int>(width, scaleLength(75));
}
ButtonInfo &bi=addButton(x, y, width, id, text, cb, tooltip, false, false);
if (bm != 0) {
SendMessage(bi.hWnd, BM_SETIMAGE, IMAGE_BITMAP, LPARAM(bm));
}
return bi;
}
ButtonInfo &ButtonInfo::setDefault()
{
flags |= 1;
storedFlags |= 1;
//SetWindowLong(hWnd, i, GetWindowLong(hWnd, i)|BS_DEFPUSHBUTTON);
return *this;
}
void ButtonInfo::moveButton(gdioutput &gdi, int nxp, int nyp) {
xp = nxp;
yp = nyp;
int w, h;
getDimension(gdi, w, h);
MoveWindow(hWnd, xp, yp, w, h, true);
gdi.updatePos(xp, yp, w, h);
}
void ButtonInfo::getDimension(gdioutput &gdi, int &w, int &h) {
RECT rc;
GetWindowRect(hWnd, &rc);
w = rc.right - rc.left + gdi.scaleLength(GDI_BUTTON_SPACING);
h = rc.bottom - rc.top;
}
ButtonInfo &gdioutput::addButton(int x, int y, int w, const string &id,
const string &text, GUICALLBACK cb, const string &tooltip,
bool AbsPos, bool hasState) {
return addButton(x, y, w, id, widen(text), cb, widen(tooltip), AbsPos, hasState);
}
ButtonInfo &gdioutput::addButton(int x, int y, int w, const string &id,
const wstring &text, GUICALLBACK cb, const wstring &tooltip,
bool AbsPos, bool hasState)
{
int style = hasState ? BS_CHECKBOX|BS_PUSHLIKE : BS_PUSHBUTTON;
if (text[0] == '@')
style |= BS_BITMAP;
ButtonInfo bi;
wstring ttext = lang.tl(text);
int tts = ttext.size();
if (tts > 2 && ttext[0] == '<' && ttext[1] == '<') {
ttext = L"" + ttext.substr(2);
}
else if (tts > 2 && ttext[tts - 1] == '>' && ttext[tts - 2] == '>') {
ttext = ttext.substr(0, tts - 2) + L"";
}
if (lang.capitalizeWords())
capitalizeWords(ttext);
int height = getButtonHeight();
if (AbsPos){
if (ttext.find_first_of('\n') != string::npos) { //WCS
style |= BS_MULTILINE;
height *= 2;
}
bi.hWnd=CreateWindow(L"BUTTON", ttext.c_str(), WS_TABSTOP|WS_VISIBLE|WS_CHILD | WS_CLIPSIBLINGS |style|BS_NOTIFY,
x-OffsetX, y, w, height, hWndTarget, NULL,
(HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE), NULL);
}
else {
bi.hWnd=CreateWindow(L"BUTTON", ttext.c_str(), WS_TABSTOP|WS_VISIBLE|WS_CHILD | WS_CLIPSIBLINGS |style|BS_NOTIFY,
x-OffsetX, y-OffsetY-1, w, height, hWndTarget, NULL,
(HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE), NULL);
}
SendMessage(bi.hWnd, WM_SETFONT, (WPARAM) getGUIFont(), 0);
if (!AbsPos)
updatePos(x, y, w+scaleLength(GDI_BUTTON_SPACING), height+5);
bi.xp=x;
bi.yp=y;
bi.width = w;
bi.text=ttext;
bi.id=id;
bi.callBack=cb;
bi.AbsPos=AbsPos;
if (tooltip.length()>0)
addToolTip(id, tooltip, bi.hWnd);
BI.push_back(bi);
biByHwnd[bi.hWnd] = &BI.back();
FocusList.push_back(bi.hWnd);
return BI.back();
}
static int checkBoxCallback(gdioutput *gdi, int type, void *data) {
if (type == GUI_LINK) {
TextInfo *ti = (TextInfo *)data;
string cid = ti->id.substr(1);
gdi->check(cid, !gdi->isChecked(cid), true);
ButtonInfo &bi = ((ButtonInfo &)gdi->getBaseInfo(cid.c_str()));
if (bi.callBack || bi.hasEventHandler())
gdi->sendCtrlMessage(cid);
//gdi->getBaseInfo(cid);
}
return 0;
}
void gdioutput::enableCheckBoxLink(TextInfo &ti, bool enable) {
bool needRefresh = false;
if (enable) {
needRefresh = ti.callBack == 0;
ti.callBack = checkBoxCallback;
ti.setColor(colorDefault);
}
else {
needRefresh = ti.callBack != 0;
ti.callBack = 0;
DWORD c = GetSysColor(COLOR_GRAYTEXT);
ti.setColor(GDICOLOR(c));
}
if (needRefresh)
InvalidateRect(hWndTarget, &ti.textRect, true);
}
ButtonInfo &gdioutput::addCheckbox(const string &id, const string &text,
GUICALLBACK cb, bool Checked, const string &tooltip)
{
return addCheckbox(CurrentX, CurrentY, id, text, cb, Checked, tooltip);
}
ButtonInfo &gdioutput::addCheckbox(const string &id, const wstring &text,
GUICALLBACK cb, bool Checked, const wstring &tooltip)
{
return addCheckbox(CurrentX, CurrentY, id, text, cb, Checked, tooltip);
}
ButtonInfo &gdioutput::addCheckbox(int x, int y, const string &id, const string &text,
GUICALLBACK cb, bool Checked, const string &tooltip, bool AbsPos)
{
return addCheckbox(x,y,id, widen(text), cb, Checked, widen(tooltip), AbsPos);
}
ButtonInfo &gdioutput::addCheckbox(int x, int y, const string &id, const wstring &text,
GUICALLBACK cb, bool Checked, const wstring &tooltip, bool AbsPos)
{
ButtonInfo bi;
SIZE size;
wstring ttext = lang.tl(text);
HDC hDC=GetDC(hWndTarget);
SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
GetTextExtentPoint32(hDC, L"M", 1, &size);
int ox=OffsetX;
int oy=OffsetY;
if (AbsPos) {
ox=0;
oy=0;
}
int h = size.cy;
SelectObject(hDC, getGUIFont());
GetTextExtentPoint32(hDC, ttext.c_str(), ttext.length(), &size);
ReleaseDC(hWndTarget, hDC);
bi.hWnd=CreateWindowEx(0,L"BUTTON", L"", WS_TABSTOP|WS_VISIBLE|
WS_CHILD | WS_CLIPSIBLINGS |BS_AUTOCHECKBOX|BS_NOTIFY,
x-ox, y-oy + (size.cy-h)/2, h, h, hWndTarget, NULL,
(HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE), NULL);
TextInfo &desc = addStringUT(y , x + (3*h)/2, 0, ttext, 0, checkBoxCallback);
desc.id = "T" + id;
SendMessage(bi.hWnd, WM_SETFONT, (WPARAM) getGUIFont(), 0);
if (Checked)
SendMessage(bi.hWnd, BM_SETCHECK, BST_CHECKED, 0);
bi.checked = Checked;
if (!AbsPos) {
if (ttext.empty())
updatePos(x, y, size.cx + int(30 * scale), size.cy + int(scale * 12) + 3);
else
updatePos(x, y, size.cx + int(30 * scale), desc.textRect.bottom - desc.textRect.top + scaleLength(4));
}
if (tooltip.length()>0) {
addToolTip(id, tooltip, bi.hWnd);
addToolTip(desc.id, tooltip, 0, &desc.textRect);
}
bi.isCheckbox = true;
bi.xp=x;
bi.yp=y;
bi.width = desc.textRect.right - (x-ox);
bi.text=ttext;
bi.id=id;
bi.callBack=cb;
bi.AbsPos=AbsPos;
bi.originalState = Checked;
bi.isEdit(true);
BI.push_back(bi);
biByHwnd[bi.hWnd] = &BI.back();
FocusList.push_back(bi.hWnd);
return BI.back();
}
bool gdioutput::isChecked(const string &id)
{
list<ButtonInfo>::iterator it;
for(it=BI.begin(); it != BI.end(); ++it)
if (it->id==id)
return SendMessage(it->hWnd, BM_GETCHECK, 0, 0)==BST_CHECKED;
return false;
}
void gdioutput::check(const string &id, bool state, bool keepOriginalState){
list<ButtonInfo>::iterator it;
for(it=BI.begin(); it != BI.end(); ++it) {
if (it->id==id){
SendMessage(it->hWnd, BM_SETCHECK, state ? BST_CHECKED:BST_UNCHECKED, 0);
it->checked = state;
it->synchData();
if (!keepOriginalState)
it->originalState = state;
return;
}
}
#ifdef _DEBUG
string err = string("Internal Error, identifier not found: X#") + id;
throw std::exception(err.c_str());
#endif
}
InputInfo &gdioutput::addInput(const string &id, const wstring &text, int length,
GUICALLBACK cb, const wstring &explanation, const wstring &help)
{
return addInput(CurrentX, CurrentY, id, text, length, cb, explanation, help);
}
HFONT gdioutput::getGUIFont() const
{
if (scale==1)
return (HFONT)GetStockObject(DEFAULT_GUI_FONT);
else
return getCurrentFont().getGUIFont();
}
pair<int, int> gdioutput::getInputDimension(int length) const {
if (!guiMeasure) {
HDC hDC = GetDC(hWndTarget);
SelectObject(hDC, getGUIFont());
SIZE size;
GetTextExtentPoint32(hDC, L"M", 1, &size);
SIZE sizeAvg;
wstring avgText = L"123456789ABCDEFGHIJHKLMNOPQRSTUVXYZ abcdefghijklmnopqrstuvxyz";
GetTextExtentPoint32(hDC, avgText.c_str(), avgText.length(), &sizeAvg);
ReleaseDC(hWndTarget, hDC);
int dy = GetSystemMetrics(SM_CYEDGE);
int dx = GetSystemMetrics(SM_CXEDGE);
guiMeasure = make_shared<GuiMeasure>();
guiMeasure->letterWidth = size.cx;
guiMeasure->extraX = 2 * dx;
guiMeasure->height = 4 + dy * 2 + size.cy;
guiMeasure->avgCharWidth = float(sizeAvg.cx) / float(avgText.length());
}
return make_pair(length * guiMeasure->letterWidth + guiMeasure->extraX, guiMeasure->height);
}
int gdioutput::getButtonHeight() const {
return int(getInputDimension(0).second * 1.2);//int(scale * 24) + 0;
}
InputInfo &gdioutput::addInput(int x, int y, const string &id, const wstring &text,
int length, GUICALLBACK cb,
const wstring &explanation, const wstring &help) {
if (explanation.length()>0) {
addString(id + "_label", y, x, 0, explanation);
y+=lineHeight;
}
InputInfo ii;
auto dim = getInputDimension(length);
int ox=OffsetX;
int oy=OffsetY;
ii.hWnd=CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", text.c_str(),
WS_TABSTOP|WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | ES_AUTOHSCROLL | WS_BORDER,
x-ox, y-oy, dim.first, dim.second,
hWndTarget, NULL, (HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE), NULL);
int mrg = scaleLength(4);
updatePos(x, y, dim.first+mrg, dim.second+mrg);
SendMessage(ii.hWnd, WM_SETFONT,
(WPARAM) getGUIFont(), 0);
ii.xp=x;
ii.yp=y;
ii.width = dim.first;
ii.height = dim.second;
ii.text = text;
ii.original = text;
ii.focusText = text;
ii.id=id;
ii.callBack=cb;
II.push_back(ii);
iiByHwnd[ii.hWnd] = &II.back();
if (help.length() > 0)
addToolTip(id, help, ii.hWnd);
FocusList.push_back(ii.hWnd);
if (II.size() == 1) {
SetFocus(ii.hWnd);
currentFocus = ii.hWnd;
}
return II.back();
}
InputInfo &gdioutput::addInputBox(const string &id, int width, int height, const wstring &text,
GUICALLBACK cb, const wstring &explanation)
{
return addInputBox(id, CurrentX, CurrentY, width, height, text, cb, explanation);
}
InputInfo &gdioutput::addInputBox(const string &id, int x, int y, int width, int height,
const wstring &text, GUICALLBACK cb, const wstring &Explanation)
{
if (Explanation.length()>0) {
addString("", y, x, 0, Explanation);
y+=lineHeight;
}
InputInfo ii;
int ox=OffsetX;
int oy=OffsetY;
ii.hWnd=CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", text.c_str(), WS_HSCROLL|WS_VSCROLL|
WS_TABSTOP|WS_VISIBLE|WS_CHILD | WS_CLIPSIBLINGS |ES_AUTOHSCROLL|ES_MULTILINE|ES_AUTOVSCROLL|WS_BORDER,
x-ox, y-oy, width, height, hWndTarget, NULL,
(HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE), NULL);
updatePos(x, y, width, height);
SendMessage(ii.hWnd, WM_SETFONT, (WPARAM) getGUIFont(), 0);
ii.xp=x;
ii.yp=y;
ii.width = width;
ii.height = height;
ii.text = text;
ii.original = text;
ii.focusText = text;
ii.id=id;
ii.callBack=cb;
II.push_back(ii);
iiByHwnd[ii.hWnd] = &II.back();
//if (Help.length() > 0)
// addToolTip(Help, ii.hWnd);
FocusList.push_back(ii.hWnd);
return II.back();
}
ListBoxInfo &gdioutput::addListBox(const string &id, int width, int height, GUICALLBACK cb, const wstring &explanation, const wstring &tooltip, bool multiple)
{
return addListBox(CurrentX, CurrentY, id, width, height, cb, explanation, tooltip, multiple);
}
LRESULT CALLBACK GetMsgProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
ListBoxInfo *lbi = (ListBoxInfo *)(GetWindowLongPtr(hWnd, GWLP_USERDATA));
if (!lbi) {
throw std::exception("Internal GDI error");
}
LPARAM res = CallWindowProc(lbi->originalProc, hWnd, iMsg, wParam, lParam);
if (iMsg == WM_VSCROLL || iMsg == WM_MOUSEWHEEL || iMsg == WM_KEYDOWN) {
LRESULT topIndex = CallWindowProc(lbi->originalProc, hWnd, LB_GETTOPINDEX, 0, 0);
if (lbi->lbiSync) {
ListBoxInfo *other = lbi->lbiSync;
CallWindowProc(other->originalProc, other->hWnd, LB_SETTOPINDEX, topIndex, 0);
}
}
return res;
}
void gdioutput::synchronizeListScroll(const string &id1, const string &id2)
{
ListBoxInfo *a = 0, *b = 0;
list<ListBoxInfo>::iterator it;
for (it = LBI.begin(); it != LBI.end(); ++it) {
if (it->id == id1)
a = &*it;
else if (it->id == id2)
b = &*it;
}
if (!a || !b)
throw std::exception("Not found");
a->lbiSync = b;
b->lbiSync = a;
SetWindowLongPtr(a->hWnd, GWLP_USERDATA, LONG_PTR(a));
SetWindowLongPtr(b->hWnd, GWLP_USERDATA, LONG_PTR(b));
a->originalProc = WNDPROC(GetWindowLongPtr(a->hWnd, GWLP_WNDPROC));
b->originalProc = WNDPROC(GetWindowLongPtr(b->hWnd, GWLP_WNDPROC));
SetWindowLongPtr(a->hWnd, GWLP_WNDPROC, LONG_PTR(GetMsgProc));
SetWindowLongPtr(b->hWnd, GWLP_WNDPROC, LONG_PTR(GetMsgProc));
}
ListBoxInfo &gdioutput::addListBox(int x, int y, const string &id, int width, int height, GUICALLBACK cb,
const wstring &explanation, const wstring &tooltip, bool multiple) {
if (explanation.length()>0) {
addString(id+"_label", y, x, 0, explanation);
y+=lineHeight;
}
ListBoxInfo lbi;
int ox=OffsetX;
int oy=OffsetY;
DWORD style=WS_TABSTOP|WS_VISIBLE|WS_CHILD | WS_CLIPSIBLINGS |WS_BORDER|LBS_USETABSTOPS|LBS_NOTIFY|WS_VSCROLL;
if (multiple)
style|=LBS_MULTIPLESEL;
lbi.hWnd=CreateWindowEx(WS_EX_CLIENTEDGE, L"LISTBOX", L"", style,
x-ox, y-oy, int(width*scale), int(height*scale), hWndTarget, NULL,
(HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE), NULL);
updatePos(x, y, int(scale*(width+5)), int(scale * (height+2)));
SendMessage(lbi.hWnd, WM_SETFONT, (WPARAM) getGUIFont(), 0);
lbi.IsCombo=false;
lbi.multipleSelection = multiple;
lbi.xp=x;
lbi.yp=y;
lbi.width = scale*width;
lbi.height = scale*height;
lbi.id=id;
lbi.callBack=cb;
LBI.push_back(lbi);
lbiByHwnd[lbi.hWnd] = &LBI.back();
if (tooltip.length() > 0)
addToolTip(id, tooltip, lbi.hWnd);
FocusList.push_back(lbi.hWnd);
return LBI.back();
}
void gdioutput::setSelection(const string &id, const set<int> &selection)
{
list<ListBoxInfo>::iterator it;
for(it=LBI.begin(); it != LBI.end(); ++it){
if (it->id==id && !it->IsCombo) {
list<int>::const_iterator cit;
if (selection.count(-1)==1)
SendMessage(it->hWnd, LB_SETSEL, 1, -1);
else {
LRESULT count=SendMessage(it->hWnd, LB_GETCOUNT, 0,0);
SendMessage(it->hWnd, LB_SETSEL, 0, -1);
for(int i=0;i<count;i++){
LRESULT d=SendMessage(it->hWnd, LB_GETITEMDATA, i, 0);
if (selection.count(int(d))==1)
SendMessage(it->hWnd, LB_SETSEL, 1, i);
}
return;
}
}
}
}
void gdioutput::getSelection(const string &id, set<int> &selection) {
list<ListBoxInfo>::iterator it;
for(it=LBI.begin(); it != LBI.end(); ++it){
if (it->id==id && !it->IsCombo) {
selection.clear();
LRESULT count=SendMessage(it->hWnd, LB_GETCOUNT, 0,0);
for(int i=0;i<count;i++){
LRESULT s=SendMessage(it->hWnd, LB_GETSEL, i, 0);
if (s) {
LRESULT d=SendMessage(it->hWnd, LB_GETITEMDATA, i, 0);
selection.insert(int(d));
}
}
return;
}
}
#ifdef _DEBUG
string err = string("Internal Error, identifier not found: X#") + id;
throw std::exception(err.c_str());
#endif
}
ListBoxInfo &gdioutput::addSelection(const string &id, int width, int height, GUICALLBACK cb, const wstring &explanation, const wstring &tooltip)
{
return addSelection(CurrentX, CurrentY, id, width, height, cb, explanation, tooltip);
}
ListBoxInfo &gdioutput::addSelection(int x, int y, const string &id, int width, int height,
GUICALLBACK cb, const wstring &explanation, const wstring &tooltip)
{
if (explanation.length()>0) {
addString(id + "_label", y, x, 0, explanation);
y+=lineHeight;
}
ListBoxInfo lbi;
int ox = OffsetX;
int oy = OffsetY;
lbi.hWnd=CreateWindowEx(WS_EX_CLIENTEDGE, L"COMBOBOX", L"", WS_TABSTOP|WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS |WS_BORDER|CBS_DROPDOWNLIST|WS_VSCROLL ,
x-ox, y-oy, int(scale*width), int(scale*height), hWndTarget, NULL,
(HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE), NULL);
updatePos(x, y, int(scale*(width+5)), int(scale*30));
SendMessage(lbi.hWnd, WM_SETFONT, (WPARAM) getGUIFont(), 0);
lbi.IsCombo=true;
lbi.xp=x;
lbi.yp=y;
lbi.width = scale*width;
lbi.height = scale*30;
lbi.id=id;
lbi.callBack=cb;
LBI.push_back(lbi);
lbiByHwnd[lbi.hWnd] = &LBI.back();
if (tooltip.length() > 0)
addToolTip(id, tooltip, lbi.hWnd);
FocusList.push_back(lbi.hWnd);
return LBI.back();
}
ListBoxInfo &gdioutput::addCombo(const string &id, int width, int height, GUICALLBACK cb,
const wstring &explanation, const wstring &tooltip) {
return addCombo(CurrentX, CurrentY, id, width, height, cb, explanation, tooltip);
}
ListBoxInfo &gdioutput::addCombo(int x, int y, const string &id, int width, int height, GUICALLBACK cb,
const wstring &explanation, const wstring &tooltip) {
if (explanation.length()>0) {
addString(id + "_label", y, x, 0, explanation);
y+=lineHeight;
}
ListBoxInfo lbi;
int ox=OffsetX;
int oy=OffsetY;
lbi.hWnd=CreateWindowEx(WS_EX_CLIENTEDGE, L"COMBOBOX", L"", WS_TABSTOP|WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS |WS_BORDER|CBS_DROPDOWN |CBS_AUTOHSCROLL,
x-ox, y-oy, int(scale*width), int(scale*height), hWndTarget, NULL,
(HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE), NULL);
updatePos(x, y, int(scale * (width+5)), getButtonHeight()+scaleLength(5));
SendMessage(lbi.hWnd, WM_SETFONT, (WPARAM) getGUIFont(), 0);
lbi.IsCombo=true;
lbi.xp=x;
lbi.yp=y;
lbi.width = scale*width;
lbi.height = scale*height;
lbi.id=id;
lbi.callBack=cb;
LBI.push_back(lbi);
lbiByHwnd[lbi.hWnd] = &LBI.back();
if (tooltip.length() > 0)
addToolTip(id, tooltip, lbi.hWnd);
FocusList.push_back(lbi.hWnd);
return LBI.back();
}
bool gdioutput::addItem(const string &id, const wstring &text, size_t data)
{
list<ListBoxInfo>::reverse_iterator it;
for (it=LBI.rbegin(); it != LBI.rend(); ++it) {
if (it->id==id) {
if (it->IsCombo) {
LRESULT index=SendMessage(it->hWnd, CB_ADDSTRING, 0, LPARAM(text.c_str()));
SendMessage(it->hWnd, CB_SETITEMDATA, index, data);
it->data2Index[data] = int(index);
}
else {
LRESULT index=SendMessage(it->hWnd, LB_INSERTSTRING, -1, LPARAM(text.c_str()));
SendMessage(it->hWnd, LB_SETITEMDATA, index, data);
it->data2Index[data] = int(index);
}
return true;
}
}
return false;
}
bool gdioutput::addItem(const string& id, const vector< pair<wstring, size_t> >& items)
{
list<ListBoxInfo>::reverse_iterator it;
for (it = LBI.rbegin(); it != LBI.rend(); ++it) {
if (it->id == id) {
if (it->IsCombo) {
SendMessage(it->hWnd, CB_RESETCONTENT, 0, 0);
SendMessage(it->hWnd, CB_INITSTORAGE, items.size(), 48);
SendMessage(it->hWnd, WM_SETREDRAW, FALSE, 0);
it->data2Index.clear();
for (size_t k = 0; k < items.size(); k++) {
LRESULT index = SendMessage(it->hWnd, CB_ADDSTRING, 0, LPARAM(items[k].first.c_str()));
SendMessage(it->hWnd, CB_SETITEMDATA, index, items[k].second);
it->data2Index[items[k].second] = int(index);
}
SendMessage(it->hWnd, WM_SETREDRAW, TRUE, 0);
RedrawWindow(it->hWnd, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
}
else {
SendMessage(it->hWnd, LB_RESETCONTENT, 0, 0);
SendMessage(it->hWnd, LB_INITSTORAGE, items.size(), 48);
SendMessage(it->hWnd, WM_SETREDRAW, FALSE, 0);
it->data2Index.clear();
for (size_t k = 0; k < items.size(); k++) {
LRESULT index = SendMessage(it->hWnd, LB_INSERTSTRING, -1, LPARAM(items[k].first.c_str()));
SendMessage(it->hWnd, LB_SETITEMDATA, index, items[k].second);
it->data2Index[items[k].second] = int(index);
}
SendMessage(it->hWnd, WM_SETREDRAW, TRUE, 0);
RedrawWindow(it->hWnd, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
}
return true;
}
}
return false;
}
void gdioutput::filterOnData(const string &id, const unordered_set<int> &filter) {
list<ListBoxInfo>::iterator it;
for (it=LBI.begin(); it != LBI.end(); ++it) {
if (it->id==id) {
if (it->IsCombo) {
}
else {
const HWND &hWnd = it->hWnd;
LRESULT count = SendMessage(hWnd, LB_GETCOUNT, 0, 0);
for (intptr_t ix = count - 1; ix>=0; ix--) {
LRESULT ret = SendMessage(hWnd, LB_GETITEMDATA, ix, 0);
if (ret != LB_ERR && filter.count(int(ret)) == 0)
SendMessage(hWnd, LB_DELETESTRING, ix, 0);
}
return;
}
}
}
assert(false);
}
bool gdioutput::clearList(const string &id)
{
list<ListBoxInfo>::iterator it;
for(it=LBI.begin(); it != LBI.end(); ++it){
if (it->id==id) {
it->original = L"";
it->originalIdx = -1;
if (it->IsCombo)
SendMessage(it->hWnd, CB_RESETCONTENT , 0, 0);
else
SendMessage(it->hWnd, LB_RESETCONTENT , 0, 0);
return true;
}
}
return false;
}
bool gdioutput::getSelectedItem(const string &id, ListBoxInfo &lbi) {
lbi = ListBoxInfo();
list<ListBoxInfo>::iterator it;
for (it=LBI.begin(); it != LBI.end(); ++it) {
if (it->id==id) {
bool ret = getSelectedItem(*it);
it->copyUserData(lbi);
return ret;
}
}
return false;
}
pair<int, bool> gdioutput::getSelectedItem(const string &id) {
ListBoxInfo lbi;
bool ret = getSelectedItem(id, lbi);
return make_pair(lbi.getDataInt(), ret);
}
pair<int, bool> gdioutput::getSelectedItem(const char *id) {
string ids = id;
return getSelectedItem(ids);
}
void ListBoxInfo::copyUserData(ListBoxInfo &dest) const {
dest.data = data;
dest.text = text;
dest.id = id;
dest.extra = extra;
dest.index = index;
dest.IsCombo = IsCombo;
}
bool gdioutput::getSelectedItem(ListBoxInfo &lbi) {
if (lbi.IsCombo) {
LRESULT index=SendMessage(lbi.hWnd, CB_GETCURSEL, 0, 0);
if (index == CB_ERR) {
wchar_t bf[256];
GetWindowText(lbi.hWnd, bf, 256);
lbi.text=bf;
lbi.data=-1;
lbi.index=int(index);
return false;
}
lbi.data=SendMessage(lbi.hWnd, CB_GETITEMDATA, index, 0);
wchar_t bf[1024];
if (SendMessage(lbi.hWnd, CB_GETLBTEXT, index, LPARAM(bf)) != CB_ERR)
lbi.text=bf;
}
else {
LRESULT index=SendMessage(lbi.hWnd, LB_GETCURSEL, 0, 0);
if (index==LB_ERR)
return false;
lbi.data=SendMessage(lbi.hWnd, LB_GETITEMDATA, index, 0);
lbi.index=int(index);
TCHAR bf[1024];
if (SendMessage(lbi.hWnd, LB_GETTEXT, index, LPARAM(bf))!=LB_ERR)
lbi.text=bf;
}
return true;
}
int gdioutput::getNumItems(const char *id) {
for (auto &lbi : LBI) {
if (lbi.id == id) {
if (lbi.IsCombo) {
return (int)SendMessage(lbi.hWnd, CB_GETCOUNT, 0, 0);
}
else {
return (int)SendMessage(lbi.hWnd, LB_GETCOUNT, 0, 0);
}
}
}
#ifdef _DEBUG
string err = string("Internal Error, identifier not found: X#") + id;
throw std::exception(err.c_str());
#endif
return 0;
}
int gdioutput::getItemDataByName(const char *id, const char *name) const{
wstring wname = recodeToWide(name);
list<ListBoxInfo>::const_iterator it;
for(it = LBI.begin(); it != LBI.end(); ++it){
if (it->id==id) {
if (it->IsCombo) {
LRESULT ix = SendMessage(it->hWnd, CB_FINDSTRING, -1, LPARAM(wname.c_str()));
if (ix >= 0) {
return (int)SendMessage(it->hWnd, CB_GETITEMDATA, ix, 0);
}
return -1;
}
else {
LRESULT ix = SendMessage(it->hWnd, LB_FINDSTRING, -1, LPARAM(wname.c_str()));
if (ix >= 0) {
return (int)SendMessage(it->hWnd, LB_GETITEMDATA, ix, 0);
}
return -1;
}
}
}
return -1;
}
bool gdioutput::selectItemByData(const char *id, int data)
{
list<ListBoxInfo>::iterator it;
for(it=LBI.begin(); it != LBI.end(); ++it){
if (it->id==id) {
if (it->IsCombo) {
if (data==-1) {
SendMessage(it->hWnd, CB_SETCURSEL, -1, 0);
it->data = 0;
it->text = L"";
it->original = L"";
it->originalIdx = -1;
return true;
}
else {
LRESULT count = SendMessage(it->hWnd, CB_GETCOUNT, 0, 0);
for (int m = 0; m < count; m++) {
LRESULT ret = SendMessage(it->hWnd, CB_GETITEMDATA, m, 0);
if (ret == data) {
SendMessage(it->hWnd, CB_SETCURSEL, m, 0);
it->data = data;
it->originalIdx = data;
TCHAR bf[1024];
if (SendMessage(it->hWnd, CB_GETLBTEXT, m, LPARAM(bf))!=CB_ERR) {
it->text = bf;
it->original = bf;
}
return true;
}
}
}
return false;
}
else {
if (data==-1) {
SendMessage(it->hWnd, LB_SETCURSEL, -1, 0);
it->data=0;
it->text = L"";
it->original = L"";
it->originalIdx = -1;
return true;
}
else {
LRESULT count = SendMessage(it->hWnd, LB_GETCOUNT, 0, 0);
for (int m = 0; m < count; m++) {
LRESULT ret = SendMessage(it->hWnd, LB_GETITEMDATA, m, 0);
if (ret == data) {
SendMessage(it->hWnd, LB_SETCURSEL, m, 0);
it->data = data;
it->originalIdx = data;
TCHAR bf[1024];
if (SendMessage(it->hWnd, LB_GETTEXT, m, LPARAM(bf)) != LB_ERR) {
it->text = bf;
it->original = bf;
}
return true;
}
}
}
return false;
}
}
}
return false;
}
bool gdioutput::selectItemByIndex(const char *id, int index) {
for (auto it = LBI.begin(); it != LBI.end(); ++it) {
if (it->id == id) {
if (it->IsCombo) {
if (index == -1) {
SendMessage(it->hWnd, CB_SETCURSEL, -1, 0);
it->data = 0;
it->text = L"";
it->original = L"";
it->originalIdx = -1;
return true;
}
else {
SendMessage(it->hWnd, CB_SETCURSEL, index, 0);
LRESULT data = SendMessage(it->hWnd, CB_GETITEMDATA, index, 0);
it->data = data;
it->originalIdx = data;
TCHAR bf[1024];
if (SendMessage(it->hWnd, CB_GETLBTEXT, index, LPARAM(bf)) != CB_ERR) {
it->text = bf;
it->original = bf;
}
return true;
}
return false;
}
else {
if (index == -1) {
SendMessage(it->hWnd, LB_SETCURSEL, -1, 0);
it->data = 0;
it->text = L"";
it->original = L"";
it->originalIdx = -1;
return true;
}
else {
SendMessage(it->hWnd, LB_SETCURSEL, index, 0);
LRESULT data = SendMessage(it->hWnd, LB_GETITEMDATA, index, 0);
it->data = data;
it->originalIdx = data;
TCHAR bf[1024];
if (SendMessage(it->hWnd, LB_GETTEXT, index, LPARAM(bf)) != LB_ERR) {
it->text = bf;
it->original = bf;
}
return true;
}
return false;
}
}
}
return false;
}
bool gdioutput::autoGrow(const char *id) {
list<ListBoxInfo>::iterator it;
int size = 0;
TextInfo TI;
TI.format=0;
TI.xp=0;
TI.yp=0;
TI.id="";
TI.xlimit=0;
TI.callBack=0;
HDC hDC=GetDC(hWndTarget);
for(it=LBI.begin(); it != LBI.end(); ++it){
if (it->id==id) {
if (it->IsCombo) {
LRESULT count = SendMessage(it->hWnd, CB_GETCOUNT, 0, 0);
for (int m = 0; m < count; m++) {
wchar_t bf[1024];
if (SendMessage(it->hWnd, CB_GETLBTEXT, m, LPARAM(bf))!=CB_ERR) {
TI.text = bf;
calcStringSize(TI, hDC);
size = max<int>(size, TI.textRect.right - TI.textRect.left);
}
}
ReleaseDC(hWndTarget, hDC);
size += scaleLength(30);
if (size > it->width) {
it->width = size;
SetWindowPos(it->hWnd, 0, 0, 0, (int)it->width, (int)it->height, SWP_NOZORDER|SWP_NOCOPYBITS|SWP_NOMOVE);
updatePos(it->xp, it->yp, (int)it->width + int(scale*5), (int)it->height);
return true;
}
return false;
}
else {
LRESULT count = SendMessage(it->hWnd, LB_GETCOUNT, 0, 0);
for (int m = 0; m < count; m++) {
wchar_t bf[1024];
LRESULT len = SendMessage(it->hWnd, LB_GETTEXT, m, LPARAM(bf));
if (len!=LB_ERR) {
if (it->lastTabStop == 0)
TI.text = bf;
else {
auto pos = len;
while(pos > 0) {
if (bf[pos-1] == '\t') {
break;
}
pos--;
}
TI.text = &bf[pos];
}
calcStringSize(TI, hDC);
size = max<int>(size, TI.realWidth + it->lastTabStop);
}
}
ReleaseDC(hWndTarget, hDC);
size += scaleLength(30);
if (size > it->width) {
it->width = size;
SetWindowPos(it->hWnd, 0, 0, 0, (int)it->width, (int)it->height, SWP_NOZORDER|SWP_NOCOPYBITS|SWP_NOMOVE);
updatePos(it->xp, it->yp, (int)it->width+int(scale*5), (int)it->height);
return true;
}
return false;
}
}
}
ReleaseDC(hWndTarget, hDC);
return false;
}
void gdioutput::removeSelected(const char *id)
{
}
LRESULT gdioutput::ProcessMsg(UINT iMessage, LPARAM lParam, WPARAM wParam)
{
wstring msg;
try {
return ProcessMsgWrp(iMessage, lParam, wParam);
}
catch (const meosCancel&) {
return false;
}
catch (meosException & ex) {
msg = ex.wwhat();
}
catch(std::exception &ex) {
msg=widen(ex.what());
if (msg.empty())
msg=L"Ett okänt fel inträffade.";
}
catch(...) {
msg=L"Ett okänt fel inträffade.";
}
if (!msg.empty()) {
alert(msg);
setWaitCursor(false);
}
return 0;
}
void gdioutput::processButtonMessage(ButtonInfo &bi, WPARAM wParam)
{
WORD hwParam = HIWORD(wParam);
switch (hwParam) {
case BN_CLICKED: {
string cmd;
if (getRecorder().recording()) {
if (bi.isExtraString()) {
cmd = "press(\"" + bi.id + "\", \"" + narrow(bi.getExtra()) + "\"); //" + toUTF8(bi.text);
}
else {
int arg = int((size_t)bi.extra);
if (arg > 1000000 || arg < -1000000 || arg == 0)
cmd = "press(\"" + bi.id + "\"); //" + toUTF8(bi.text);
else
cmd = "press(\"" + bi.id + "\", " + itos(bi.getExtraInt()) + "); //" + toUTF8(bi.text);
}
}
if (bi.isCheckbox)
bi.checked = SendMessage(bi.hWnd, BM_GETCHECK, 0, 0)==BST_CHECKED;
bi.synchData();
if (bi.callBack || bi.handler) {
setWaitCursor(true);
if (!bi.handleEvent(*this, GUI_BUTTON) && bi.callBack)
bi.callBack(this, GUI_BUTTON, &bi); //it may be destroyed here...
setWaitCursor(false);
}
getRecorder().record(cmd);
break;
}
case BN_SETFOCUS:
if (currentFocus.hWnd != bi.hWnd) {
// if (currentF ocus.wasTabbed)
// Button_SetState(currentFocus.hWnd, false);
currentFocus = bi.hWnd;
}
break;
case BN_KILLFOCUS:
if (currentFocus.hWnd == bi.hWnd) {
// if (currentFocus.wasTabbed)
// Button_SetState(currentFocus.hWnd, false);
}
break;
}
}
void gdioutput::processEditMessage(InputInfo &bi, WPARAM wParam)
{
WORD hwParam = HIWORD(wParam);
switch (hwParam) {
case EN_CHANGE:
if (bi.writeLock)
return;
getWindowText(bi.hWnd, bi.text);
if (bi.handler)
bi.handler->handle(*this, bi, GUI_INPUTCHANGE);
else if (bi.managedHandler)
bi.managedHandler->handle(*this, bi, GUI_INPUTCHANGE);
else if (bi.callBack)
bi.callBack(this, GUI_INPUTCHANGE, &bi); //it may be destroyed here...
break;
case EN_KILLFOCUS: {
autoCompleteInfo.reset();
wstring old = bi.focusText;
getWindowText(bi.hWnd, bi.text);
bi.synchData();
bool equal = old == bi.text;
string cmd = "input(\"" + bi.id + "\", \"" + toUTF8(bi.text) + "\");";
if (bi.handler)
bi.handler->handle(*this, bi, GUI_INPUT);
else if (bi.managedHandler)
bi.managedHandler->handle(*this, bi, GUI_INPUT);
else if (bi.callBack)
bi.callBack(this, GUI_INPUT, &bi);
if (!equal)
getRecorder().record(cmd);
break;
}
case EN_SETFOCUS:
currentFocus = bi.hWnd;
getWindowText(bi.hWnd, bi.text);
bi.synchData();
bi.focusText = bi.text;
if (bi.handler)
bi.handler->handle(*this, bi, GUI_FOCUS);
else if (bi.managedHandler)
bi.managedHandler->handle(*this, bi, GUI_FOCUS);
else if (bi.callBack)
bi.callBack(this, GUI_FOCUS, &bi);
break;
}
}
void gdioutput::processComboMessage(ListBoxInfo &bi, WPARAM wParam)
{
WORD hwParam = HIWORD(wParam);
LRESULT index;
switch (hwParam) {
case CBN_SETFOCUS:
currentFocus = bi.hWnd;
lockUpDown = true;
break;
case CBN_KILLFOCUS: {
if (autoCompleteInfo && !autoCompleteInfo->locked())
autoCompleteInfo.reset();
lockUpDown = false;
TCHAR bf[1024];
index=SendMessage(bi.hWnd, CB_GETCURSEL, 0, 0);
if (index != CB_ERR) {
if (SendMessage(bi.hWnd, CB_GETLBTEXT, index, LPARAM(bf)) != CB_ERR) {
bi.text = bf;
bi.data=SendMessage(bi.hWnd, CB_GETITEMDATA, index, 0);
if (bi.handler)
bi.handler->handle(*this, bi, GUI_COMBO);
else if (bi.callBack)
bi.callBack(this, GUI_COMBO, &bi); //it may be destroyed here...
}
}
else {
GetWindowText(bi.hWnd, bf, sizeof(bf)-1);
bi.data = -1;
bi.text = bf;
string cmd = "input(\"" + bi.id + "\", \"" + toUTF8(bi.text) + "\");";
if (bi.handler)
bi.handler->handle(*this, bi, GUI_COMBO);
else if (bi.callBack)
bi.callBack(this, GUI_COMBO, &bi); //it may be destroyed here...
getRecorder().record(cmd);
}
}
break;
case CBN_EDITCHANGE: {
if (bi.writeLock)
return;
getWindowText(bi.hWnd, bi.text);
if (bi.handler)
bi.handler->handle(*this, bi, GUI_COMBOCHANGE);
else if (bi.callBack)
bi.callBack(this, GUI_COMBOCHANGE, &bi); //it may be destroyed here...
break;
}
case CBN_SELCHANGE:
index=SendMessage(bi.hWnd, CB_GETCURSEL, 0, 0);
if (index != CB_ERR) {
bi.data=SendMessage(bi.hWnd, CB_GETITEMDATA, index, 0);
TCHAR bf[1024];
if (SendMessage(bi.hWnd, CB_GETLBTEXT, index, LPARAM(bf)) != CB_ERR)
bi.text=bf;
string cmd = "select(\"" + bi.id + "\", " + itos(bi.data) + ");";
internalSelect(bi);
getRecorder().record(cmd);
}
break;
}
}
void gdioutput::keyCommand(KeyCommandCode code) {
if (hasCommandLock())
return;
if (code == KC_SLOWDOWN)
autoSpeed *= 0.9;
else if (code == KC_SPEEDUP)
autoSpeed *= 1.0/0.9;
wstring msg;
try {
list<TableInfo>::iterator tit;
if (useTables) {
for (tit=Tables.begin(); tit!=Tables.end(); ++tit)
if (tit->table->keyCommand(*this, code))
return;
}
for (list<EventInfo>::iterator it = Events.begin(); it != Events.end(); ++it) {
if (it->getKeyCommand() == code) {
it->setData("", 0);
it->setExtra(0);
if (!it->handleEvent(*this, GUI_EVENT) && it->callBack) {
it->callBack(this, GUI_EVENT, &*it); //it may be destroyed here...
}
return;
}
}
}
catch (const meosCancel&) {
return;
}
catch (meosException & ex) {
msg = ex.wwhat();
}
catch(std::exception &ex) {
msg = widen(ex.what());
if (msg.empty())
msg = L"Ett okänt fel inträffade.";
}
catch(...) {
msg = L"Ett okänt fel inträffade.";
}
if (!msg.empty())
alert(msg);
}
void gdioutput::processListMessage(ListBoxInfo &bi, WPARAM wParam)
{
WORD hwParam = HIWORD(wParam);
LRESULT index;
switch (hwParam) {
case LBN_SETFOCUS:
currentFocus = bi.hWnd;
lockUpDown = true;
break;
case LBN_KILLFOCUS:
autoCompleteInfo.reset();
lockUpDown = false;
break;
case LBN_SELCHANGE:
case LBN_DBLCLK:
index=SendMessage(bi.hWnd, LB_GETCURSEL, 0, 0);
if (index!=LB_ERR) {
bi.data = SendMessage(bi.hWnd, LB_GETITEMDATA, index, 0);
TCHAR bf[1024];
if (SendMessage(bi.hWnd, LB_GETTEXT, index, LPARAM(bf)) != LB_ERR)
bi.text = bf;
string cmd;
if (hwParam == LBN_SELCHANGE)
cmd = "select(\"" + bi.id + "\", " + itos(bi.data) + ");";
else
cmd = "dblclick(\"" + bi.id + "\", " + itos(bi.data) + ");";
if (bi.callBack || bi.handler) {
setWaitCursor(true);
if (hwParam == LBN_SELCHANGE) {
if (bi.handler)
bi.handler->handle(*this, bi, GUI_LISTBOX);
else
bi.callBack(this, GUI_LISTBOX, &bi); //it may be destroyed here...
}
else {
if (bi.handler)
bi.handler->handle(*this, bi, GUI_LISTBOXSELECT);
else
bi.callBack(this, GUI_LISTBOXSELECT, &bi); //it may be destroyed here...
}
setWaitCursor(false);
}
getRecorder().record(cmd);
}
break;
}
}
LRESULT gdioutput::ProcessMsgWrp(UINT iMessage, LPARAM lParam, WPARAM wParam)
{
if (iMessage == WM_COMMAND) {
WORD hwParam = HIWORD(wParam);
HWND hWnd = (HWND)lParam;
if (hwParam == EN_CHANGE) {
list<TableInfo>::iterator tit;
if (useTables)
for (tit = Tables.begin(); tit != Tables.end(); ++tit)
if (tit->table->inputChange(*this, hWnd))
return 0;
}
{
//list<ButtonInfo>::iterator it;
//for (it=BI.begin(); it != BI.end(); ++it) {
unordered_map<HWND, ButtonInfo*>::iterator it = biByHwnd.find(HWND(lParam));
// if (it->hWnd==hWnd) {
if (it != biByHwnd.end()) {
ButtonInfo &bi = *it->second;
processButtonMessage(bi, wParam);
return 0;
}
//}
}
{
unordered_map<HWND, InputInfo*>::iterator it = iiByHwnd.find(HWND(lParam));
if (it != iiByHwnd.end()) {
InputInfo &ii = *it->second;
processEditMessage(ii, wParam);
return 0;
}
//list<InputInfo>::iterator it;
/*for (it=II.begin(); it != II.end(); ++it) {
if (it->hWnd==hWnd) {
processEditMessage(*it, wParam);
return 0;
}
}*/
}
{
//list<ListBoxInfo>::iterator it;
//for(it=LBI.begin(); it != LBI.end(); ++it) {
unordered_map<HWND, ListBoxInfo*>::iterator it = lbiByHwnd.find(HWND(lParam));
if (it != lbiByHwnd.end()) {
ListBoxInfo &lbi = *it->second;
if (lbi.IsCombo)
processComboMessage(lbi, wParam);
else
processListMessage(lbi, wParam);
return 0;
}
}
}
else if (iMessage == WM_MOUSEMOVE) {
POINT pt;
pt.x = (signed short)LOWORD(lParam);
pt.y = (signed short)HIWORD(lParam);
list<TableInfo>::iterator tit;
bool GotCapture = false;
if (useTables)
for (tit = Tables.begin(); tit != Tables.end(); ++tit)
GotCapture = tit->table->mouseMove(*this, pt.x, pt.y) || GotCapture;
if (GotCapture)
return 0;
list<InfoBox>::iterator it = IBox.begin();
while (it != IBox.end()) {
if (PtInRect(&it->TextRect, pt) && (it->callBack || it->hasEventHandler())) {
SetCursor(LoadCursor(NULL, IDC_HAND));
HDC hDC = GetDC(hWndTarget);
drawBoxText(hDC, it->TextRect, *it, true);
ReleaseDC(hWndTarget, hDC);
SetCapture(hWndTarget);
GotCapture = true;
it->HasTCapture = true;
}
else {
if (it->HasTCapture) {
HDC hDC = GetDC(hWndTarget);
drawBoxText(hDC, it->TextRect, *it, false);
ReleaseDC(hWndTarget, hDC);
if (!GotCapture)
ReleaseCapture();
it->HasTCapture = false;
}
}
if (it->HasCapture) {
if (GetCapture() != hWndTarget) {
HDC hDC = GetDC(hWndTarget);
drawCloseBox(hDC, it->Close, false);
ReleaseDC(hWndTarget, hDC);
if (!GotCapture) ReleaseCapture();
it->HasCapture = false;
}
else if (!PtInRect(&it->Close, pt)) {
HDC hDC = GetDC(hWndTarget);
drawCloseBox(hDC, it->Close, false);
ReleaseDC(hWndTarget, hDC);
}
else {
HDC hDC = GetDC(hWndTarget);
drawCloseBox(hDC, it->Close, true);
ReleaseDC(hWndTarget, hDC);
}
}
++it;
}
for (size_t k = 0; k < shownStrings.size(); k++) {
TextInfo &ti = *shownStrings[k];
if ((!ti.callBack && !ti.hasEventHandler()) || ti.hasTimer)
continue;
if (PtInRect(&ti.textRect, pt)) {
if (!ti.highlight) {
ti.highlight = true;
InvalidateRect(hWndTarget, &ti.textRect, true);
}
SetCapture(hWndTarget);
GotCapture = true;
ti.hasCapture = true;
SetCursor(LoadCursor(NULL, IDC_HAND));
}
else {
if (ti.highlight) {
ti.highlight = false;
InvalidateRect(hWndTarget, &ti.textRect, true);
}
if (ti.hasCapture) {
if (!GotCapture)
ReleaseCapture();
ti.hasCapture = false;
}
}
}
}
else if (iMessage == WM_LBUTTONDOWN) {
if (autoCompleteInfo) {
autoCompleteInfo.reset();
return 0;
}
list<InfoBox>::iterator it = IBox.begin();
POINT pt;
pt.x = (signed short)LOWORD(lParam);
pt.y = (signed short)HIWORD(lParam);
list<TableInfo>::iterator tit;
if (useTables) {
for (tit = Tables.begin(); tit != Tables.end(); ++tit)
if (tit->table->mouseLeftDown(*this, pt.x, pt.y))
return 0;
}
while (it != IBox.end()) {
if (PtInRect(&it->Close, pt)) {
HDC hDC = GetDC(hWndTarget);
drawCloseBox(hDC, it->Close, true);
ReleaseDC(hWndTarget, hDC);
SetCapture(hWndTarget);
it->HasCapture = true;
}
++it;
}
//Handle links
for (size_t k = 0; k < shownStrings.size(); k++) {
TextInfo &ti = *shownStrings[k];
if (!ti.callBack && !ti.hasEventHandler())
continue;
if (ti.hasCapture) {
HDC hDC = GetDC(hWndTarget);
if (PtInRect(&ti.textRect, pt)) {
ti.active = true;
RenderString(ti, hDC);
}
ReleaseDC(hWndTarget, hDC);
}
}
}
else if (iMessage == WM_LBUTTONUP) {
list<TableInfo>::iterator tit;
list<InfoBox>::iterator it = IBox.begin();
POINT pt;
pt.x = (signed short)LOWORD(lParam);
pt.y = (signed short)HIWORD(lParam);
if (useTables) {
for (tit = Tables.begin(); tit != Tables.end(); ++tit)
if (tit->table->mouseLeftUp(*this, pt.x, pt.y))
return 0;
}
while (it != IBox.end()) {
if (it->HasCapture) {
HDC hDC = GetDC(hWndTarget);
drawCloseBox(hDC, it->Close, false);
ReleaseDC(hWndTarget, hDC);
ReleaseCapture();
it->HasCapture = false;
if (PtInRect(&it->Close, pt)) {
IBox.erase(it);
refresh();
return 0;
}
}
else if (it->HasTCapture) {
ReleaseCapture();
it->HasTCapture = false;
if (PtInRect(&it->TextRect, pt)) {
if (!it->handleEvent(*this, GUI_INFOBOX) && it->callBack)
it->callBack(this, GUI_INFOBOX, &*it); //it may be destroyed here...
return 0;
}
}
++it;
}
//Handle links
for (size_t k = 0; k < shownStrings.size(); k++) {
TextInfo &ti = *shownStrings[k];
if (!ti.callBack && !ti.hasEventHandler())
continue;
if (ti.hasCapture) {
ReleaseCapture();
ti.hasCapture = false;
if (PtInRect(&ti.textRect, pt)) {
if (ti.active) {
string cmd;
if (ti.getExtraInt() != 0)
cmd = "click(\"" + ti.id + "\", " + itos(ti.getExtraInt()) + "); //" + toUTF8(ti.text);
else
cmd = "click(\"" + ti.id + "\"); //" + toUTF8(ti.text);
ti.active = false;
RenderString(ti);
if (!ti.handleEvent(*this, GUI_LINK))
ti.callBack(this, GUI_LINK, &ti);
getRecorder().record(cmd);
return 0;
}
}
}
else if (ti.active) {
ti.active = false;
RenderString(ti);
}
}
}
else if (iMessage == WM_LBUTTONDBLCLK) {
list<TableInfo>::iterator tit;
POINT pt;
pt.x = (signed short)LOWORD(lParam);
pt.y = (signed short)HIWORD(lParam);
if (useTables)
for (tit = Tables.begin(); tit != Tables.end(); ++tit)
if (tit->table->mouseLeftDblClick(*this, pt.x, pt.y))
return 0;
}
else if (iMessage == WM_RBUTTONDOWN) {
POINT pt;
pt.x = (signed short)LOWORD(lParam);
pt.y = (signed short)HIWORD(lParam);
if (useTables) {
for (auto tit = Tables.begin(); tit != Tables.end(); ++tit)
if (tit->table->mouseRightDown(*this, pt.x, pt.y))
return 0;
}
}
else if (iMessage == WM_RBUTTONUP) {
POINT pt;
pt.x = (signed short)LOWORD(lParam);
pt.y = (signed short)HIWORD(lParam);
if (useTables) {
for (auto tit = Tables.begin(); tit != Tables.end(); ++tit)
if (tit->table->mouseRightUp(*this, pt.x, pt.y))
return 0;
}
}
else if (iMessage == WM_MBUTTONDOWN) {
POINT pt;
pt.x = (signed short)LOWORD(lParam);
pt.y = (signed short)HIWORD(lParam);
if (useTables) {
for (auto tit = Tables.begin(); tit != Tables.end(); ++tit)
if (tit->table->mouseMidDown(*this, pt.x, pt.y))
return 0;
}
}
else if (iMessage == WM_MBUTTONUP) {
POINT pt;
pt.x = (signed short)LOWORD(lParam);
pt.y = (signed short)HIWORD(lParam);
if (useTables) {
for (auto tit = Tables.begin(); tit != Tables.end(); ++tit)
if (tit->table->mouseMidUp(*this, pt.x, pt.y))
return 0;
}
}
else if (iMessage == WM_CHAR) {
/*list<TableInfo>::iterator tit;
if (useTables)
for (tit=Tables.begin(); tit!=Tables.end(); ++tit)
if (tit->table->character(*this, int(wParam), lParam & 0xFFFF))
return 0;*/
}
else if (iMessage == WM_CTLCOLOREDIT) {
unordered_map<HWND, InputInfo*>::iterator it = iiByHwnd.find(HWND(lParam));
if (it != iiByHwnd.end()) {
InputInfo &ii = *it->second;
if (ii.bgColor != colorDefault || ii.fgColor != colorDefault) {
if (ii.bgColor != colorDefault) {
SetDCBrushColor(HDC(wParam), ii.bgColor);
SetBkColor(HDC(wParam), ii.bgColor);
}
else {
SetDCBrushColor(HDC(wParam), GetSysColor(COLOR_WINDOW));
SetBkColor(HDC(wParam), GetSysColor(COLOR_WINDOW));
}
if (ii.fgColor != colorDefault)
SetTextColor(HDC(wParam), ii.fgColor);
return LRESULT(GetStockObject(DC_BRUSH));
}
}
return 0;
}
else if (iMessage == WM_DESTROY) {
canClear();// Ignore return value
}
return 0;
}
void gdioutput::TabFocus(int direction)
{
list<TableInfo>::iterator tit;
if (useTables)
for (tit=Tables.begin(); tit!=Tables.end(); ++tit)
if (tit->table->tabFocus(*this, direction))
return;
if (FocusList.empty())
return;
list<HWND>::iterator it=FocusList.begin();
while(it!=FocusList.end() && *it != currentFocus.hWnd)
++it;
//if (*it==CurrentFocus)
if (it!=FocusList.end()) {
if (direction==1){
++it;
if (it==FocusList.end()) it=FocusList.begin();
while(!IsWindowEnabled(*it) && *it != currentFocus.hWnd){
++it;
if (it==FocusList.end()) it=FocusList.begin();
}
}
else{
if (it==FocusList.begin()) it=FocusList.end();
it--;
while(!IsWindowEnabled(*it) && *it != currentFocus.hWnd){
if (it==FocusList.begin()) it=FocusList.end();
it--;
}
}
// if (currentFocus.wasTabbed)
// Button_SetState(currentFocus.hWnd, false);
HWND hWT = *it;
//SetFocus(0);
SetFocus(hWT);
currentFocus = hWT;
//currentFocus = *it;
/*if (biByHwnd.find(currentFocus.hWnd) != biByHwnd.end()) {
currentFocus.wasTabbed = true;
Button_SetState(currentFocus.hWnd, true);
}*/
}
else{
SetFocus(currentFocus.hWnd);
currentFocus=*FocusList.begin();
}
}
bool gdioutput::isInputChanged(const string &exclude)
{
for(list<InputInfo>::iterator it=II.begin(); it != II.end(); ++it) {
if (it->id!=exclude) {
if (it->changed() && !it->ignoreCheck)
return true;
}
}
for (list<ListBoxInfo>::iterator it = LBI.begin(); it != LBI.end(); ++it) {
getSelectedItem(*it);
if (it->changed() && !it->ignoreCheck)
return true;
}
for (list<ButtonInfo>::iterator it = BI.begin(); it != BI.end(); ++it) {
bool checked = SendMessage(it->hWnd, BM_GETCHECK, 0, 0)==BST_CHECKED;
if (it->originalState != checked)
return true;
}
return false;
}
InputInfo *gdioutput::replaceSelection(const char *id, const wstring &text)
{
for(list<InputInfo>::iterator it=II.begin(); it != II.end(); ++it)
if (it->id==id) {
SendMessage(it->hWnd, EM_REPLACESEL, TRUE, LPARAM(text.c_str()));
return &*it;
}
return 0;
}
BaseInfo *gdioutput::setInputFocus(const string &id, bool select)
{
for(list<InputInfo>::iterator it=II.begin(); it != II.end(); ++it)
if (it->id==id) {
scrollTo(it->xp, it->yp);
BaseInfo *bi = SetFocus(it->hWnd)!=NULL ? &*it: 0;
if (bi) {
if (select)
PostMessage(it->hWnd, EM_SETSEL, it->text.length(), 0);
}
return bi;
}
for(list<ListBoxInfo>::iterator it=LBI.begin(); it!=LBI.end();++it)
if (it->id==id) {
scrollTo(it->xp, it->yp);
return SetFocus(it->hWnd)!=NULL ? &*it: 0;
}
for(list<ButtonInfo>::iterator it=BI.begin(); it!=BI.end();++it)
if (it->id==id) {
scrollTo(it->xp, it->yp);
return SetFocus(it->hWnd)!=NULL ? &*it: 0;
}
return 0;
}
InputInfo *gdioutput::getInputFocus()
{
HWND hF=GetFocus();
if (hF) {
list<InputInfo>::iterator it;
for(it=II.begin(); it != II.end(); ++it)
if (it->hWnd==hF)
return &*it;
}
return 0;
}
void gdioutput::enter()
{
if (hasCommandLock())
return;
wstring msg;
try {
doEnter();
}
catch (const meosCancel&) {
return;
}
catch (meosException & ex) {
msg = ex.wwhat();
}
catch(std::exception &ex) {
msg = widen(ex.what());
if (msg.empty())
msg = L"Ett okänt fel inträffade.";
}
catch(...) {
msg = L"Ett okänt fel inträffade.";
}
if (!msg.empty())
alert(msg);
}
void gdioutput::doEnter() {
if (autoCompleteInfo) {
autoCompleteInfo->enter();
return;
}
list<TableInfo>::iterator tit;
if (useTables)
for (tit=Tables.begin(); tit!=Tables.end(); ++tit)
if (tit->table->enter(*this))
return;
HWND hWnd=GetFocus();
for (list<ButtonInfo>::iterator it=BI.begin(); it!=BI.end(); ++it)
if (it->isDefaultButton() && (it->callBack || it->handler)) {
if (it->handler)
it->handleEvent(*this, GUI_BUTTON);
else
it->callBack(this, GUI_BUTTON, &*it);
return;
}
list<InputInfo>::iterator it;
for(it=II.begin(); it != II.end(); ++it)
if (it->hWnd==hWnd && (it->callBack || it->handler)){
TCHAR bf[1024];
GetWindowText(hWnd, bf, 1024);
it->text=bf;
if (it->handler)
it->handleEvent(*this, GUI_INPUT);
else
it->callBack(this, GUI_INPUT, &*it);
return;
}
}
bool gdioutput::upDown(int direction)
{
wstring msg;
try {
return doUpDown(direction);
}
catch (const meosCancel&) {
return false;
}
catch (meosException & ex) {
msg = ex.wwhat();
}
catch(std::exception &ex) {
msg = widen(ex.what());
if (msg.empty())
msg = L"Ett okänt fel inträffade.";
}
catch(...) {
msg = L"Ett okänt fel inträffade.";
}
if (!msg.empty())
alert(msg);
return false;
}
bool gdioutput::doUpDown(int direction)
{
if (autoCompleteInfo) {
autoCompleteInfo->upDown(direction);
return true;
}
list<TableInfo>::iterator tit;
if (useTables)
for (tit=Tables.begin(); tit!=Tables.end(); ++tit)
if (tit->table->upDown(*this, direction))
return true;
return false;
}
void gdioutput::escape()
{
if (hasCommandLock())
return;
wstring msg;
try {
doEscape();
}
catch (const meosCancel&) {
return;
}
catch (meosException & ex) {
msg = ex.wwhat();
}
catch(std::exception &ex) {
msg = widen(ex.what());
if (msg.empty())
msg = L"Ett okänt fel inträffade.";
}
catch(...) {
msg = L"Ett okänt fel inträffade.";
}
if (!msg.empty())
alert(msg);
}
void gdioutput::doEscape()
{
if (fullScreen) {
PostMessage(hWndTarget, WM_CLOSE, 0,0);
}
if (autoCompleteInfo) {
autoCompleteInfo.reset();
return;
}
list<TableInfo>::iterator tit;
if (useTables)
for (tit=Tables.begin(); tit!=Tables.end(); ++tit)
tit->table->escape(*this);
for (list<ButtonInfo>::iterator it=BI.begin(); it!=BI.end(); ++it) {
if (it->isCancelButton() && (it->callBack || it->handler) ) {
if (it->handler)
it->handleEvent(*this, GUI_BUTTON);
else
it->callBack(this, GUI_BUTTON, &*it);
return;
}
}
}
void gdioutput::clearPage(bool autoRefresh, bool keepToolbar) {
maxTextBlockHeight = getLineHeight();
animationData.reset();
lockUpDown = false;
hasAnyTimer = false;
enableTables();
#ifndef MEOSDB
if (toolbar && !keepToolbar)
toolbar->hide();
#endif
while (!timers.empty()) {
KillTimer(hWndTarget, (UINT_PTR)&timers.back());
timers.back().setWnd = 0;
timers.back().parent = 0;
timers.pop_back();
}
restorePoints.clear();
shownStrings.clear();
onClear = 0;
FocusList.clear();
currentFocus = 0;
TL.clear();
itTL = TL.end();
updateImageReferences();
listDescription.clear();
if (hWndTarget && autoRefresh)
InvalidateRect(hWndTarget, NULL, true);
fillDown();
hasCleared = true;
for (ToolList::iterator it = toolTips.begin(); it != toolTips.end(); ++it) {
if (hWndToolTip) {
SendMessage(hWndToolTip, TTM_DELTOOL, 0, (LPARAM)&it->ti);
}
}
toolTips.clear();
{
list<ButtonInfo>::iterator it;
for (it = BI.begin(); it != BI.end(); ++it) {
it->callBack = 0;
it->setHandler(0);
DestroyWindow(it->hWnd);
}
biByHwnd.clear();
BI.clear();
}
{
list<InputInfo>::iterator it;
for (it = II.begin(); it != II.end(); ++it) {
it->callBack = 0;
it->setHandler(0);
DestroyWindow(it->hWnd);
}
iiByHwnd.clear();
II.clear();
}
{
list<ListBoxInfo>::iterator it;
for (it = LBI.begin(); it != LBI.end(); ++it) {
it->callBack = 0;
it->setHandler(0);
DestroyWindow(it->hWnd);
if (it->writeLock)
hasCleared = true;
}
lbiByHwnd.clear();
LBI.clear();
}
while (!Tables.empty()) {
auto t = Tables.front().table;
Tables.pop_front();
t->hide(*this);
}
DataInfo.clear();
FocusList.clear();
Events.clear();
Rectangles.clear();
MaxX = scaleLength(60);
MaxY = scaleLength(100);
CurrentX = scaleLength(40);
CurrentY = scaleLength(START_YP);
SX = CurrentX;
SY = CurrentY;
OffsetX = 0;
OffsetY = 0;
renderOptimize = true;
backgroundColor1 = -1;
backgroundColor2 = -1;
foregroundColor = -1;
backgroundImage = -1;
setRestorePoint();
if (autoRefresh)
updateScrollbars();
try {
if (postClear)
postClear(this, GUI_POSTCLEAR, 0);
}
catch (const meosCancel&) {
}
catch (meosException & ex) {
if (isTestMode)
throw ex;
wstring msg = ex.wwhat();
alert(msg);
}
catch (const std::exception &ex) {
if (isTestMode)
throw ex;
string msg(ex.what());
alert(msg);
}
postClear = nullptr;
manualUpdate = !autoRefresh;
}
void gdioutput::updateImageReferences() {
if (imageReferences.size() > 0) {
imageReferences.clear();
for (auto& ti : TL) {
if ((ti.format & 0xFF) == textImage) {
imageReferences.push_back(&ti);
}
}
}
}
void gdioutput::getWindowText(HWND hWnd, wstring &text)
{
TCHAR bf[1024];
TCHAR *bptr=bf;
int len=GetWindowTextLength(hWnd);
if (len>1023)
bptr=new TCHAR[len+1];
GetWindowText(hWnd, bptr, len+1);
text=bptr;
if (len>1023)
delete[] bptr;
}
BaseInfo& gdioutput::getBaseInfo(const char* id, int requireExtraMatch) const {
for (auto& ii : II) {
if (ii.id == id && ii.matchExtra(requireExtraMatch)) {
return const_cast<InputInfo&>(ii);
}
}
for (auto& lbi : LBI) {
if (lbi.id == id && lbi.matchExtra(requireExtraMatch)) {
return const_cast<ListBoxInfo&>(lbi);
}
}
for (auto& bi : BI) {
if (bi.id == id && bi.matchExtra(requireExtraMatch)) {
return const_cast<ButtonInfo&>(bi);
}
}
for (auto& tl : TL) {
if (tl.id == id && tl.matchExtra(requireExtraMatch)) {
return const_cast<TextInfo&>(tl);
}
}
string err = string("Internal Error, identifier not found: X#") + id;
throw std::exception(err.c_str());
}
const wstring &gdioutput::getText(const char *id, bool acceptMissing, int requireExtraMatch) const {
TCHAR bf[1024];
TCHAR *bptr=bf;
for(list<InputInfo>::const_iterator it=II.begin();
it != II.end(); ++it){
if (it->id==id && it->matchExtra(requireExtraMatch)){
int len=GetWindowTextLength(it->hWnd);
if (len>1023)
bptr=new TCHAR[len+1];
GetWindowText(it->hWnd, bptr, len+1);
const_cast<wstring&>(it->text)=bptr;
if (len>1023)
delete[] bptr;
return it->text;
}
}
for(list<ListBoxInfo>::const_iterator it=LBI.begin();
it != LBI.end(); ++it){
if (it->id==id && it->IsCombo && it->matchExtra(requireExtraMatch)){
if (!it->writeLock) {
GetWindowText(it->hWnd, bf, 1024);
const_cast<wstring&>(it->text)=bf;
}
return it->text;
}
}
for(list<TextInfo>::const_iterator it=TL.begin();
it != TL.end(); ++it){
if (it->id==id && it->matchExtra(requireExtraMatch)) {
return it->text;
}
}
#ifdef _DEBUG
if (!acceptMissing) {
string err = string("Internal Error, identifier not found: X#") + id;
throw std::exception(err.c_str());
}
#endif
return _EmptyWString;
}
bool gdioutput::hasWidget(const string &id) const
{
for(list<InputInfo>::const_iterator it=II.begin();
it != II.end(); ++it){
if (it->id==id)
return true;
}
for(list<ListBoxInfo>::const_iterator it=LBI.begin();
it != LBI.end(); ++it){
if (it->id==id)
return true;
}
for(list<ButtonInfo>::const_iterator it=BI.begin();
it != BI.end(); ++it){
if (it->id==id)
return true;
}
for (auto &tl : TL) {
if (tl.id == id)
return true;
}
return false;
}
int gdioutput::getTextNo(const char *id, bool acceptMissing) const
{
const wstring &t = getText(id, acceptMissing);
return _wtoi(t.c_str());
}
BaseInfo *gdioutput::setTextTranslate(const char *id,
const wstring &text,
bool update) {
return setText(id, lang.tl(text), update);
}
BaseInfo *gdioutput::setTextTranslate(const string &id,
const wstring &text,
bool update) {
return setText(id, lang.tl(text), update);
}
BaseInfo *gdioutput::setTextTranslate(const char *id,
const wchar_t *text,
bool update) {
return setText(id, lang.tl(text), update);
}
BaseInfo *gdioutput::setText(const char *id, int number, bool Update)
{
return setText(id, itow(number), Update);
}
BaseInfo *gdioutput::setTextZeroBlank(const char *id, int number, bool Update)
{
if (number!=0)
return setText(id, number, Update);
else
return setText(id, L"", Update);
}
BaseInfo *gdioutput::setText(const char *id, const wstring &text, bool Update, int requireExtraMatch)
{
for (auto it = II.begin(); it != II.end(); ++it) {
if (it->id == id && it->matchExtra(requireExtraMatch)) {
bool oldWR = it->writeLock;
it->writeLock = true;
SetWindowText(it->hWnd, text.c_str());
it->writeLock = oldWR;
it->text = text;
it->synchData();
it->original = text;
it->focusText = text;
return &*it;
}
}
for (auto it = LBI.begin(); it != LBI.end(); ++it) {
if (it->id == id && it->IsCombo && it->matchExtra(requireExtraMatch)) {
SetWindowText(it->hWnd, text.c_str());
it->text = text;
it->original = text;
return &*it;
}
}
for (auto it = BI.begin(); it != BI.end(); ++it) {
if (it->id == id && it->matchExtra(requireExtraMatch)) {
SetWindowText(it->hWnd, text.c_str());
it->text = text;
return &*it;
}
}
for (auto it = TL.begin(); it != TL.end(); ++it) {
if (it->id == id && it->matchExtra(requireExtraMatch)) {
RECT rc = it->textRect;
it->text = text;
calcStringSize(*it);
rc.right = max(it->textRect.right, rc.right);
rc.bottom = max(it->textRect.bottom, rc.bottom);
bool changed = updatePos(0, 0, it->textRect.right, it->textRect.bottom);
if (Update && hWndTarget) {
if (changed)
InvalidateRect(hWndTarget, 0, true);
else
InvalidateRect(hWndTarget, &rc, true);
}
return &*it;
}
}
return 0;
}
bool gdioutput::insertText(const string &id, const wstring &text)
{
for (list<InputInfo>::iterator it=II.begin();
it != II.end(); ++it) {
if (it->id==id) {
SetWindowText(it->hWnd, text.c_str());
it->text = text;
if (it->handler)
it->handleEvent(*this, GUI_INPUT);
else if (it->callBack)
it->callBack(this, GUI_INPUT, &*it);
return true;
}
}
return false;
}
void gdioutput::setData(const string &id, DWORD data)
{
void *pd = (void *)(size_t(data));
setData(id, pd);
}
void gdioutput::setData(const string &id, void *data)
{
list<DataStore>::iterator it;
for(it=DataInfo.begin(); it != DataInfo.end(); ++it){
if (it->id==id){
it->data = data;
return;
}
}
DataStore ds;
ds.id=id;
ds.data=data;
DataInfo.push_front(ds);
return;
}
bool gdioutput::getData(const string &id, DWORD &data) const
{
list<DataStore>::const_iterator it;
for(it=DataInfo.begin(); it != DataInfo.end(); ++it){
if (it->id==id){
data=DWORD(size_t(it->data));
return true;
}
}
data=0;
return false;
}
void gdioutput::setData(const string &id, const string &data) {
for (auto &it : DataInfo) {
if (it.id == id) {
it.sdata = data;
return;
}
}
DataStore ds;
ds.id = id;
ds.sdata = data;
DataInfo.push_front(ds);
return;
}
bool gdioutput::getData(const string &id, string &out) const {
for (auto &it : DataInfo) {
if (it.id == id) {
out = it.sdata;
return true;
}
}
out.clear();
return false;
}
void *gdioutput::getData(const string &id) const {
list<DataStore>::const_iterator it;
for (it = DataInfo.begin(); it != DataInfo.end(); ++it){
if (it->id == id){
return it->data;
}
}
throw meosException("Data X not found#" + id);
}
bool gdioutput::hasData(const char *id) const {
DWORD dummy;
return getData(id, dummy);
}
bool gdioutput::updatePosTight(int x, int y, int width, int height, int marginx, int marginy) {
int ox = MaxX;
int oy = MaxY;
MaxX = max(x + width, MaxX);
MaxY = max(y + height, MaxY);
bool changed = (ox != MaxX || oy != MaxY);
if (changed && hWndTarget && !manualUpdate) {
RECT rc;
if (ox == MaxX) {
rc.top = oy - CurrentY - 5;
rc.bottom = MaxY - CurrentY + scaleLength(50);
rc.right = 10000;
rc.left = 0;
InvalidateRect(hWndTarget, &rc, true);
}
else {
InvalidateRect(hWndTarget, 0, true);
}
GetClientRect(hWndTarget, &rc);
if (MaxX > rc.right || MaxY > rc.bottom) //Update scrollbars
SendMessage(hWndTarget, WM_SIZE, 0, MAKELONG(rc.right, rc.bottom));
}
if (flowDirection == FlowDirection::Down) {
CurrentY = max(y + height + marginy, CurrentY);
}
else if (flowDirection == FlowDirection::Right) {
CurrentX = max(x + width + marginx, CurrentX);
}
return changed;
}
bool gdioutput::updatePos(int x, int y, int width, int height) {
return updatePosTight(x, y, width, height, 0, 0);
}
void gdioutput::adjustDimension(int width, int height)
{
int ox = MaxX;
int oy = MaxY;
MaxX = width;
MaxY = height;
if ((ox!=MaxX || oy!=MaxY) && hWndTarget && !manualUpdate) {
RECT rc;
if (ox == MaxX) {
rc.top = oy - CurrentY - 5;
rc.bottom = MaxY - CurrentY + scaleLength(50);
rc.right = 10000;
rc.left = 0;
InvalidateRect(hWndTarget, &rc, true);
}
else {
InvalidateRect(hWndTarget, 0, true);
}
GetClientRect(hWndTarget, &rc);
if (MaxX>rc.right || MaxY>rc.bottom) //Update scrollbars
SendMessage(hWndTarget, WM_SIZE, 0, MAKELONG(rc.right, rc.bottom));
}
}
// Alert from main thread (via callback)
void gdioutput::delayAlert(const wstring& msg) {
if (!delayedAlert.empty())
delayedAlert += L", ";
if (delayedAlert.length() > 1000)
delayedAlert = L"";
delayedAlert += lang.tl(msg);
PostMessage(hWndAppMain, WM_USER + 6, 0, LPARAM(this));
}
wstring gdioutput::getDelayedAlert() {
wstring out = L"#" + delayedAlert;
delayedAlert.clear();
return out;
}
void gdioutput::alert(const string &msg) const
{
alert(widen(msg));
}
void gdioutput::alert(const wstring &msg) const
{
if (isTestMode) {
if (!cmdAnswers.empty()) {
string ans = cmdAnswers.front();
cmdAnswers.pop_front();
if (ans == "ok")
return;
}
throw meosException(msg + L"-- ok");
}
HWND hFlt = getToolbarWindow();
if (hasToolbar()) {
EnableWindow(hFlt, false);
}
refreshFast();
SetForegroundWindow(hWndAppMain);
setCommandLock();
try {
MessageBoxW(hWndAppMain, lang.tl(msg).c_str(), L"MeOS", MB_OK|MB_ICONINFORMATION);
if (hasToolbar()) {
EnableWindow(hFlt, true);
}
liftCommandLock();
}
catch (...) {
liftCommandLock();
throw;
}
}
bool gdioutput::ask(const wstring &s)
{
if (isTestMode) {
if (!cmdAnswers.empty()) {
string ans = cmdAnswers.front();
cmdAnswers.pop_front();
if (ans == "yes")
return true;
else if (ans == "no")
return false;
}
throw meosException(s + L"--yes/no");
}
setCommandLock();
SetForegroundWindow(hWndAppMain);
bool yes;
try {
yes = MessageBox(hWndAppMain, lang.tl(s).c_str(), L"MeOS", MB_YESNO|MB_ICONQUESTION)==IDYES;
liftCommandLock();
}
catch (...) {
liftCommandLock();
throw;
}
return yes;
}
gdioutput::AskAnswer gdioutput::askCancel(const wstring &s)
{
if (isTestMode) {
if (!cmdAnswers.empty()) {
string ans = cmdAnswers.front();
cmdAnswers.pop_front();
if (ans == "cancel")
return AskAnswer::AnswerCancel;
else if (ans == "yes")
return AskAnswer::AnswerYes;
else if (ans == "no")
return AskAnswer::AnswerNo;
}
throw meosException(s + L"--yes/no/cancel");
}
setCommandLock();
SetForegroundWindow(hWndAppMain);
int a = MessageBox(hWndAppMain, lang.tl(s).c_str(), L"MeOS", MB_YESNOCANCEL|MB_ICONQUESTION);
liftCommandLock();
if (a == IDYES)
return AskAnswer::AnswerYes;
else if (a == IDNO)
return AskAnswer::AnswerNo;
else
return AskAnswer::AnswerCancel;
}
gdioutput::AskAnswer gdioutput::askOkCancel(const wstring& s)
{
if (isTestMode) {
if (!cmdAnswers.empty()) {
string ans = cmdAnswers.front();
cmdAnswers.pop_front();
if (ans == "cancel")
return AskAnswer::AnswerCancel;
else if (ans == "ok")
return AskAnswer::AnswerOK;
}
throw meosException(s + L"--ok/cancel");
}
setCommandLock();
SetForegroundWindow(hWndAppMain);
int a = MessageBox(hWndAppMain, lang.tl(s).c_str(), L"MeOS", MB_OKCANCEL | MB_ICONINFORMATION);
liftCommandLock();
if (a == IDOK)
return AskAnswer::AnswerOK;
else
return AskAnswer::AnswerCancel;
}
void gdioutput::setTabStops(const string& name, int t1, int t2) {
getInputDimension(0);
double relTextScale = scale / guiMeasure->avgCharWidth;
DWORD ptr[2];
int n = 1;
//LONG bu=GetDialogBaseUnits();
//int baseunitX=LOWORD(bu);
//array[0]=int(t1 * 4.2 * scale) / baseunitX ;
//array[1]=int(t2 * 4.2 * scale) / baseunitX ;
ptr[0] = int(t1 * relTextScale * 6.4 * 4.2 / 8.0);
ptr[1] = int(t2 * relTextScale * 6.4 * 4.2 / 8.0);
int lastTabStop = 0;
if (t2 > 0) {
n = 2;
lastTabStop = t2;
}
else {
lastTabStop = t1;
}
list<ListBoxInfo>::iterator it;
for (it = LBI.begin(); it != LBI.end(); ++it) {
if (it->id == name) {
if (!it->IsCombo) {
SendMessage(it->hWnd, LB_SETTABSTOPS, n, LPARAM(ptr));
it->lastTabStop = lastTabStop;
}
return;
}
}
}
void gdioutput::setInputStatus(const char *id, bool status, bool acceptMissing, int matchExtra) {
bool hit = false;
for(list<InputInfo>::iterator it=II.begin(); it != II.end(); ++it)
if (it->id==id && (matchExtra == -1 || it->getExtraInt() == matchExtra)) {
EnableWindow(it->hWnd, status);
hit = true;
}
for(list<ListBoxInfo>::iterator it=LBI.begin(); it != LBI.end(); ++it)
if (it->id==id && (matchExtra == -1 || it->getExtraInt() == matchExtra)) {
EnableWindow(it->hWnd, status);
hit = true;
}
for(list<ButtonInfo>::iterator it=BI.begin(); it != BI.end(); ++it)
if (it->id==id && (matchExtra == -1 || it->getExtraInt() == matchExtra)) {
EnableWindow(it->hWnd, status);
if (it->isCheckbox) {
string tid = "T" + it->id;
for(list<TextInfo>::iterator tit=TL.begin(); tit != TL.end(); ++tit){
if (tit->id == tid) {
enableCheckBoxLink(*tit, status);
break;
}
}
}
hit = true;
if (status==false) {
it->storedFlags |= it->flags;
it->flags = 0; //Remove default status etc.
}
else {
// Restore flags
it->flags |= it->storedFlags;
}
}
if (acceptMissing)
return;
#ifdef _DEBUG
if (!hit) {
string err = string("Internal Error, identifier not found: X#") + id;
throw std::exception(err.c_str());
}
#endif
}
void gdioutput::refresh() const {
#ifdef DEBUGRENDER
OutputDebugString("### Full refresh\n");
#endif
if (hWndTarget) {
updateScrollbars();
InvalidateRect(hWndTarget, NULL, true);
UpdateWindow(hWndTarget);
}
screenXYToString.clear();
stringToScreenXY.clear();
}
void gdioutput::refreshFast() const {
#ifdef DEBUGRENDER
OutputDebugString("Fast refresh\n");
#endif
if (hWndTarget) {
InvalidateRect(hWndTarget, NULL, true);
UpdateWindow(hWndTarget);
}
screenXYToString.clear();
stringToScreenXY.clear();
}
void gdioutput::takeShownStringsSnapshot() {
#ifdef DEBUGRENDER
OutputDebugString("** Take snapshot\n");
#endif
screenXYToString.clear();
stringToScreenXY.clear();
snapshotMaxXY.first = MaxX;
snapshotMaxXY.second = MaxY - OffsetY;
#ifdef DEBUGRENDER
OutputDebugString(("ymax:" + itos(MaxY-OffsetY) + "\n").c_str());
#endif
for (size_t k = 0; k < shownStrings.size(); k++) {
if (shownStrings[k]->hasTimer)
continue; //Ignore
int x = shownStrings[k]->xp - OffsetX;
int y = shownStrings[k]->yp - OffsetY;
const wstring &str = shownStrings[k]->text;
#ifdef DEBUGRENDER
//OutputDebugString((itos(k) + ":" + itos(shownStrings[k]->xp) + "," + itos(shownStrings[k]->yp) + "," + str + "\n").c_str());
#endif
screenXYToString.insert(make_pair(make_pair(x, y), ScreenStringInfo(shownStrings[k]->textRect, str)));
if (stringToScreenXY.count(str) == 0)
stringToScreenXY.insert(make_pair(str,make_pair(x, y)));
}
RECT rc;
GetClientRect(hWndTarget, &rc);
int BoundYup = OffsetY;
int BoundYdown = OffsetY+rc.bottom;
for (list<RectangleInfo>::iterator it = Rectangles.begin(); it != Rectangles.end(); ++it) {
if (it->rc.top <= BoundYdown && it->rc.bottom >= BoundYup) {
wstring r = L"[R]";
RECT rect_rc = it->rc;
OffsetRect(&rect_rc, -OffsetX, -OffsetY);
screenXYToString.insert(make_pair(make_pair(rect_rc.left, rect_rc.top), ScreenStringInfo(rect_rc, r)));
}
}
}
void updateScrollInfo(HWND hWnd, gdioutput &gdi, int nHeight, int nWidth);
void gdioutput::refreshSmartFromSnapshot(bool allowMoveOffset) {
#ifdef DEBUGRENDER
OutputDebugString("Smart refresh\n");
#endif
RECT clientRC;
GetClientRect(hWndTarget, &clientRC);
updateStringPosCache();
vector<int> changedStrings;
bool updateScroll = false;
if (allowMoveOffset) {
map< pair<int, int>, int> offsetCount;
int misses = 0, hits = 0;
for (size_t k = 0; k < shownStrings.size(); k++) {
if (shownStrings[k]->hasTimer)
continue; //Ignore
int x = shownStrings[k]->xp - OffsetX;
int y = shownStrings[k]->yp - OffsetY;
const wstring &str = shownStrings[k]->text;
map<wstring, pair<int,int> >::const_iterator found = stringToScreenXY.find(str);
if (found != stringToScreenXY.end()) {
hits++;
int ox = found->second.first - x;
int oy = found->second.second - y;
++offsetCount[make_pair(ox, oy)];
if (hits > 30)
break;
}
else {
misses++;
if (misses > 20)
break;
}
}
// Choose dominating offset, if dominating enough
pair<int, int> offset(0,0);
int maxVal = 10; // Require at least 10 hits
for(map< pair<int, int>, int>::iterator it = offsetCount.begin(); it != offsetCount.end(); ++it) {
if (it->second > maxVal) {
maxVal = it->second;
offset = it->first;
}
}
int maxOffsetY=max<int>(getPageY()-clientRC.bottom, 0);
int maxOffsetX=max<int>(getPageX()-clientRC.right, 0);
int noy = OffsetY - offset.second;
int nox = OffsetX - offset.first;
if ((offset.first != 0 && nox>0 && nox<maxOffsetX) || (offset.second != 0 && noy>0 && noy<maxOffsetY) ) {
#ifdef DEBUGRENDER
OutputDebugString(("Change offset: " + itos(offset.first) + "," + itos(offset.second) + "\n").c_str());
#endif
OffsetX -= offset.first;
OffsetY -= offset.second;
autoPos -= offset.second;
if (offset.second != 0) {
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
si.nPos = OffsetY;
SetScrollInfo(hWndTarget, SB_VERT, &si, false);
updateScroll = true;
}
if (offset.first != 0) {
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
si.nPos = OffsetX;
SetScrollInfo(hWndTarget, SB_HORZ, &si, false);
updateScroll = true;
}
updateStringPosCache();
}
}
#ifdef DEBUGRENDER
OutputDebugString(("* ymax:" + itos(MaxY-OffsetY) + "\n").c_str());
#endif
RECT invalidRect;
invalidRect.top = 1000000;
invalidRect.left = 1000000;
invalidRect.right = -1;
invalidRect.bottom = -1;
bool invalid = false;
for (size_t k = 0; k < shownStrings.size(); k++) {
if (shownStrings[k]->hasTimer)
continue; //Ignore
int x = shownStrings[k]->xp - OffsetX;
int y = shownStrings[k]->yp - OffsetY;
const wstring &str = shownStrings[k]->text;
#ifdef DEBUGRENDER
//OutputDebugString((itos(k) + ":" + itos(shownStrings[k]->xp) + "," + itos(shownStrings[k]->yp) + "," + str + "\n").c_str());
#endif
map<pair<int, int>, ScreenStringInfo>::iterator res = screenXYToString.find(make_pair(x,y));
if (res != screenXYToString.end()) {
res->second.reached = true;
if (str != res->second.str) {
if (res->second.rc.bottom >= 0 && res->second.rc.top <= clientRC.bottom) {
changedStrings.push_back(k);
invalidRect.top = min(invalidRect.top, res->second.rc.top);
invalidRect.bottom = max(invalidRect.bottom, res->second.rc.bottom);
invalidRect.left = min(invalidRect.left, res->second.rc.left);
invalidRect.right = max(invalidRect.right, res->second.rc.right);
}
}
}
else
changedStrings.push_back(k);
}
RECT rc;
GetClientRect(hWndTarget, &rc);
int BoundYup = OffsetY;
int BoundYdown = OffsetY+rc.bottom;
for (list<RectangleInfo>::iterator it = Rectangles.begin(); it != Rectangles.end(); ++it) {
if (it->rc.top <= BoundYdown && it->rc.bottom >= BoundYup) {
RECT rect_rc = it->rc;
OffsetRect(&rect_rc, -OffsetX, -OffsetY);
map<pair<int, int>, ScreenStringInfo>::iterator res = screenXYToString.find(make_pair(rect_rc.left, rect_rc.top));
bool add = false;
if (res != screenXYToString.end()) {
res->second.reached = true;
if (!EqualRect(&rect_rc, &res->second.rc)) {
add = true;
invalidRect.top = min(invalidRect.top, res->second.rc.top);
invalidRect.bottom = max(invalidRect.bottom, res->second.rc.bottom);
invalidRect.left = min(invalidRect.left, res->second.rc.left);
invalidRect.right = max(invalidRect.right, res->second.rc.right);
}
}
else
add = true;
if (add) {
invalid = true;
invalidRect.top = min(invalidRect.top, rect_rc.top);
invalidRect.bottom = max(invalidRect.bottom, rect_rc.bottom);
invalidRect.left = min(invalidRect.left, rect_rc.left);
invalidRect.right = max(invalidRect.right, rect_rc.right);
}
}
}
for (map<pair<int, int>, ScreenStringInfo>::iterator it = screenXYToString.begin(); it != screenXYToString.end(); ++it) {
if (!it->second.reached) {
invalid = true;
invalidRect.top = min(invalidRect.top, it->second.rc.top);
invalidRect.bottom = max(invalidRect.bottom, it->second.rc.bottom);
invalidRect.left = min(invalidRect.left, it->second.rc.left);
invalidRect.right = max(invalidRect.right, it->second.rc.right);
}
}
screenXYToString.clear();
stringToScreenXY.clear();
if (snapshotMaxXY.second != MaxY - OffsetY) {
// We added (or removed) a row. Add result to list is typical case.
int currentMaxP = (MaxY - OffsetY);
int oldMaxP = snapshotMaxXY.second;
bool bottomVisible = ((currentMaxP < clientRC.bottom + 15) && currentMaxP > -15) ||
((oldMaxP < clientRC.bottom + 15) && oldMaxP > -15);
if (bottomVisible && !highContrast && oldMaxP != currentMaxP) {
invalid = true;
invalidRect.top = min<int>(invalidRect.top, oldMaxP-15);
invalidRect.top = min<int>(invalidRect.top, currentMaxP-15);
invalidRect.bottom = max<int>(invalidRect.bottom, oldMaxP+15);
invalidRect.bottom = max<int>(invalidRect.bottom, currentMaxP+15);
invalidRect.left = 0;
invalidRect.right = clientRC.right;
#ifdef DEBUGRENDER
OutputDebugString("Extend Y\n");
#endif
}
updateScroll = true;
}
if (snapshotMaxXY.first != MaxX) {
// This almost never happens
invalidRect = clientRC;
invalid = true;
updateScroll;
}
if (updateScroll) {
bool hc = highContrast;
highContrast = false;
updateScrollInfo(hWndTarget, *this, clientRC.bottom, clientRC.right); // No throw
highContrast = hc;
}
if (changedStrings.empty() && !invalid) {
#ifdef DEBUGRENDER
//breakRender = true;
OutputDebugString("*** NO CHANGE\n");
#endif
return;
}
for (size_t k = 0; k< changedStrings.size(); k++) {
TextInfo &ti = *shownStrings[changedStrings[k]];
invalidRect.top = min(invalidRect.top, ti.textRect.top);
invalidRect.bottom = max(invalidRect.bottom, ti.textRect.bottom);
invalidRect.left = min(invalidRect.left, ti.textRect.left);
invalidRect.right = max(invalidRect.right, ti.textRect.right);
}
if (invalidRect.bottom<0 || invalidRect.right < 0
|| invalidRect.top > clientRC.bottom || invalidRect.left > clientRC.right) {
#ifdef DEBUGRENDER
//breakRender = true;
OutputDebugString("*** EMPTY CHANGE\n");
#endif
return;
}
if (hWndTarget) {
//InvalidateRect(hWndTarget, &invalidRect, true);
//UpdateWindow(hWndTarget);
HDC hDC = GetDC(hWndTarget);
IntersectClipRect(hDC, invalidRect.left, invalidRect.top, invalidRect.right, invalidRect.bottom);
//debugDrawColor = RGB((30*counterRender)%256,0,0);
draw(hDC, clientRC, invalidRect);
//debugDrawColor = 0;
ReleaseDC(hWndTarget, hDC);
}
}
void gdioutput::removeString(string id) {
int cnt = 0;
for (auto it = TL.begin(); it != TL.end(); ++it, ++cnt) {
if (it->id == id) {
InvalidateRect(hWndTarget, &it->textRect, true);
TL.erase(it);
itTL = TL.end();
shownStrings.clear();
updateImageReferences();
// Update restorepoints
for (auto& rp : restorePoints) {
if (rp.second.nTL > cnt)
rp.second.nTL--;
}
return;
}
}
}
bool gdioutput::selectFirstItem(const string& id) {
for (auto it = LBI.begin(); it != LBI.end(); ++it)
if (it->id == id) {
bool ret;
if (it->IsCombo)
ret = SendMessage(it->hWnd, CB_SETCURSEL, 0, 0) >= 0;
else
ret = SendMessage(it->hWnd, LB_SETCURSEL, 0, 0) >= 0;
getSelectedItem(*it);
it->original = it->text;
it->originalIdx = it->data;
}
return false;
}
void gdioutput::setWindowTitle(const wstring &title)
{
if (title.length()>0) {
wstring titlew = title + makeDash(L" - MeOS");
SetWindowText(hWndAppMain, titlew.c_str());
}
else SetWindowText(hWndAppMain, L"MeOS");
}
void gdioutput::setWaitCursor(bool wait)
{
if (wait)
SetCursor(LoadCursor(NULL, IDC_WAIT));
else
SetCursor(LoadCursor(NULL, IDC_ARROW));
}
struct FadeInfo
{
TextInfo ti;
DWORD Start;
DWORD End;
HWND hWnd;
COLORREF StartC;
COLORREF EndC;
};
void TextFader(void *f)
{
FadeInfo *fi=(FadeInfo *)f;
HDC hDC=GetDC(fi->hWnd);
SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
SetBkMode(hDC, TRANSPARENT);
double p=0;
double r1=GetRValue(fi->StartC);
double g1=GetGValue(fi->StartC);
double b1=GetBValue(fi->StartC);
double r2=GetRValue(fi->EndC);
double g2=GetGValue(fi->EndC);
double b2=GetBValue(fi->EndC);
while(p<1)
{
p=double(GetTickCount()-fi->Start)/double(fi->End-fi->Start);
if (p>1) p=1;
p=1-(p-1)*(p-1);
int red=int((1-p)*r1+(p)*r2);
int green=int((1-p)*g1+(p)*g2);
int blue=int((1-p)*b1+(p)*b2);
//int green=int((p-1)*GetGValue(fi->StartC)+(p)*GetGValue(fi->EndC));
//int blue=int((p-1)*GetBValue(fi->StartC)+(p)*GetBValue(fi->EndC));
SetTextColor(hDC, RGB(red, green, blue));
TextOut(hDC, fi->ti.xp, fi->ti.yp, fi->ti.text.c_str(), fi->ti.text.length());
Sleep(30);
//char bf[10];
//fi->ti.text=fi->ti.text+itoa(red, bf, 16);
}
ReleaseDC(fi->hWnd, hDC);
delete fi;
}
void gdioutput::fadeOut(string Id, int ms)
{
list<TextInfo>::iterator it;
for(it=TL.begin(); it != TL.end(); ++it){
if (it->id==Id){
FadeInfo *fi=new FadeInfo;
fi->Start=GetTickCount();
fi->End=fi->Start+ms;
fi->ti=*it;
fi->StartC=RGB(0, 0, 0);
fi->EndC=GetSysColor(COLOR_WINDOW);
fi->hWnd=hWndTarget;
_beginthread(TextFader, 0, fi);
TL.erase(it);
return;
}
}
}
void gdioutput::RenderString(TextInfo &ti, HDC hDC) {
if (skipTextRender(ti.format))
return;
if (ti.hasTimer && ti.xp == 0)
return;
HDC hThis=0;
if (!hDC){
assert(hWndTarget!=0);
hDC=hThis=GetDC(hWndTarget);
}
RECT rc;
if ((ti.format & absolutePosition) == 0) {
rc.left = ti.xp - OffsetX;
rc.top = ti.yp - OffsetY;
}
else {
rc.left = ti.xp;
rc.top = ti.yp;
}
rc.right = rc.left;
rc.bottom = rc.top;
formatString(ti, hDC);
int format=ti.format&0xFF;
if (format == textImage) {
// Image
int id = _wtoi(ti.text.c_str());
bool fixedRect = false;
int h = 16, w = 16;
if (id > 0) {
image.loadImage(id, Image::ImageMethod::Default);
w = image.getWidth(id);
h = image.getHeight(id);
image.drawImage(id, Image::ImageMethod::Default, hDC, rc.left, rc.top, w, h);
}
else if (ti.text.size()>1) {
if (ti.text[0] == 'S') { // Icon
w = _wtoi(ti.text.c_str() + 1);
h = getLineHeight();
}
else if (ti.text[0] == 'L') {
fixedRect = true;
uint64_t imgId = _wcstoui64(ti.text.c_str() + 1, nullptr, 10);
w = ti.textRect.right - ti.textRect.left;
h = ti.textRect.bottom - ti.textRect.top;
image.drawImage(imgId, Image::ImageMethod::Default, hDC, rc.left, rc.top, w, h);
//width = image.getWidth(imgId);
//height = image.getHeight(imgId);
}
}
if (!fixedRect) {
ti.textRect.left = rc.left;
ti.textRect.right = rc.left + w + 5;
ti.textRect.top = rc.top;
ti.textRect.bottom = rc.bottom + h + 5;
}
}
else if (format != 10 && (breakLines&ti.format) == 0) {
if (ti.xlimit == 0) {
if (ti.format&textRight) {
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_CALCRECT | DT_NOPREFIX);
int dx = rc.right - rc.left;
ti.realWidth = dx;
rc.right -= dx;
rc.left -= dx;
ti.textRect = rc;
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_RIGHT | DT_NOCLIP | DT_NOPREFIX);
}
else if (ti.format&textCenter) {
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_CENTER | DT_CALCRECT | DT_NOPREFIX);
int dx = rc.right - rc.left;
ti.realWidth = dx;
rc.right -= dx / 2;
rc.left -= dx / 2;
ti.textRect = rc;
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_CENTER | DT_NOCLIP | DT_NOPREFIX);
}
else {
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_LEFT | DT_CALCRECT | DT_NOPREFIX);
ti.textRect = rc;
ti.realWidth = rc.right - rc.left;
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_LEFT | DT_NOCLIP | DT_NOPREFIX);
}
}
else {
int flags = DT_NOPREFIX;
if (ti.format & textLimitEllipsis)
flags = DT_END_ELLIPSIS;
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_CALCRECT | flags);
ti.realWidth = rc.right - rc.left;
if (ti.format&textRight) {
rc.right = rc.left + ti.xlimit - (rc.bottom - rc.top) / 2;
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_RIGHT | flags);
}
else if (ti.format&textCenter) {
rc.right = rc.left + ti.xlimit - (rc.bottom - rc.top) / 2;
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_CENTER | flags);
}
else {
rc.right = rc.left + ti.xlimit;
DrawText(hDC, ti.text.c_str(), -1, &rc, DT_LEFT | flags);
}
ti.textRect = rc;
}
}
else {
memset(&rc, 0, sizeof(rc));
int width = scaleLength( (breakLines&ti.format) ? ti.xlimit : 450 );
rc.right = width;
int dx = (breakLines&ti.format) ? 0 : scaleLength(20);
ti.realWidth = width + dx;
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_CALCRECT|DT_LEFT|DT_NOPREFIX|DT_WORDBREAK);
ti.textRect=rc;
ti.textRect.right+=ti.xp+dx;
ti.textRect.left+=ti.xp;
ti.textRect.top+=ti.yp;
ti.textRect.bottom+=ti.yp+dx;
if (ti.format == 10) {
DWORD c = colorLightYellow;// GetSysColor(COLOR_INFOBK);
double red=GetRValue(c);
double green=GetGValue(c);
double blue=GetBValue(c);
double blue1=min(255., blue*1.05);
double green1=min(255., green*1.05);
double red1=min(255., red*1.05);
TRIVERTEX vert[2];
vert [0] .x = ti.xp-OffsetX;
vert [0] .y = ti.yp-OffsetY;
vert [0] .Red = 0xff00&DWORD(red1*256);
vert [0] .Green = 0xff00&DWORD(green1*256);
vert [0] .Blue = 0xff00&DWORD(blue1*256);
vert [0] .Alpha = 0x0000;
vert [1] .x = ti.xp+rc.right+dx-OffsetX;
vert [1] .y = ti.yp+rc.bottom+dx-OffsetY;
vert [1] .Red = 0xff00&DWORD(red*256);
vert [1] .Green = 0xff00&DWORD(green*256);
vert [1] .Blue = 0xff00&DWORD(blue*256);
vert [1] .Alpha = 0x0000;
GRADIENT_RECT gr[1];
gr[0].UpperLeft=0;
gr[0].LowerRight=1;
GradientFill(hDC,vert, 2, gr, 1,GRADIENT_FILL_RECT_H);
SelectObject(hDC, GetStockObject(NULL_BRUSH));
SelectObject(hDC, GetStockObject(DC_PEN));
SetDCPenColor(hDC, RGB(DWORD(red*0.5),
DWORD(green*0.5),
DWORD(blue*0.5)));
Rectangle(hDC, vert[0].x, vert[0].y, vert[1].x, vert[1].y);
SetDCPenColor(hDC, RGB(DWORD(min(255., red*1.1)),
DWORD(min(255., green*1.2)),
DWORD(min(255., blue))));
POINT pt;
MoveToEx(hDC, vert[0].x-1, vert[1].y, &pt);
LineTo(hDC, vert[0].x-1, vert[0].y-1);
LineTo(hDC, vert[1].x, vert[0].y-1);
SetDCPenColor(hDC, RGB(DWORD(min(255., red*0.4)),
DWORD(min(255., green*0.4)),
DWORD(min(255., blue*0.4))));
MoveToEx(hDC, vert[1].x+0, vert[0].y, &pt);
LineTo(hDC, vert[1].x+0, vert[1].y+0);
LineTo(hDC, vert[0].x, vert[1].y+0);
}
dx/=2;
rc.top=ti.yp+dx-OffsetY;
rc.left=ti.xp+dx-OffsetX;
rc.bottom+=ti.yp+dx-OffsetY;
rc.right=ti.xp+dx+width-OffsetX;
SetTextColor(hDC, 0);
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_LEFT|DT_NOPREFIX|DT_WORDBREAK);
}
if (hThis)
ReleaseDC(hWndTarget, hDC);
}
void gdioutput::RenderString(TextInfo &ti, const wstring &text, HDC hDC)
{
if (skipTextRender(ti.format))
return;
RECT rc;
if ((ti.format & absolutePosition) == 0) {
rc.left = ti.xp - OffsetX;
rc.top = ti.yp - OffsetY;
}
else {
rc.left = ti.xp;
rc.top = ti.yp;
}
rc.right = rc.left;
rc.bottom = rc.top;
int format=ti.format&0xFF;
assert(format!=10);
formatString(ti, hDC);
if (ti.xlimit==0){
if (ti.format&textRight) {
DrawText(hDC, text.c_str(), text.length(), &rc, DT_CALCRECT|DT_NOPREFIX);
int dx=rc.right-rc.left;
rc.right-=dx;
rc.left-=dx;
ti.textRect=rc;
DrawText(hDC, text.c_str(), text.length(), &rc, DT_RIGHT|DT_NOCLIP|DT_NOPREFIX);
}
else if (ti.format&textCenter) {
DrawText(hDC, text.c_str(), text.length(), &rc, DT_CENTER|DT_CALCRECT|DT_NOPREFIX);
int dx=rc.right-rc.left;
rc.right-=dx/2;
rc.left-=dx/2;
ti.textRect=rc;
DrawText(hDC, text.c_str(), text.length(), &rc, DT_CENTER|DT_NOCLIP|DT_NOPREFIX);
}
else{
DrawText(hDC, text.c_str(), text.length(), &rc, DT_LEFT|DT_CALCRECT|DT_NOPREFIX);
ti.textRect=rc;
DrawText(hDC, text.c_str(), text.length(), &rc, DT_LEFT|DT_NOCLIP|DT_NOPREFIX);
}
}
else{
if (ti.format&textRight) {
DrawText(hDC, text.c_str(), text.length(), &rc, DT_LEFT|DT_CALCRECT|DT_NOPREFIX);
rc.right = rc.left + ti.xlimit;
ti.textRect = rc;
DrawText(hDC, text.c_str(), text.length(), &rc, DT_RIGHT|DT_NOPREFIX);
}
else {
DrawText(hDC, text.c_str(), text.length(), &rc, DT_LEFT|DT_CALCRECT|DT_NOPREFIX);
rc.right=rc.left+ti.xlimit;
DrawText(hDC, text.c_str(), text.length(), &rc, DT_LEFT|DT_NOPREFIX);
ti.textRect=rc;
}
}
}
void gdioutput::resetLast() const {
lastFormet = -1;
lastActive = false;
lastHighlight = false;
lastColor = -1;
lastFont.clear();
}
void gdioutput::getFontInfo(const TextInfo &ti, FontInfo &fi) const {
if (ti.font.empty()) {
fi.name = 0;
fi.bold = fi.normal = fi.italic = 0;
}
else {
fi.name = &ti.font;
getFont(ti.font).getInfo(fi);
}
}
void gdioutput::formatString(const TextInfo &ti, HDC hDC) const
{
int format=ti.format&0xFF;
if (lastFormet == format &&
lastActive == ti.active &&
lastHighlight == ti.highlight &&
lastColor == ti.color &&
ti.font == lastFont)
return;
if (ti.font.empty()) {
getCurrentFont().selectFont(hDC, format);
lastFont.clear();
}
else {
getFont(ti.font).selectFont(hDC, format);
lastFont = ti.font;
}
SetBkMode(hDC, TRANSPARENT);
if (ti.active)
SetTextColor(hDC, RGB(255,0,0));
else if (ti.highlight)
SetTextColor(hDC, RGB(64,64,128));
else if (ti.color == 0 && foregroundColor != -1)
SetTextColor(hDC, foregroundColor);
else
SetTextColor(hDC, ti.color);
}
void gdioutput::calcStringSize(TextInfo &ti, HDC hDC_in) const {
RECT rc;
rc.left=ti.xp-OffsetX;
rc.top=ti.yp-OffsetY;
rc.right = rc.left;
rc.bottom = rc.top;
if ((ti.format & 0xFF) == textImage) {
// Image
int id = _wtoi(ti.text.c_str());
int w = 16, h = 16;
if (id > 0) {
w = image.getWidth(id);
h = image.getHeight(id);
}
else if (ti.text.size()>1 && ti.text[0] == 'S') { // Icon
w = _wtoi(ti.text.c_str() + 1);
h = getLineHeight();
}
else if (ti.text[0] == 'L') {
return;
}
ti.textRect.left = rc.left;
ti.textRect.right = rc.left + w + 5;
ti.textRect.top = rc.top;
ti.textRect.bottom = rc.bottom + h + 5;
return;
}
HDC hDC = hDC_in;
if (!hDC) {
// assert(hWndTarget!=0);
hDC = GetDC(hWndTarget);
}
resetLast();
formatString(ti, hDC);
int format=ti.format&0xFF;
if (format != 10 && (breakLines&ti.format) == 0) {
if (ti.xlimit==0){
if (ti.format&textRight) {
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_CALCRECT|DT_NOPREFIX);
int dx=rc.right-rc.left;
ti.realWidth = dx;
rc.right-=dx;
rc.left-=dx;
ti.textRect=rc;
}
else if (ti.format&textCenter) {
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_CENTER|DT_CALCRECT|DT_NOPREFIX);
int dx=rc.right-rc.left;
ti.realWidth = dx;
rc.right-=dx/2;
rc.left-=dx/2;
ti.textRect=rc;
}
else{
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_LEFT|DT_CALCRECT|DT_NOPREFIX);
ti.realWidth = rc.right - rc.left;
ti.textRect=rc;
}
}
else {
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_LEFT|DT_CALCRECT|DT_NOPREFIX);
ti.realWidth = rc.right - rc.left;
rc.right=rc.left+ti.xlimit;
ti.textRect=rc;
}
}
else {
memset(&rc, 0, sizeof(rc));
rc.right = scaleLength( (breakLines&ti.format) ? ti.xlimit : 450 );
int dx = (breakLines&ti.format) ? 0 : scaleLength(20);
ti.realWidth = rc.right + dx;
DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_CALCRECT|DT_LEFT|DT_NOPREFIX|DT_WORDBREAK);
ti.textRect=rc;
ti.textRect.right+=ti.xp+dx;
ti.textRect.left+=ti.xp;
ti.textRect.top+=ti.yp;
ti.textRect.bottom+=ti.yp+dx;
}
if (!hDC_in)
ReleaseDC(hWndTarget, hDC);
}
void gdioutput::updateScrollbars() const
{
RECT rc;
GetClientRect(hWndTarget, &rc);
SendMessage(hWndTarget, WM_SIZE, 0, MAKELONG(rc.right, rc.bottom));
}
void gdioutput::updateObjectPositions()
{
{
list<ButtonInfo>::iterator it;
for(it=BI.begin(); it != BI.end(); ++it)
{
//MoveWindow(it->hWnd, it->
if (!it->AbsPos)
SetWindowPos(it->hWnd, 0, it->xp-OffsetX, it->yp-OffsetY, 0,0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOCOPYBITS);
}
}
{
list<InputInfo>::iterator it;
for(it=II.begin(); it != II.end(); ++it)
SetWindowPos(it->hWnd, 0, it->xp-OffsetX, it->yp-OffsetY, 0,0, SWP_NOSIZE|SWP_NOZORDER);
}
{
list<ListBoxInfo>::iterator it;
for(it=LBI.begin(); it != LBI.end(); ++it)
SetWindowPos(it->hWnd, 0, it->xp-OffsetX, it->yp-OffsetY, 0,0, SWP_NOSIZE|SWP_NOZORDER);
}
}
void gdioutput::addInfoBox(string id, wstring text, int TimeOut, GUICALLBACK cb)
{
InfoBox Box;
Box.id=id;
Box.callBack=cb;
Box.text=lang.tl(text);
if (TimeOut>0)
Box.TimeOut=GetTickCount()+TimeOut;
IBox.push_back(Box);
refresh();
}
void gdioutput::drawBox(HDC hDC, InfoBox &Box, RECT &pos)
{
SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
SetBkMode(hDC, TRANSPARENT);
//Calculate size.
RECT testrect;
memset(&testrect, 0, sizeof(RECT));
DrawText(hDC, Box.text.c_str(), Box.text.length(), &testrect, DT_CALCRECT|DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
if (testrect.right>250 || Box.text.find_first_of('\n')!=string::npos)
{
testrect.right=250;
DrawText(hDC, Box.text.c_str(), Box.text.length(), &testrect, DT_CALCRECT|DT_LEFT|DT_NOPREFIX|DT_WORDBREAK);
}
else if (testrect.right<80)
testrect.right=80;
pos.left=pos.right-(testrect.right+22);
pos.top=pos.bottom-(testrect.bottom+20);
DWORD c=GetSysColor(COLOR_INFOBK);
double red=GetRValue(c);
double green=GetGValue(c);
double blue=GetBValue(c);
double blue1=min(255., blue*1.1);
double green1=min(255., green*1.1);
double red1=min(255., red*1.1);
TRIVERTEX vert[2];
vert [0] .x = pos.left;
vert [0] .y = pos.top;
vert [0] .Red = 0xff00&DWORD(red*256);
vert [0] .Green = 0xff00&DWORD(green*256);
vert [0] .Blue = 0xff00&DWORD(blue*256);
vert [0] .Alpha = 0x0000;
vert [1] .x = pos.right;
vert [1] .y = pos.bottom;
vert [1] .Red = 0xff00&DWORD(red1*256);
vert [1] .Green = 0xff00&DWORD(green1*256);
vert [1] .Blue = 0xff00&DWORD(blue1*256);
vert [1] .Alpha = 0x0000;
GRADIENT_RECT gr[1];
gr[0].UpperLeft=0;
gr[0].LowerRight=1;
//if (MaxY>500)
GradientFill(hDC,vert, 2, gr, 1,GRADIENT_FILL_RECT_V);
SelectObject(hDC, GetStockObject(NULL_BRUSH));
SelectObject(hDC, GetStockObject(BLACK_PEN));
Rectangle(hDC, pos.left, pos.top, pos.right, pos.bottom);
Box.BoundingBox=pos;
//Close Box
RECT Close;
Close.top=pos.top+3;
Close.bottom=Close.top+11;
Close.right=pos.right-3;
Close.left=Close.right-11;
Box.Close=Close;
drawCloseBox(hDC, Close, false);
RECT tr=pos;
tr.left+=10;
tr.right-=10;
tr.top+=15;
tr.bottom-=5;
drawBoxText(hDC, tr, Box, false);
Box.TextRect=tr;
}
void gdioutput::drawBoxes(HDC hDC, RECT &rc)
{
RECT pos;
pos.right=rc.right;
pos.bottom=rc.bottom;
list<InfoBox>::iterator it=IBox.begin();
int maxNumBox = 10;
while(it!=IBox.end() && --maxNumBox > 0) {
drawBox(hDC, *it, pos);
pos.bottom=pos.top;
++it;
}
}
void gdioutput::drawCloseBox(HDC hDC, RECT &Close, bool pressed)
{
if (!pressed) {
SelectObject(hDC, GetStockObject(WHITE_BRUSH));
SelectObject(hDC, GetStockObject(BLACK_PEN));
}
else {
SelectObject(hDC, GetStockObject(LTGRAY_BRUSH));
SelectObject(hDC, GetStockObject(BLACK_PEN));
}
//Close Box
Rectangle(hDC, Close.left, Close.top, Close.right, Close.bottom);
MoveToEx(hDC, Close.left+2, Close.top+2, 0);
LineTo(hDC, Close.right-2, Close.bottom-2);
MoveToEx(hDC, Close.right-2, Close.top+2, 0);
LineTo(hDC, Close.left+2, Close.bottom-2);
}
void gdioutput::drawBoxText(HDC hDC, RECT &tr, InfoBox &Box, bool highligh)
{
SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
SetBkMode(hDC, TRANSPARENT);
if (highligh) {
SetTextColor(hDC, 0x005050FF);
}
else {
SetTextColor(hDC, GetSysColor(COLOR_INFOTEXT));
}
DrawText(hDC, Box.text.c_str(), Box.text.length(), &tr, DT_LEFT|DT_NOPREFIX|DT_WORDBREAK);
}
bool gdioutput::RemoveFirstInfoBox(const string &id)
{
list<InfoBox>::iterator it=IBox.begin();
while(it!=IBox.end()) {
if (it->id==id) {
IBox.erase(it);
return true;
}
++it;
}
return false;
}
wstring gdioutput::getTimerText(int zeroTime, int format, bool timeInSeconds) {
TextInfo temp;
temp.zeroTime=0;
//memset(&temp, 0, sizeof(TextInfo));
temp.format=format;
if (timeInSeconds)
return getTimerText(&temp, 1000*zeroTime);
else
return getTimerText(&temp, (1000/timeUnitsPerSecond) * zeroTime);
}
wstring gdioutput::getTimerText(TextInfo *tit, DWORD T)
{
int rt=(int(T)-int(tit->zeroTime))/1000;
int tenth = (abs(int(T)-int(tit->zeroTime))/100)%10;
wstring text;
int t=abs(rt);
wchar_t bf[16];
if (tit->format & timeSeconds) {
if (tit->format & timeWithTenth)
swprintf_s(bf, 16, L"%d.%d", t, tenth);
else
swprintf_s(bf, 16, L"%d", t);
}
else if ((tit->format & timeWithTenth) && rt < timeConstSecPerHour) {
swprintf_s(bf, 16, L"%02d:%02d.%d", t/ timeConstSecPerMin, t%timeConstSecPerMin, tenth);
}
else if (rt>=timeConstSecPerHour || (tit->format&fullTimeHMS))
swprintf_s(bf, 16, L"%02d:%02d:%02d", t/ timeConstSecPerHour, (t/ timeConstSecPerMin)% timeConstSecPerMin, t%timeConstSecPerMin);
else
swprintf_s(bf, 16, L"%d:%02d", (t/ timeConstMinPerHour), t%timeConstMinPerHour);
if (rt>0 || ((tit->format&fullTimeHMS) && rt>=0) )
if (tit->format&timerCanBeNegative)
text = wstring(L"+") + bf;
else
text = bf;
else if (rt<0)
if (tit->format&timerCanBeNegative)
text = wstring(L"-")+bf;
else if (tit->format&timerIgnoreSign)
text = bf;
else
text = L"-";
return text;
}
void gdioutput::CheckInterfaceTimeouts(DWORD T)
{
list<InfoBox>::iterator it=IBox.begin();
while (it!=IBox.end()) {
if (it->TimeOut && it->TimeOut<T) {
if (it->HasCapture || it->HasTCapture)
ReleaseCapture();
InvalidateRect(hWndTarget, &(it->BoundingBox), true);
IBox.erase(it);
it=IBox.begin();
}
else ++it;
}
list<TextInfo>::iterator tit = TL.begin();
vector<TextInfo> timeout;
if (hasAnyTimer) {
bool anyChange = false;
while(tit!=TL.end()){
if (tit->hasTimer){
wstring text = tit->xp > 0 ? getTimerText(&*tit, T) : L"";
if (tit->timeOut && T>DWORD(tit->timeOut)){
tit->timeOut=0;
if (tit->callBack || tit->hasEventHandler())
timeout.push_back(*tit);
}
if (text != tit->text) {
RECT rc=tit->textRect;
tit->text=text;
calcStringSize(*tit);
rc.right=max(tit->textRect.right, rc.right);
rc.bottom=max(tit->textRect.bottom, rc.bottom);
anyChange = true;
//InvalidateRecthWndTarget, &rc, true);
}
}
++tit;
}
if (anyChange) {
int w, h;
getTargetDimension(w, h);
HDC hDC = GetDC(hWndTarget);
HBITMAP btm = CreateCompatibleBitmap(hDC, w, h);
HDC memDC = CreateCompatibleDC (hDC);
HGDIOBJ hOld = SelectObject(memDC, btm);
RECT rc;
rc.top = 0;
rc.left = 0;
rc.bottom = h;
rc.right = w;
RECT area = rc;
drawBackground(memDC, rc);
draw(memDC, rc, area);
BitBlt(hDC, 0, 0, w, h, memDC, 0,0, SRCCOPY);
SelectObject(memDC, hOld);
DeleteObject(btm);
DeleteDC(memDC);
ReleaseDC(hWndTarget, hDC);
}
}
for (size_t k = 0; k < timeout.size(); k++) {
if (!timeout[k].handleEvent(*this, GUI_TIMEOUT))
timeout[k].callBack(this, GUI_TIMEOUT, &timeout[k]);
}
}
bool gdioutput::removeWidget(const string &id)
{
{
auto it = BI.begin();
int cnt = 0;
while (it != BI.end()) {
if (it->id == id) {
DestroyWindow(it->hWnd);
biByHwnd.erase(it->hWnd);
if (it->isCheckbox)
removeString("T" + id);
BI.erase(it);
// Update restorepoints
for (auto& rp : restorePoints) {
if (rp.second.nBI > cnt)
rp.second.nBI--;
}
return true;
}
++it;
++cnt;
}
}
{
auto lit = LBI.begin();
int cnt = 0;
while (lit != LBI.end()) {
if (lit->id == id) {
DestroyWindow(lit->hWnd);
lbiByHwnd.erase(lit->hWnd);
removeString(id + "_label");
if (lit->writeLock)
hasCleared = true;
LBI.erase(lit);
// Update restorepoints
for (auto& rp : restorePoints) {
if (rp.second.nLBI > cnt)
rp.second.nLBI--;
}
return true;
}
++lit;
cnt++;
}
}
{
auto iit = II.begin();
int cnt = 0;
while (iit != II.end()) {
if (iit->id == id) {
DestroyWindow(iit->hWnd);
iiByHwnd.erase(iit->hWnd);
II.erase(iit);
removeString(id + "_label");
// Update restorepoints
for (auto& rp : restorePoints) {
if (rp.second.nII > cnt)
rp.second.nII--;
}
return true;
}
++iit;
cnt++;
}
}
removeString(id);
return false;
}
bool gdioutput::hideWidget(const string &id, bool hide) {
list<ButtonInfo>::iterator it=BI.begin();
while (it!=BI.end()) {
if (it->id == id) {
ShowWindow(it->hWnd, hide ? SW_HIDE : SW_SHOW);
if (it->isCheckbox) {
hideWidget("T" + id, hide);
}
return true;
}
++it;
}
list<ListBoxInfo>::iterator lit=LBI.begin();
while (lit!=LBI.end()) {
if (lit->id==id) {
ShowWindow(lit->hWnd, hide ? SW_HIDE : SW_SHOW);
return true;
}
++lit;
}
list<InputInfo>::iterator iit=II.begin();
while (iit!=II.end()) {
if (iit->id==id) {
ShowWindow(iit->hWnd, hide ? SW_HIDE : SW_SHOW);
return true;
}
++iit;
}
for (auto &ti : TL) {
if (ti.id == id) {
if (hide)
ti.format |= hiddenText;
else
ti.format &= ~hiddenText;
return true;
}
}
return false;
}
void gdioutput::setRestorePoint() {
setRestorePoint("");
}
void gdioutput::setRestorePoint(const string &id) {
RestoreInfo ri;
ri.id = id;
ri.nLBI = LBI.size();
ri.nBI = BI.size();
ri.nII = II.size();
ri.nTL = TL.size();
ri.nRect = Rectangles.size();
ri.nTooltip = toolTips.size();
ri.nTables = Tables.size();
ri.nHWND = FocusList.size();
ri.nData = DataInfo.size();
for (auto& rp : restorePoints)
ri.restorePoints.insert(rp.first);
ri.sCX=CurrentX;
ri.sCY=CurrentY;
ri.sMX=MaxX;
ri.sMY=MaxY;
ri.sOX=OffsetX;
ri.sOY=OffsetY;
ri.onClear = onClear;
ri.postClear = postClear;
restorePoints[id]=ri;
}
bool gdioutput::getWidgetRestorePoint(const string& id, string& restorePoint) const {
int count;
count = 0;
for (auto& lbi : LBI) {
if (lbi.id == id) {
const RestoreInfo* bestRestoreInfo = nullptr;
for (auto& rp : restorePoints) {
if (count <= rp.second.nLBI && (bestRestoreInfo == nullptr || rp.second < *bestRestoreInfo)) {
bestRestoreInfo = &rp.second;
restorePoint = rp.first;
}
}
return bestRestoreInfo != nullptr;
}
count++;
}
count = 0;
for (auto& ii : II) {
if (ii.id == id) {
const RestoreInfo* bestRestoreInfo = nullptr;
for (auto& rp : restorePoints) {
if (count <= rp.second.nII && (bestRestoreInfo == nullptr || rp.second < *bestRestoreInfo)) {
bestRestoreInfo = &rp.second;
restorePoint = rp.first;
}
}
return bestRestoreInfo != nullptr;
}
count++;
}
count = 0;
for (auto& bi : BI) {
if (bi.id == id) {
const RestoreInfo* bestRestoreInfo = nullptr;
for (auto& rp : restorePoints) {
if (count <= rp.second.nBI && (bestRestoreInfo == nullptr || rp.second < *bestRestoreInfo)) {
bestRestoreInfo = &rp.second;
restorePoint = rp.first;
}
}
return bestRestoreInfo != nullptr;
}
count++;
}
count = 0;
for (auto& ti : TL) {
if (ti.id == id) {
const RestoreInfo* bestRestoreInfo = nullptr;
for (auto& rp : restorePoints) {
if (count <= rp.second.nTL && (bestRestoreInfo == nullptr || rp.second < *bestRestoreInfo)) {
bestRestoreInfo = &rp.second;
restorePoint = rp.first;
}
}
return bestRestoreInfo != nullptr;
}
count++;
}
return false;
}
void gdioutput::setWidgetRestorePoint(const string& id, const string& restorePoint) {
for (auto it = LBI.begin(); it != LBI.end(); ++it) {
if (it->id == id) {
auto& rpTarget = restorePoints[restorePoint];
auto itNew = LBI.begin();
int numNew = rpTarget.nLBI;
advance(itNew, numNew);
LBI.splice(itNew, LBI, it);
for (auto& rp : restorePoints) {
if (rp.second.nLBI >= numNew) {
rp.second.nLBI++;
}
}
return;
}
}
for (auto it = II.begin(); it != II.end(); ++it) {
if (it->id == id) {
auto& rpTarget = restorePoints[restorePoint];
auto itNew = II.begin();
int numNew = rpTarget.nII;
advance(itNew, numNew);
II.splice(itNew, II, it);
for (auto& rp : restorePoints) {
if (rp.second.nII >= numNew) {
rp.second.nII++;
}
}
return;
}
}
for (auto it = BI.begin(); it != BI.end(); ++it) {
if (it->id == id) {
auto& rpTarget = restorePoints[restorePoint];
auto itNew = BI.begin();
int numNew = rpTarget.nBI;
advance(itNew, numNew);
BI.splice(itNew, BI, it);
for (auto& rp : restorePoints) {
if (rp.second.nBI >= numNew) {
rp.second.nBI++;
}
}
return;
}
}
for (auto it = TL.begin(); it != TL.end(); ++it) {
if (it->id == id) {
auto& rpTarget = restorePoints[restorePoint];
auto itNew = TL.begin();
int numNew = rpTarget.nTL;
advance(itNew, numNew);
TL.splice(itNew, TL, it);
for (auto& rp : restorePoints) {
if (rp.second.nTL >= numNew) {
rp.second.nTL++;
}
}
return;
}
}
}
void gdioutput::restoreInternal(const RestoreInfo &ri)
{
int toolRemove=toolTips.size()-ri.nTooltip;
while (toolRemove>0 && toolTips.size()>0) {
ToolInfo &info=toolTips.back();
if (hWndToolTip) {
SendMessage(hWndToolTip, TTM_DELTOOL, 0, (LPARAM) &info.ti);
}
toolTips.pop_back();
toolRemove--;
}
int lbiRemove=LBI.size()-ri.nLBI;
while (lbiRemove>0 && LBI.size()>0) {
ListBoxInfo &lbi=LBI.back();
lbi.callBack = nullptr; // Avoid kill focus event here
lbi.clearHandler();
DestroyWindow(lbi.hWnd);
if (lbi.writeLock)
hasCleared = true;
lbiByHwnd.erase(lbi.hWnd);
LBI.pop_back();
lbiRemove--;
}
int tlRemove=TL.size()-ri.nTL;
while (tlRemove>0 && TL.size()>0) {
TL.pop_back();
tlRemove--;
}
itTL=TL.begin();
updateImageReferences();
// Clear cache of shown strings
shownStrings.clear();
int biRemove=BI.size()-ri.nBI;
while (biRemove>0 && BI.size()>0) {
ButtonInfo &bi=BI.back();
bi.callBack = nullptr;
bi.clearHandler();
DestroyWindow(bi.hWnd);
biByHwnd.erase(bi.hWnd);
BI.pop_back();
biRemove--;
}
int iiRemove=II.size()-ri.nII;
while (iiRemove>0 && II.size()>0) {
InputInfo &ii=II.back();
ii.callBack = nullptr; // Avoid kill focus event here
ii.clearHandler();
DestroyWindow(ii.hWnd);
iiByHwnd.erase(ii.hWnd);
II.pop_back();
iiRemove--;
}
int rectRemove=Rectangles.size()-ri.nRect;
while (rectRemove>0 && Rectangles.size()>0) {
Rectangles.pop_back();
rectRemove--;
}
int hwndRemove=FocusList.size()-ri.nHWND;
while(hwndRemove>0 && FocusList.size()>0) {
FocusList.pop_back();
hwndRemove--;
}
while(Tables.size() > unsigned(ri.nTables)){
auto t=Tables.back().table;
Tables.pop_back();
t->hide(*this);
}
int dataRemove=DataInfo.size()-ri.nData;
while(dataRemove>0 && DataInfo.size()>0) {
DataInfo.pop_front();
dataRemove--;
}
CurrentX=ri.sCX;
CurrentY=ri.sCY;
onClear = ri.onClear;
postClear = ri.postClear;
for (auto it = restorePoints.begin(); it != restorePoints.end(); ) {
if (!ri.restorePoints.count(it->first) && (& it->second != &ri))
it = restorePoints.erase(it);
else
++it;
}
}
void gdioutput::restore(const string &id, bool DoRefresh)
{
if (restorePoints.count(id)==0)
return;
const RestoreInfo &ri=restorePoints[id];
restoreInternal(ri);
MaxX=ri.sMX;
MaxY=ri.sMY;
if (DoRefresh)
refresh();
setOffset(ri.sOY, ri.sOY, false);
}
void gdioutput::restoreNoUpdate(const string &id)
{
if (restorePoints.count(id)==0)
return;
const RestoreInfo &ri=restorePoints[id];
MaxX=ri.sMX;
MaxY=ri.sMY;
restoreInternal(ri);
}
void gdioutput::setPostClearCb(GUICALLBACK cb)
{
postClear=cb;
}
void gdioutput::setOnClearCb(GUICALLBACK cb)
{
onClear=cb;
}
bool gdioutput::canClear()
{
if (!onClear)
return true;
try {
return onClear(this, GUI_CLEAR, 0)!=0;
}
catch (const meosCancel&) {
return false;
}
catch (meosException & ex) {
if (isTestMode)
throw ex;
wstring msg = ex.wwhat();
alert(msg);
}
catch(const std::exception &ex) {
if (isTestMode)
throw ex;
string msg(ex.what());
alert(msg);
}
return true;
}
int gdioutput::sendCtrlMessage(const string &id)
{
for (list<ButtonInfo>::iterator it=BI.begin(); it != BI.end(); ++it) {
if (id==it->id) {
if (it->handler)
return it->handleEvent(*this, GUI_BUTTON);
else if (it->callBack)
return it->callBack(this, GUI_BUTTON, &*it); //it may be destroyed here...
}
}
for(list<EventInfo>::iterator it=Events.begin(); it != Events.end(); ++it){
if (id==it->id) {
if (it->hasEventHandler())
return it->handleEvent(*this, GUI_EVENT);
else if (it->callBack)
return it->callBack(this, GUI_EVENT, &*it); //it may be destroyed here...
}
}
#ifdef _DEBUG
throw meosException("Unknown command " +id);
#endif
return 0;
}
void gdioutput::unregisterEvent(const string &id)
{
list<EventInfo>::iterator it;
for (it = Events.begin(); it != Events.end(); ++it) {
if ( id == it->id) {
Events.erase(it);
return;
}
}
}
EventInfo &gdioutput::registerEvent(const string &id, GUICALLBACK cb)
{
list<EventInfo>::iterator it;
for (it = Events.begin(); it != Events.end(); ++it) {
if ( id == it->id) {
Events.erase(it);
break;
}
}
EventInfo ei;
ei.id=id;
ei.callBack=cb;
Events.push_front(ei);
return Events.front();
}
void flushEvent(const string &id, const string &origin, DWORD data, int extraData);
DWORD gdioutput::makeEvent(const string &id, const string &origin,
DWORD data, int extraData, bool doflush)
{
if (doflush) {
#ifndef MEOSDB
::flushEvent(id, origin, data, extraData);
#else
throw std::exception("internal gdi/database error");
#endif
}
else {
list<EventInfo>::iterator it;
for(it=Events.begin(); it != Events.end(); ++it){
if (id==it->id && (it->callBack || it->hasEventHandler()) ) {
it->setData(origin, data);
if (extraData) {
it->setExtra(extraData);
}
if (it->handleEvent(*this, GUI_EVENT)) {
return 1;
}
else
return it->callBack(this, GUI_EVENT, &*it); //it may be destroyed here...
}
}
}
return -1;
}
RectangleInfo &RectangleInfo::changeDimension(gdioutput &gdi, int dx, int dy) {
rc.right += dx;
rc.bottom += dy;
int ex = gdi.scaleLength(5);
gdi.updatePos(rc.left, rc.top, rc.right-rc.left+ex, rc.bottom-rc.top+ex);
return *this;
}
RectangleInfo &gdioutput::addRectangle(const RECT &rc, GDICOLOR color, bool drawBorder, bool addFirst) {
RectangleInfo ri;
ri.rc.left = min<int>(rc.left, rc.right);
ri.rc.right = max<int>(rc.left, rc.right);
ri.rc.top = min<int>(rc.top, rc.bottom);
ri.rc.bottom = max<int>(rc.top, rc.bottom);
if (color==colorDefault)
ri.color = GetSysColor(COLOR_INFOBK);
else if (color == colorWindowBar) {
ri.color = GetSysColor(COLOR_3DFACE);
}
else ri.color = color;
ri.color2 = ri.color;
ri.drawBorder = drawBorder;
if (hWndTarget && !manualUpdate) {
HDC hDC=GetDC(hWndTarget);
renderRectangle(hDC, 0, ri);
ReleaseDC(hWndTarget, hDC);
}
int ex = scaleLength(5);
updatePos(ri.rc.left, ri.rc.top, ri.rc.right-ri.rc.left+ex, ri.rc.bottom-ri.rc.top+ex);
if (addFirst) {
Rectangles.push_front(ri);
return Rectangles.front();
}
else {
Rectangles.push_back(ri);
return Rectangles.back();
}
}
RectangleInfo &gdioutput::getRectangle(const char *id) {
for (list<RectangleInfo>::iterator it = Rectangles.begin(); it != Rectangles.end(); ++it) {
return *it;
}
string err = string("Internal Error, identifier not found: X#") + id;
throw std::exception(err.c_str());
}
void gdioutput::setOffset(int x, int y, bool update)
{
int h,w;
getTargetDimension(w, h);
int cdy = 0;
int cdx = 0;
if (y!=OffsetY) {
int oldY = OffsetY;
OffsetY = y;
if (OffsetY < 0)
OffsetY = 0;
else if (OffsetY > MaxY)
OffsetY = MaxY;
//cdy=(oldY!=OffsetY);
cdy = oldY - OffsetY;
}
if (x!=OffsetX) {
int oldX = OffsetX;
OffsetX = x;
if (OffsetX < 0)
OffsetX = 0;
else if (OffsetX > MaxX)
OffsetX = MaxX;
//cdx=(oldX!=OffsetX);
cdx = oldX - OffsetX;
}
if (cdx || cdy) {
updateScrollbars();
updateObjectPositions();
if (cdy) {
SCROLLINFO si;
memset(&si, 0, sizeof(si));
si.nPos=OffsetY;
si.fMask=SIF_POS;
SetScrollInfo(hWndTarget, SB_VERT, &si, true);
}
if (cdx) {
SCROLLINFO si;
memset(&si, 0, sizeof(si));
si.nPos=OffsetX;
si.fMask=SIF_POS;
SetScrollInfo(hWndTarget, SB_HORZ, &si, true);
}
if (update) {
//RECT ScrollArea, ClipArea;
//GetClientRect(hWndTarget, &ScrollArea);
//ClipArea = ScrollArea;
/* ScrollArea.top=-gdi->getHeight()-100;
ScrollArea.bottom+=gdi->getHeight();
ScrollArea.right=gdi->getWidth()-gdi->GetOffsetX()+15;
ScrollArea.left = -2000;
*/
ScrollWindowEx(hWndTarget, -cdx, cdy,
NULL, NULL,
(HRGN) NULL, (LPRECT) NULL, 0/*SW_INVALIDATE|SW_SMOOTHSCROLL|(1000*65536 )*/);
UpdateWindow(hWndTarget);
}
}
}
void gdioutput::scrollTo(int x, int y)
{
int cx=x-OffsetX;
int cy=y-OffsetY;
int h,w;
getTargetDimension(w, h);
bool cdy=false;
bool cdx=false;
if (cy<=(h/15) || cy>=(h-h/10)) {
int oldY=OffsetY;
OffsetY=y-h/2;
if (OffsetY<0)
OffsetY=0;
else if (OffsetY>MaxY)
OffsetY=MaxY;
cdy=(oldY!=OffsetY);
}
if (cx<=(w/15) || cx>=(w-w/8)) {
int oldX=OffsetX;
OffsetX=x-w/2;
if (OffsetX<0)
OffsetX=0;
else if (OffsetX>MaxX)
OffsetX=MaxX;
cdx=(oldX!=OffsetX);
}
if (cdx || cdy) {
updateScrollbars();
updateObjectPositions();
if (cdy) {
SCROLLINFO si;
memset(&si, 0, sizeof(si));
si.nPos=OffsetY;
si.fMask=SIF_POS;
SetScrollInfo(hWndTarget, SB_VERT, &si, true);
}
if (cdx) {
SCROLLINFO si;
memset(&si, 0, sizeof(si));
si.nPos=OffsetX;
si.fMask=SIF_POS;
SetScrollInfo(hWndTarget, SB_HORZ, &si, true);
}
}
}
void gdioutput::scrollToBottom()
{
OffsetY=MaxY;
SCROLLINFO si;
memset(&si, 0, sizeof(si));
updateScrollbars();
updateObjectPositions();
si.nPos=OffsetY;
si.fMask=SIF_POS;
SetScrollInfo(hWndTarget, SB_VERT, &si, true);
}
bool gdioutput::clipOffset(int PageX, int PageY, int &MaxOffsetX, int &MaxOffsetY)
{
if (animationData) {
MaxOffsetX = 0;
MaxOffsetY = 0;
return false;
}
if (highContrast)
setHighContrastMaxWidth();
int oy=OffsetY;
int ox=OffsetX;
MaxOffsetY=max(getPageY()-PageY, 0);
MaxOffsetX=max(getPageX()-PageX, 0);
if (OffsetY<0) OffsetY=0;
else if (OffsetY>MaxOffsetY)
OffsetY=MaxOffsetY;
if (OffsetX<0) OffsetX=0;
else if (OffsetX>MaxOffsetX)
OffsetX=MaxOffsetX;
if (ox!=OffsetX || oy!=OffsetY){
updateObjectPositions();
return true;
}
return false;
}
//bool ::GetSaveFile(string &file, char *filter)
wstring gdioutput::browseForSave(const vector< pair<wstring, wstring> > &filter,
const wstring &defext, int &filterIndex)
{
if (isTestMode) {
if (!cmdAnswers.empty()) {
string ans = cmdAnswers.front();
cmdAnswers.pop_front();
if (ans.substr(0, 1) == "*")
return widen(ans.substr(1));
}
throw meosException("Browse for file");
}
InitCommonControls();
TCHAR FileName[260];
FileName[0]=0;
OPENFILENAME of;
wstring sFilter;
for (size_t k = 0; k< filter.size(); k++) {
sFilter.append(lang.tl(filter[k].first)).push_back(0);
sFilter.append(filter[k].second).push_back(0);
}
sFilter.push_back(0);
of.lStructSize = sizeof(of);
of.hwndOwner = hWndTarget;
of.hInstance = (HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE);
of.lpstrFilter = sFilter.c_str();
of.lpstrCustomFilter = NULL;
of.nMaxCustFilter = 0;
of.nFilterIndex = filterIndex;
of.lpstrFile = FileName;
of.nMaxFile = 260;
of.lpstrFileTitle = NULL;
of.nMaxFileTitle = 0;
of.lpstrInitialDir = NULL;
of.lpstrTitle = NULL;
of.Flags = OFN_OVERWRITEPROMPT|OFN_HIDEREADONLY;
of.lpstrDefExt = defext.c_str();
of.lpfnHook = NULL;
bool res;
setCommandLock();
try {
res = GetSaveFileName(&of) != false;
liftCommandLock();
}
catch (...) {
liftCommandLock();
throw;
}
if (res==false)
return L"";
filterIndex=of.nFilterIndex;
return FileName;
}
wstring gdioutput::browseForOpen(const vector< pair<wstring, wstring> > &filter,
const wstring &defext)
{
if (isTestMode) {
if (!cmdAnswers.empty()) {
string ans = cmdAnswers.front();
cmdAnswers.pop_front();
if (ans.substr(0, 1) == "*")
return widen(ans.substr(1));
}
throw meosException("Browse for file");
}
InitCommonControls();
wchar_t FileName[260];
FileName[0]=0;
OPENFILENAME of;
wstring sFilter;
for (size_t k = 0; k< filter.size(); k++) {
sFilter.append(lang.tl(filter[k].first)).push_back(0);
sFilter.append(filter[k].second).push_back(0);
}
sFilter.push_back(0);
of.lStructSize = sizeof(of);
of.hwndOwner = hWndTarget;
of.hInstance = (HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE);
of.lpstrFilter = sFilter.c_str();
of.lpstrCustomFilter = NULL;
of.nMaxCustFilter = 0;
of.nFilterIndex = 1;
of.lpstrFile = FileName;
of.nMaxFile = 260;
of.lpstrFileTitle = NULL;
of.nMaxFileTitle = 0;
of.lpstrInitialDir = NULL;
of.lpstrTitle = NULL;
of.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;
of.lpstrDefExt = defext.c_str();
of.lpfnHook = NULL;
bool res;
setCommandLock();
try {
res = GetOpenFileName(&of) != false;
liftCommandLock();
}
catch (...) {
liftCommandLock();
throw;
}
if (res == false)
return L"";
return FileName;
}
wstring gdioutput::browseForFolder(const wstring &folderStart, const wchar_t *descr)
{
if (isTestMode) {
if (!cmdAnswers.empty()) {
string ans = cmdAnswers.front();
cmdAnswers.pop_front();
if (ans.substr(0, 1) == "*")
return widen(ans.substr(1));
}
throw meosException("Browse for folder");
}
CoInitializeEx(0, COINIT_APARTMENTTHREADED);
BROWSEINFO bi;
wchar_t InstPath[260];
wcscpy_s(InstPath, folderStart.c_str());
memset(&bi, 0, sizeof(bi) );
bi.hwndOwner=hWndAppMain;
bi.pszDisplayName=InstPath;
wstring title = descr ? lang.tl(descr) : L"";
bi.lpszTitle = title.c_str();
bi.ulFlags=BIF_RETURNONLYFSDIRS|BIF_EDITBOX|BIF_NEWDIALOGSTYLE|BIF_EDITBOX;
LPITEMIDLIST pidl_new;
setCommandLock();
try {
pidl_new = SHBrowseForFolder(&bi);
liftCommandLock();
}
catch (...) {
liftCommandLock();
throw;
}
if (pidl_new==NULL)
return L"";
// Convert the item ID list's binary
// representation into a file system path
//char szPath[_MAX_PATH];
SHGetPathFromIDList(pidl_new, InstPath);
// Allocate a pointer to an IMalloc interface
LPMALLOC pMalloc;
// Get the address of our task allocator's IMalloc interface
SHGetMalloc(&pMalloc);
// Free the item ID list allocated by SHGetSpecialFolderLocation
pMalloc->Free(pidl_new);
// Free our task allocator
pMalloc->Release();
return InstPath;
}
bool gdioutput::openDoc(const wchar_t *doc)
{
return (intptr_t)ShellExecute(hWndTarget, L"open", doc, NULL, L"", SW_SHOWNORMAL ) >32;
}
void gdioutput::init(HWND hWnd, HWND hMain, HWND hTab)
{
setWindow(hWnd);
hWndAppMain=hMain;
hWndTab=hTab;
InitCommonControls();
hWndToolTip = CreateWindow(TOOLTIPS_CLASS, (LPWSTR) NULL, TTS_ALWAYSTIP,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, (HMENU) NULL, (HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE), NULL);
}
ToolInfo &gdioutput::addToolTip(const string &tipId, const wstring &tip, HWND hWnd, RECT *rc) {
static ToolInfo dummy;
if (!hWndToolTip)
return dummy;
toolTips.push_back(ToolInfo());
ToolInfo &info = toolTips.back();
TOOLINFOW &ti = info.ti;
info.tip = lang.tl(tip);
memset(&ti, 0, sizeof(ti));
ti.cbSize = sizeof(TOOLINFO);
if (hWnd != 0) {
ti.uFlags = TTF_IDISHWND;
info.id = uintptr_t(hWnd);
ti.uId = (UINT_PTR) hWnd;
}
else {
ti.uFlags = TTF_SUBCLASS;
info.id = toolTips.size();
ti.uId = info.id;
}
ti.hwnd = hWndTarget;
ti.hinst = (HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE);
info.name = tipId;
ti.lpszText = (LPWSTR)toolTips.back().tip.c_str();
if (rc != 0)
ti.rect = *rc;
SendMessage(hWndToolTip, TTM_ADDTOOLW, 0, (LPARAM) &ti);
if (tip.find('\n') != string::npos || tip.length()>40)
SendMessage(hWndToolTip, TTM_SETMAXTIPWIDTH, 0, 250);
return info;
}
ToolInfo *gdioutput::getToolTip(const string &id) {
for (ToolList::reverse_iterator it = toolTips.rbegin(); it != toolTips.rend(); ++it) {
if (it->name == id)
return &*it;
}
return 0;
}
ToolInfo &gdioutput::updateToolTip(const string &id, const wstring &tip) {
for (ToolList::reverse_iterator it = toolTips.rbegin(); it != toolTips.rend(); ++it) {
if (it->name == id && hWndToolTip) {
it->tip = lang.tl(tip);
SendMessage(hWndToolTip, TTM_UPDATETIPTEXTW, 0, (LPARAM) &it->ti);
return *it;
}
}
BaseInfo &bi = getBaseInfo(id.c_str());
return addToolTip(id, tip, bi.getControlWindow());
}
void gdioutput::selectTab(int Id)
{
if (hWndTab)
TabCtrl_SetCurSel(hWndTab, Id);
}
void gdioutput::getTargetDimension(int &x, int &y) const
{
if (hWndTarget){
RECT rc;
GetClientRect(hWndTarget, &rc);
x=rc.right;
y=rc.bottom;
}
else {
x=0;
y=0;
}
}
Table &gdioutput::getTable() const {
if (Tables.empty())
throw std::exception("No table defined");
return *const_cast<Table *>(Tables.back().table.get());
}
static int gdiTableCB(gdioutput *gdi, int type, void *data)
{
if (type == GUI_BUTTON) {
ButtonInfo bi = *static_cast<ButtonInfo *>(data);
//gdi->tableCB(bi, static_cast<Table *>(bi.getExtra()));
gdi->tableCB(bi, &gdi->getTable());
}
return 0;
}
void gdioutput::tableCB(ButtonInfo &bu, Table *t)
{
#ifndef MEOSDB
if (bu.id=="tblPrint") {
t->keyCommand(*this, KC_PRINT);
}
else if (bu.id=="tblColumns") {
disableTables();
if (Tables.empty())
return;
restore("tblRestore");
int ybase = Tables.back().yp;
addString("", ybase, 20, boldLarge, "Välj kolumner");
ybase += scaleLength(30);
addString("", ybase, 20, 0, L"Välj kolumner för tabellen X.#"+ t->getTableName());
ybase += getLineHeight()*2;
addListBox(20, ybase, "tblColSel", 180, 450, 0, L"", L"", true);
const int btnHeight = getButtonHeight()+scaleLength(5);
vector<Table::ColSelection> cols = t->getColumns();
set<int> sel;
for (size_t k=0; k<cols.size(); k++) {
addItem("tblColSel", cols[k].name, cols[k].index);
if (cols[k].selected)
sel.insert(cols[k].index);
}
setSelection("tblColSel", sel);
int xp = scaleLength(220);
addButton(xp, ybase+btnHeight*0, "tblAll", "Välj allt", gdiTableCB);
addButton(xp, ybase+btnHeight*1, "tblNone", "Välj inget", gdiTableCB);
addButton(xp, ybase+btnHeight*2, "tblAuto", "Välj automatiskt", gdiTableCB).setExtra(t->getTableId());
addButton(xp, ybase+btnHeight*4, "tblOK", "OK", gdiTableCB).setExtra(t->getTableId());
addButton(xp, ybase+btnHeight*5, "tblCancel", "Avbryt", gdiTableCB);
if (toolbar)
toolbar->hide();
refresh();
}
else if (bu.id=="tblAll") {
set<int> sel;
sel.insert(-1);
setSelection("tblColSel", sel);
}
else if (bu.id=="tblNone") {
set<int> sel;
setSelection("tblColSel", sel);
}
else if (bu.id=="tblAuto") {
restore("tblRestore", false);
t->autoSelectColumns();
t->autoAdjust(*this);
enableTables();
refresh();
}
else if (bu.id=="tblOK") {
set<int> sel;
getSelection("tblColSel", sel);
restore("tblRestore", false);
t->clearCellSelection(this);
t->selectColumns(sel);
t->autoAdjust(*this);
enableTables();
refresh();
}
else if (bu.id=="tblReset") {
t->clearCellSelection(this);
t->resetColumns();
t->autoAdjust(*this);
t->updateDimension(*this);
refresh();
}
else if (bu.id == "tblMarkAll") {
t->keyCommand(*this, KC_MARKALL);
}
else if (bu.id == "tblClearAll") {
t->keyCommand(*this, KC_CLEARALL);
}
else if (bu.id=="tblUpdate") {
t->keyCommand(*this, KC_REFRESH);
}
else if (bu.id=="tblCancel") {
restore("tblRestore", true);
enableTables();
refresh();
}
else if (bu.id == "tblCopy") {
t->keyCommand(*this, KC_COPY);
}
else if (bu.id == "tblPaste") {
t->keyCommand(*this, KC_PASTE);
}
else if (bu.id == "tblRemove") {
t->keyCommand(*this, KC_DELETE);
}
else if (bu.id == "tblInsert") {
t->keyCommand(*this, KC_INSERT);
}
#endif
}
void gdioutput::enableTables()
{
useTables=true;
if (!Tables.empty()) {
auto &t = Tables.front().table;
if (toolbar == 0)
toolbar = new Toolbar(*this);
toolbar->setData(t);
string tname = string("table") + itos(t->canDelete()) + itos(t->canInsert()) + itos(t->canPaste());
if (!toolbar->isLoaded(tname)) {
toolbar->reset();
toolbar->addButton("tblColumns", 1, 2, "Välj vilka kolumner du vill visa");
toolbar->addButton("tblPrint", 0, STD_PRINT, "Skriv ut tabellen (X)#Ctrl+P");
toolbar->addButton("tblUpdate", 1, 0, "Uppdatera alla värden i tabellen (X)#F5");
toolbar->addButton("tblReset", 1, 4, "Återställ tabeldesignen och visa allt");
toolbar->addButton("tblMarkAll", 1, 5, "Markera allt (X)#Ctrl+A");
toolbar->addButton("tblClearAll", 1, 6, "Markera inget (X)#Ctrl+D");
toolbar->addButton("tblCopy", 0, STD_COPY, "Kopiera selektionen till urklipp (X)#Ctrl+C");
if (t->canPaste())
toolbar->addButton("tblPaste", 0, STD_PASTE, "Klistra in data från urklipp (X)#Ctrl+V");
if (t->canDelete())
toolbar->addButton("tblRemove", 1, 1, "Ta bort valda rader från tabellen (X)#Del");
if (t->canInsert())
toolbar->addButton("tblInsert", 1, 3, "Lägg till en ny rad i tabellen (X)#Ctrl+I");
toolbar->createToolbar(tname, L"Tabellverktyg");
}
else {
toolbar->show();
}
}
}
void gdioutput::processToolbarMessage(const string &id, Table *tbl) {
if (hasCommandLock())
return;
wstring msg;
string cmd;
if (getRecorder().recording()) {
cmd = "tableCmd(\"" + id + "\"); //" + narrow(tbl->getTableName());
}
try {
ButtonInfo bi;
bi.id = id;
tableCB(bi, tbl);
getRecorder().record(cmd);
}
catch (const meosCancel&) {
}
catch (meosException &ex) {
msg = ex.wwhat();
}
catch(std::exception &ex) {
msg = widen(ex.what());
if (msg.empty())
msg = L"Ett okänt fel inträffade.";
}
catch(...) {
msg = L"Ett okänt fel inträffade.";
}
if (!msg.empty())
alert(msg);
}
HWND gdioutput::getToolbarWindow() const {
if (!toolbar)
return 0;
return toolbar->getFloater();
}
bool gdioutput::hasToolbar() const {
if (!toolbar)
return false;
return toolbar->isVisible();
}
void gdioutput::activateToolbar(bool active) {
if (!toolbar)
return;
toolbar->activate(active);
}
void gdioutput::disableTables()
{
useTables=false;
for(list<ButtonInfo>::iterator bit=BI.begin(); bit != BI.end();) {
if (bit->id.substr(0, 3)=="tbl" && bit->getExtra()!=0) {
string id = bit->id;
++bit;
removeWidget(id);
}
else
++bit;
}
}
void gdioutput::addTable(const shared_ptr<Table> &t, int x, int y)
{
TableInfo ti;
ti.table = t;
ti.xp = x;
ti.yp = y;
t->setPosition(x,y, MaxX, MaxY);
if (t->hasAutoSelect())
t->autoSelectColumns();
t->autoAdjust(*this);
Tables.push_back(ti);
//updatePos(x, y, dx + TableXMargin, dy + TableYMargin);
setRestorePoint("tblRestore");
enableTables();
updateScrollbars();
}
void gdioutput::pasteText(const char *id)
{
list<InputInfo>::iterator it;
for (it=II.begin(); it != II.end(); ++it) {
if (it->id==id) {
SendMessage(it->hWnd, WM_PASTE, 0,0);
return;
}
}
}
wchar_t *gdioutput::getExtra(const char *id) const {
return getBaseInfo(id).getExtra();
}
int gdioutput::getExtraInt(const char *id) const {
return getBaseInfo(id).getExtraInt();
}
bool gdioutput::hasEditControl() const
{
return !II.empty() || (Tables.size()>0 && Tables.front().table->hasEditControl());
}
void gdioutput::enableEditControls(bool enable, bool processAll)
{
set<string> TCheckControls;
for (list<ButtonInfo>::iterator it=BI.begin(); it != BI.end(); ++it) {
if (it->isEditControl || processAll) {
EnableWindow(it->hWnd, enable);
if (it->isCheckbox) {
TCheckControls.insert("T" + it->id);
}
}
}
for (list<TextInfo>::iterator it=TL.begin(); it != TL.end(); ++it) {
if (TCheckControls.count(it->id)) {
enableCheckBoxLink(*it, enable);
}
}
for (list<InputInfo>::iterator it=II.begin(); it != II.end(); ++it) {
if (it->isEditControl)
EnableWindow(it->hWnd, enable);
}
for( list<ListBoxInfo>::iterator it=LBI.begin(); it != LBI.end(); ++it) {
if (it->isEditControl)
EnableWindow(it->hWnd, enable);
}
}
void gdioutput::closeWindow()
{
PostMessage(hWndTarget, WM_CLOSE, 0, 0);
}
InputInfo &InputInfo::setPassword(bool pwd) {
LONG style = GetWindowLong(hWnd, GWL_STYLE);
if (pwd)
style |= ES_PASSWORD;
else
style &= ~ES_PASSWORD;
SetWindowLong(hWnd, GWL_STYLE, style);
SendMessage(hWnd, EM_SETPASSWORDCHAR, 183, 0);
return *this;
}
int gdioutput::setHighContrastMaxWidth() {
RECT rc;
GetClientRect(hWndTarget, &rc);
if (lockRefresh)
return rc.bottom;
#ifdef DEBUGRENDER
OutputDebugString("Set high contrast\n");
#endif
double w = getPageX();
double s = rc.right / w;
if (!highContrast || (fabs(s-1.0) > 1e-3 && (s * scale) >= 1.0) ) {
lockRefresh = true;
try {
highContrast = true;
scaleSize(s);
refresh();
lockRefresh = false;
}
catch (...) {
lockRefresh = false;
throw;
}
}
return rc.bottom;
}
double static acc = 0;
void gdioutput::setAutoScroll(double speed) {
if (autoSpeed == 0 && speed != 0) {
SetTimer(hWndTarget, 1001, 20, 0);
autoPos = OffsetY;
}
else if (speed == 0 && autoSpeed != 0) {
KillTimer(hWndTarget, 1001);
}
if (speed == -1)
autoSpeed = -autoSpeed;
else
autoSpeed = speed;
autoCounter = - M_PI_2;
acc = 0;
}
void gdioutput::getAutoScroll(double &speed, double &pos) const {
RECT rc;
GetClientRect(hWndTarget, &rc);
double height = rc.bottom;
double s = autoSpeed * (1 + height/1000 + sin(autoCounter)/max(1.0, 500/height));
autoCounter += M_PI/75.0;
if (autoCounter > M_PI)
autoCounter -= 2*M_PI;
acc += 0.3/30;
if (acc>0.8)
acc = 0.8;
speed = (lastSpeed * (1.0-acc) + s * acc);
lastSpeed = speed;
pos = autoPos;
}
void gdioutput::storeAutoPos(double pos) {
autoPos = pos;
}
void gdioutput::setFullScreen(bool useFullScreen) {
if (useFullScreen && !fullScreen) {
SetWindowLong(hWndTarget, GWL_STYLE, WS_POPUP | WS_BORDER);
ShowWindow(hWndTarget, SW_MAXIMIZE);
UpdateWindow(hWndTarget);
}
else if (fullScreen) {
SetWindowLong(hWndTarget, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
ShowWindow(hWndTarget, SW_NORMAL);
UpdateWindow(hWndTarget);
}
fullScreen = useFullScreen;
}
void gdioutput::setColorMode(DWORD bgColor1, DWORD bgColor2,
DWORD fgColor, const wstring &bgImage) {
backgroundColor1 = bgColor1;
backgroundColor2 = bgColor2;
foregroundColor = fgColor;
backgroundImage = bgImage;
}
DWORD gdioutput::getFGColor() const {
return foregroundColor != -1 ? foregroundColor : 0;
}
DWORD gdioutput::getBGColor() const {
return backgroundColor1 != -1 ? backgroundColor1 : RGB(255,255,255);
}
DWORD gdioutput::getBGColor2() const {
return backgroundColor2;
}
const wstring &gdioutput::getBGImage() const {
return backgroundImage;
}
bool gdioutput::hasCommandLock() const {
if (commandLock)
return true;
if (commandUnlockTime > 0) {
DWORD t = GetTickCount();
if (commandUnlockTime < (commandUnlockTime + 500) &&
t < (commandUnlockTime+500)) {
commandUnlockTime = 0;
return true;
}
}
return false;
}
void gdioutput::setCommandLock() const {
commandLock = true;
}
void gdioutput::liftCommandLock() const {
commandUnlockTime = GetTickCount();
commandLock = false;
}
int gdioutput::getLineHeight(gdiFonts font, const wchar_t *face) const {
int h;
if (face == nullptr)
h = getFontHeight(font, _EmptyWString);
else
h = getFontHeight(font, face);
return (11*h)/10;
}
GDIImplFontSet::GDIImplFontSet() {
Huge = 0;
Large = 0;
Medium = 0;
Small = 0;
pfLarge = 0;
pfMedium = 0;
pfMediumPlus = 0;
pfSmall = 0;
pfSmallItalic = 0;
pfItalic = 0;
pfItalicMediumPlus = 0;
pfMono = 0;
}
GDIImplFontSet::~GDIImplFontSet() {
deleteFonts();
}
void GDIImplFontSet::deleteFonts()
{
if (Huge)
DeleteObject(Huge);
Huge = 0;
if (Large)
DeleteObject(Large);
Large = 0;
if (Medium)
DeleteObject(Medium);
Medium = 0;
if (Small)
DeleteObject(Small);
Small = 0;
if (pfLarge)
DeleteObject(pfLarge);
pfLarge = 0;
if (pfMedium)
DeleteObject(pfMedium);
pfMedium = 0;
if (pfMediumPlus)
DeleteObject(pfMediumPlus);
pfMediumPlus = 0;
if (pfSmall)
DeleteObject(pfSmall);
pfSmall = 0;
if (pfMono)
DeleteObject(pfMono);
pfMono = 0;
if (pfSmallItalic)
DeleteObject(pfSmallItalic);
pfSmallItalic = 0;
if (pfItalicMediumPlus)
DeleteObject(pfItalicMediumPlus);
pfItalicMediumPlus = 0;
if (pfItalic)
DeleteObject(pfItalic);
pfItalic = 0;
}
float GDIImplFontSet::baseSize(int format, float scale) {
format &= 0xFF;
if (format==0 || format==10) {
return 14 * scale;
}
else if (format==fontMedium){
return 14 * scale;
}
else if (format==1) {
return 14.0001f * scale; //Bold
}
else if (format==boldLarge){
return 24.0001f * scale;
}
else if (format==boldHuge){
return 34.0001f * scale;
}
else if (format==boldSmall){
return 11.0001f * scale;
}
else if (format==fontLarge){
return 24 * scale;
}
else if (format==fontMediumPlus){
return 18 * scale;
}
else if (format==fontSmall){
return 11 * scale;
}
else if (format==italicSmall){
return 11 * scale;
}
else if (format==italicText){
return 14 * scale;
}
else if (format == monoText){
return 14 * scale;
}
else if (format==italicMediumPlus){
return 18 * scale;
}
else {
return 10 * scale;
}
}
void GDIImplFontSet::init(double scale, const wstring &font, const wstring &gdiName_)
{
int charSet = DEFAULT_CHARSET;
deleteFonts();
gdiName = gdiName_;
Huge=CreateFont(int(scale*34), 0, 0, 0, FW_BOLD, false, false, false, charSet,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, DEFAULT_PITCH|FF_ROMAN, font.c_str());
Large=CreateFont(int(scale*24), 0, 0, 0, FW_BOLD, false, false, false, charSet,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, DEFAULT_PITCH|FF_ROMAN, font.c_str());
Medium=CreateFont(int(scale*14), 0, 0, 0, FW_BOLD, false, false, false, charSet,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, DEFAULT_PITCH|FF_ROMAN, font.c_str());
Small=CreateFont(int(scale*11), 0, 0, 0, FW_BOLD, false, false, false, charSet,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, DEFAULT_PITCH|FF_ROMAN, font.c_str());
pfLarge=CreateFont(int(scale*24), 0, 0, 0, FW_NORMAL, false, false, false, charSet,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, DEFAULT_PITCH|FF_ROMAN, font.c_str());
pfMedium=CreateFont(int(scale*14), 0, 0, 0, FW_NORMAL, false, false, false, charSet,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, DEFAULT_PITCH|FF_ROMAN, font.c_str());
pfMediumPlus=CreateFont(int(scale*18), 0, 0, 0, FW_NORMAL, false, false, false, charSet,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, DEFAULT_PITCH|FF_ROMAN, font.c_str());
pfSmall=CreateFont(int(scale*11), 0, 0, 0, FW_NORMAL, false, false, false, charSet,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, DEFAULT_PITCH|FF_ROMAN, font.c_str());
pfSmallItalic = CreateFont(int(scale*11), 0, 0, 0, FW_NORMAL, true, false, false, charSet,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, DEFAULT_PITCH|FF_ROMAN, font.c_str());
pfItalic = CreateFont(int(scale*14), 0, 0, 0, FW_NORMAL, true, false, false, charSet,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, DEFAULT_PITCH|FF_ROMAN, font.c_str());
pfMono = CreateFont(int(scale*12), 0, 0, 0, FW_NORMAL, false, false, false, charSet,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, DEFAULT_PITCH|FF_MODERN, L"Lucida Console");
pfItalicMediumPlus = CreateFont(int(scale*18), 0, 0, 0, FW_NORMAL, true, false, false, charSet,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, DEFAULT_PITCH|FF_ROMAN, font.c_str());
}
void GDIImplFontSet::getInfo(FontInfo &fi) const {
fi.normal = pfMedium;
fi.bold = Medium;
fi.italic = pfItalic;
}
void GDIImplFontSet::selectFont(HDC hDC, int format) const {
if (format==0 || format==10) {
SelectObject(hDC, pfMedium);
}
else if (format==fontMedium){
SelectObject(hDC, pfMedium);
}
else if (format==1){
SelectObject(hDC, Medium);
}
else if (format==boldLarge){
SelectObject(hDC, Large);
}
else if (format==boldHuge){
SelectObject(hDC, Huge);
}
else if (format==boldSmall){
SelectObject(hDC, Small);
}
else if (format==fontLarge){
SelectObject(hDC, pfLarge);
}
else if (format==fontMediumPlus){
SelectObject(hDC, pfMediumPlus);
}
else if (format==fontSmall){
SelectObject(hDC, pfSmall);
}
else if (format==italicSmall){
SelectObject(hDC, pfSmallItalic);
}
else if (format==italicText){
SelectObject(hDC, pfItalic);
}
else if (format==italicMediumPlus){
SelectObject(hDC, pfItalicMediumPlus);
}
else if (format == monoText) {
SelectObject(hDC, pfMono);
}
else {
SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
}
}
HFONT GDIImplFontSet::getFont(int format) const {
format = format & 31;
if (format==0 || format==10) {
return pfMedium;
}
else if (format==fontMedium){
return pfMedium;
}
else if (format==1){
return Medium;
}
else if (format==boldLarge){
return Large;
}
else if (format==boldHuge){
return Huge;
}
else if (format==boldSmall){
return Small;
}
else if (format==fontLarge){
return pfLarge;
}
else if (format==fontMediumPlus){
return pfMediumPlus;
}
else if (format==fontSmall){
return pfSmall;
}
else if (format==italicSmall){
return pfSmallItalic;
}
else if (format==italicText){
return pfItalic;
}
else if (format==italicMediumPlus){
return pfItalicMediumPlus;
}
else if (format == monoText) {
return pfMono;
}
else {
return (HFONT)GetStockObject(DEFAULT_GUI_FONT);
}
}
const GDIImplFontSet &gdioutput::getCurrentFont() const {
if (currentFontSet == 0) {
map<wstring, GDIImplFontSet>::const_iterator res = fonts.find(currentFont);
if (res == fonts.end())
throw meosException(L"Font not defined: " + currentFont);
currentFontSet = &res->second;
}
return *currentFontSet;
}
const GDIImplFontSet &gdioutput::getFont(const wstring &font) const {
map<wstring, GDIImplFontSet>::const_iterator res = fonts.find(font);
if (res == fonts.end()) {
return const_cast<gdioutput *>(this)->loadFont(font);
throw meosException(L"Font not defined: " + currentFont);
}
return res->second;
}
int CALLBACK enumFontProc(const LOGFONT* logFont, const TEXTMETRIC *metric, DWORD id, LPARAM lParam) {
if (logFont->lfFaceName[0] == '@')
return 1;
if (metric->tmAveCharWidth <= 0)
return 1;
vector<GDIImplFontEnum> &enumFonts = *(vector<GDIImplFontEnum> *)(lParam);
/*string we = "we: " + itos(logFont->lfWeight);
string wi = "wi: " + itos(metric->tmAveCharWidth);
string he = "he: " + itos(metric->tmHeight);
string info = string(logFont->lfFaceName) + ", " + we + ", " + wi + ", " + he;*/
enumFonts.push_back(GDIImplFontEnum());
GDIImplFontEnum &f = enumFonts.back();
f.face = logFont->lfFaceName;
f.height = metric->tmHeight;
f.width = metric->tmAveCharWidth;
f.relScale = ((double(metric->tmHeight) / double(metric->tmAveCharWidth)) * 14.0/36.0);
return 1;
}
void gdioutput::getEnumeratedFonts(vector< pair<wstring, size_t> > &output) const {
if (enumeratedFonts.empty()) {
HDC hDC = GetDC(hWndTarget);
// EnumFontFamilies(hDC, NULL, enumFontProc, LPARAM(&enumeratedFonts));
LOGFONT logFont;
memset(&logFont, 0, sizeof(LOGFONT));
logFont.lfCharSet = DEFAULT_CHARSET;
EnumFontFamiliesEx(hDC, &logFont, enumFontProc, LPARAM(&enumeratedFonts), 0);
ReleaseDC(hWndTarget, hDC);
}
output.resize(enumeratedFonts.size());
for (size_t k = 0; k<output.size(); k++) {
output[k].first = enumeratedFonts[k].getFace();
output[k].second = k;
}
}
double gdioutput::getRelativeFontScale(gdiFonts font, const wchar_t *fontFace) const {
double sw = scale * 5.2381; //MeOS default assums this//getCurrentFont().getAvgFontWidth(*this, normalText);
double other;
if (fontFace == 0 || fontFace[0] == 0)
other = getCurrentFont().getAvgFontWidth(*this, font);
else
other = getFont(fontFace).getAvgFontWidth(*this, font);
return other/sw;
}
double GDIImplFontSet::getAvgFontWidth(const gdioutput &gdi, gdiFonts font) const {
if (avgWidthCache.empty())
avgWidthCache.resize(16, 0);
if (size_t(font) > avgWidthCache.size())
throw meosException("Internal font error");
if (avgWidthCache[font] == 0) {
TextInfo ti;
ti.xp = 0;
ti.yp = 0;
ti.format = font;
ti.text = L"Goliat Meze 1234:5678";
ti.font = gdiName;
gdi.calcStringSize(ti);
avgWidthCache[font] = double(ti.textRect.right) / double(ti.text.length());
}
return avgWidthCache[font];
}
const wstring &gdioutput::getFontName(int id) {
return _EmptyWString;
}
GDIImplFontEnum::GDIImplFontEnum() {
relScale = 1.0;
}
GDIImplFontEnum::~GDIImplFontEnum() {
}
/*
FontEncoding interpetEncoding(const string &enc) {
if (enc == "RUSSIAN")
return Russian;
else if (enc == "EASTEUROPE")
return EastEurope;
else if (enc == "HEBREW")
return Hebrew;
else
return ANSI;
}*/
const string &gdioutput::narrow(const wstring &input) {
string &output = StringCache::getInstance().get();
output.clear();
output.insert(output.begin(), input.begin(), input.end());
return output;
}
const wstring &gdioutput::widen(const string &input) {
wstring &output = StringCache::getInstance().wget();
int cp = 1252;
if (input.empty()) {
output = L"";
return output;
}
output.reserve(input.size()+1);
output.resize(input.size(), 0);
MultiByteToWideChar(cp, MB_PRECOMPOSED, input.c_str(), input.size(), &output[0], output.size() * sizeof(wchar_t));
return output;
}
const wstring &gdioutput::recodeToWide(const string &input) {
wstring &output = StringCache::getInstance().wget();
int cp = defaultCodePage;
// if (defaultCodePage > 0)
// cp = defaultCodePage;
/*switch(getEncoding()) {
case Russian:
cp = 1251;
break;
case EastEurope:
cp = 1250;
break;
case Hebrew:
cp = 1255;
break;
}*/
if (input.empty()) {
output = L"";
return output;
}
output.reserve(input.size()+1);
output.resize(input.size(), 0);
MultiByteToWideChar(cp, MB_PRECOMPOSED, input.c_str(), input.size(), &output[0], output.size() * sizeof(wchar_t));
return output;
}
const string &gdioutput::recodeToNarrow(const wstring &input) {
string &output = StringCache::getInstance().get();
int cp = defaultCodePage;
// if (defaultCodePage > 0)
// cp = defaultCodePage;
/*switch(getEncoding()) {
case Russian:
cp = 1251;
break;
case EastEurope:
cp = 1250;
break;
case Hebrew:
cp = 1255;
break;
}*/
if (input.empty()) {
output = "";
return output;
}
int res = input.size() * 3 + 2;
output.reserve(res);
output.resize(input.size(), 0);
BOOL usedDef = false;
int ok = WideCharToMultiByte(cp, 0, input.c_str(), input.size(), &output[0], res, "?", &usedDef);
return output;
}
const wstring &gdioutput::fromUTF8(const string &input) {
wstring &output = StringCache::getInstance().wget();
size_t alloc = input.length() + 1;
output.resize(alloc);
wchar_t *ptr = &output[0];
int wlen = MultiByteToWideChar(CP_UTF8, 0, input.c_str(), input.length(), ptr, alloc);
ptr[wlen] = 0;
output.resize(wlen);
return output;
}
const string &gdioutput::toUTF8(const wstring &winput) {
string &output = StringCache::getInstance().get();
size_t alloc = winput.length()*4+32;
output.resize(alloc);
WideCharToMultiByte(CP_UTF8, 0, winput.c_str(), winput.length()+1, (char *)output.c_str(), alloc, 0, 0);
output.resize(strlen(output.c_str()));
return output;
}
void gdioutput::setListDescription(const wstring &desc) {
listDescription = desc;
}
InputInfo &InputInfo::setFont(gdioutput &gdi, gdiFonts font) {
SendMessage(hWnd, WM_SETFONT, (WPARAM) gdi.getCurrentFont().getFont(font), 0);
return *this;
}
void gdioutput::copyToClipboard(const string &html, const wstring &txt) const {
if (OpenClipboard(getHWNDMain()) != false) {
EmptyClipboard();
size_t len = html.length() + 1;
const char *output = html.c_str();
const char cbd[]=
"Version:0.9\n"
"StartHTML:%08u\n"
"EndHTML:%08u\n"
"StartFragment:%08u\n"
"EndFragment:%08u\n";
char head[256];
sprintf_s(head, cbd, 1,0,0,0);
int offset=strlen(head);
//Fill header with relevant information
int ho_start = offset;
int ho_end = offset + len;
sprintf_s(head, cbd, offset,offset+len,ho_start,ho_end);
HANDLE hMem=GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, offset+len);
LPVOID data=GlobalLock(hMem);
memcpy(LPSTR(data), head, offset);
memcpy(LPSTR(data)+offset, output, len);
GlobalUnlock(hMem);
// Text format
//HANDLE hMemText = 0;
HANDLE hMemTextWide = 0;
if (txt.length() > 0) {
size_t siz = txt.length();
hMemTextWide = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, siz * sizeof(wchar_t));
LPVOID dataText = GlobalLock(hMemTextWide);
memcpy(LPSTR(dataText), txt.c_str(), siz * sizeof(wchar_t));
GlobalUnlock(hMemTextWide);
/*
hMemText = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, txt.length()+1);
LPVOID dataText=GlobalLock(hMemText);
memcpy(LPSTR(dataText), txt.c_str() , txt.length()+1);
GlobalUnlock(hMemText);*/
}
else {
// HTML table to text
ostringstream result;
bool started = false;
bool newline = false;
bool dowrite = false;
for (size_t k = 0; k + 3 < html.size(); k++) {
if (html[k] == '<') {
if (html[k+1] == 't') {
if (html[k+2] == 'r') {
newline = true;
if (started)
result << "\r\n";
}
else if (html[k+2] == 'd') {
if (!newline)
result << "\t";
started = true;
newline = false;
dowrite = true;
}
}
else if (html[k+1] == '/') {
if (html[k+2] == 't' && html[k+3] == 'd') {
dowrite = false;
}
}
while (k < html.size() && html[k] != '>')
k++;
}
else {
if (dowrite)
result << html[k];
}
}
string atext = decodeXML(result.str());
/* result.flush();
for (size_t k = 0; k < atext.size(); k++) {
if (atext[k] == '&') {
size_t m = 0;
while ((k+m) < atext.size() && atext[k+m] != ';')
m++;
if ((k+m) < atext.size() && atext[k+m] == ';') {
string cmd = atext.substr(k, m-k);
if (cmd == "nbsp")
result << " ";
else if (cmd == "amp")
result << " ";
else if (cmd == "lt")
result << "<";
else if (cmd == "gt")
result << ">";
else if (cmd == "quot")
result << "\"";
k += m;
}
}
else
result << atext[k];
}
atext = result.str();
*/
if (atext.size() > 0) {
wstring atextw;
int osize = atext.size();
atextw.resize(osize + 1, 0);
size_t siz = atextw.size();
MultiByteToWideChar(CP_UTF8, 0, atext.c_str(), -1, &atextw[0], siz * sizeof(wchar_t));
hMemTextWide = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, siz * sizeof(wchar_t));
LPVOID dataText = GlobalLock(hMemTextWide);
memcpy(LPSTR(dataText), atextw.c_str(), siz * sizeof(wchar_t));
GlobalUnlock(hMemTextWide);
}
}
UINT CF_HTML = RegisterClipboardFormat(L"HTML format");
SetClipboardData(CF_HTML, hMem);
if (hMemTextWide != 0) {
SetClipboardData(CF_UNICODETEXT, hMemTextWide);
}
CloseClipboard();
}
}
Recorder &gdioutput::getRecorder() {
if (recorder.first == 0) {
recorder.first = new Recorder();
recorder.second = true;
}
return *recorder.first;
}
void gdioutput::initRecorder(Recorder *rec) {
if (recorder.second)
delete recorder.first;
recorder.first = rec;
recorder.second = false;
}
string gdioutput::dbPress(const string &id, int extra) {
bool notEnabled = false;
for (list<ButtonInfo>::iterator it=BI.begin(); it != BI.end(); ++it) {
if (id==it->id && (extra == -65536 || extra == it->getExtraInt())) {
if (!IsWindowEnabled(it->hWnd)) {
notEnabled = true;
continue;
}
if (it->isCheckbox) {
check(id, !isChecked(id));
}
else if(!it->callBack && !it->handler)
throw meosException("Button " + id + " is not active.");
wstring val = it->text;
if (it->handler)
it->handleEvent(*this, GUI_BUTTON);
else if (it->callBack)
it->callBack(this, GUI_BUTTON, &*it); //it may be destroyed here...
return toUTF8(val);
}
}
if (notEnabled)
throw meosException("Button " + id + " is not active.");
throw meosException("Unknown command " + id + ".");
}
string gdioutput::dbPress(const string &id, const char *extra) {
wstring eid = widen(extra ? extra : "");
for (list<ButtonInfo>::iterator it=BI.begin(); it != BI.end(); ++it) {
if (id==it->id && (!extra || (it->isExtraString() && eid == it->getExtra()))) {
if (!IsWindowEnabled(it->hWnd))
throw meosException("Button " + id + " is not active.");
if (it->isCheckbox) {
check(id, !isChecked(id));
}
else if(!it->callBack && !it->handler)
throw meosException("Button " + id + " is not active.");
wstring val = it->text;
if (it->handler)
it->handleEvent(*this, GUI_BUTTON);
else if (it->callBack)
it->callBack(this, GUI_BUTTON, &*it); //it may be destroyed here...
return toUTF8(val);
}
}
throw meosException(L"Unknown command " + widen(id) + L"/" + eid + L".");
}
string gdioutput::dbSelect(const string &id, int data) {
for (list<ListBoxInfo>::iterator it = LBI.begin(); it != LBI.end(); ++it) {
if (id==it->id) {
if (!IsWindowEnabled(it->hWnd))
throw meosException("Selection " + id + " is not active.");
if (it->multipleSelection) {
auto res = it->data2Index.find(data);
if (res != it->data2Index.end())
SendMessage(it->hWnd, LB_SETSEL, true, res->second);
else
throw meosException("List " + id + " does not contain value " + itos(data) + ".");
}
else {
if (!selectItemByData(id, data))
throw meosException("List " + id + " does not contain value " + itos(data) + ".");
}
UpdateWindow(it->hWnd);
wstring res = it->text;
internalSelect(*it);
return toUTF8(res);
}
}
throw meosException("Unknown selection " + id + ".");
}
void gdioutput::internalSelect(ListBoxInfo &bi) {
bi.syncData();
if (bi.callBack || bi.handler) {
setWaitCursor(true);
hasCleared = false;
try {
bi.writeLock = true;
if (bi.handler)
bi.handleEvent(*this, GUI_LISTBOX);
else
bi.callBack(this, GUI_LISTBOX, &bi); //it may be destroyed here... Then hasCleared is set.
}
catch(...) {
if (!hasCleared)
bi.writeLock = false;
setWaitCursor(false);
throw;
}
if (!hasCleared)
bi.writeLock = false;
setWaitCursor(false);
}
}
void gdioutput::dbInput(const string &id, const string &text) {
for (list<ListBoxInfo>::iterator it = LBI.begin(); it != LBI.end(); ++it) {
if (id==it->id) {
if (!IsWindowEnabled(it->hWnd) || !it->IsCombo)
throw meosException("Selection " + id + " is not active.");
SendMessage(it->hWnd, CB_SETCURSEL, -1, 0);
SetWindowText(it->hWnd, widen(text).c_str());
it->text = widen(text);
it->data = -1;
if (it->handler)
it->handleEvent(*this, GUI_COMBO);
else if (it->callBack)
it->callBack(this, GUI_COMBO, &*it); //it may be destroyed here...
return;
}
}
for (list<InputInfo>::iterator it = II.begin(); it != II.end(); ++it) {
if (id == it->id) {
if (!IsWindowEnabled(it->hWnd))
throw meosException("Input " + id + " is not active.");
it->text = widen(text);
SetWindowText(it->hWnd, widen(text).c_str());
if (it->handler)
it->handleEvent(*this, GUI_INPUT);
else if (it->callBack)
it->callBack(this, GUI_INPUT, &*it);
return;
}
}
throw meosException("Unknown input " + id + ".");
}
void gdioutput::dbCheck(const string &id, bool state) {
}
string gdioutput::dbClick(const string &id, int extra) {
for (list<TextInfo>::iterator it = TL.begin(); it != TL.end(); ++it) {
if (it->id == id && (extra == -65536 || it->getExtraInt() == extra)) {
if (it->callBack || it->hasEventHandler()) {
string res = toUTF8(it->text);
if (!it->handleEvent(*this, GUI_LINK))
it->callBack(this, GUI_LINK, &*it);
return res;
}
else
throw meosException("Link " + id + " is not active.");
}
}
throw meosException("Unknown link " + id + ".");
}
void gdioutput::dbDblClick(const string &id, int data) {
for (list<ListBoxInfo>::iterator it = LBI.begin(); it != LBI.end(); ++it) {
if (id==it->id) {
if (!IsWindowEnabled(it->hWnd))
throw meosException("Selection " + id + " is not active.");
selectItemByData(id, data);
if (it->handler)
it->handleEvent(*this, GUI_LISTBOXSELECT);
else if (it->callBack)
it->callBack(this, GUI_LISTBOXSELECT, &*it); //it may be destroyed here...
return;
}
}
throw meosException("Unknown selection " + id + ".");
}
// Add the next answer for a dialog popup
void gdioutput::dbPushDialogAnswer(const string &answer) {
cmdAnswers.push_back(answer);
}
void gdioutput::clearDialogAnswers(bool checkEmpty) {
if (!cmdAnswers.empty()) {
string front = cmdAnswers.front();
cmdAnswers.clear();
if (checkEmpty)
throw meosException("Pending answer: X#" + front);
}
}
int gdioutput::dbGetStringCount(const string &str, bool subString) const {
int count = 0;
wstring wstr = widen(str);
for (list<TextInfo>::const_iterator it = TL.begin(); it != TL.end(); ++it) {
if (subString == false) {
if (it->text == wstr)
count++;
}
else {
size_t off = 0;
while(off < it->text.size()) {
off = it->text.find(wstr, off);
if (off != string::npos) {
count++;
off++;
}
else
break;
}
}
}
return count;
}
void gdioutput::dbRegisterSubCommand(const SubCommand *cmd, const string &action) {
if (cmd == 0)
subCommands.clear();
else
subCommands.push_back(make_pair(cmd, action));
}
void gdioutput::runSubCommand() {
if (!subCommands.empty()) {
auto cmd = subCommands.back();
subCommands.pop_back();
cmd.first->subCommand(cmd.second);
}
}
void gdioutput::getWindowsPosition(RECT &rc) const {
WINDOWPLACEMENT wpl;
memset(&wpl, 0, sizeof(WINDOWPLACEMENT));
wpl.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(hWndAppMain, &wpl);
rc = wpl.rcNormalPosition;
}
void gdioutput::setWindowsPosition(const RECT &rc) {
WINDOWPLACEMENT wpl;
memset(&wpl, 0, sizeof(WINDOWPLACEMENT));
wpl.length = sizeof(WINDOWPLACEMENT);
wpl.rcNormalPosition = rc;
wpl.showCmd = SW_SHOWNORMAL;
SetWindowPlacement(hWndAppMain, &wpl);
}
void gdioutput::getVirtualScreenSize(RECT &rc) {
int px = GetSystemMetrics(SM_CXVIRTUALSCREEN);
if (px < 10 || px > 100000)
px = GetSystemMetrics(SM_CXSCREEN);
int py = GetSystemMetrics(SM_CYVIRTUALSCREEN);
if (py < 10 || py > 100000)
py = GetSystemMetrics(SM_CYSCREEN);
rc.left = 0;
rc.right = px;
rc.top = 0;
rc.bottom = py;
}
DWORD gdioutput::selectColor(wstring &def, DWORD input) {
CHOOSECOLOR cc;
memset(&cc, 0, sizeof(cc));
cc.lStructSize = sizeof(cc);
cc.hwndOwner = getHWNDMain();
cc.rgbResult = COLORREF(input);
if (GDICOLOR(input) != colorDefault)
cc.Flags |= CC_RGBINIT;
COLORREF staticColor[16];
memset(staticColor, 0, 16 * sizeof(COLORREF));
const wchar_t *end = def.c_str() + def.length();
const wchar_t * pEnd = def.c_str();
int pix = 0;
while (pEnd < end && pix < 16) {
staticColor[pix++] = wcstol(pEnd, (wchar_t **)&pEnd, 16);
}
cc.lpCustColors = staticColor;
int res = 0;
setCommandLock();
try {
res = ChooseColor(&cc);
liftCommandLock();
}
catch (...) {
liftCommandLock();
throw;
}
if (res) {
wstring co;
for (int ix = 0; ix < 16; ix++) {
wchar_t bf[16];
swprintf_s(bf, L"%x ", staticColor[ix]);
co += bf;
}
swap(def,co);
return cc.rgbResult;
}
return -1;
}
void gdioutput::setAnimationMode(const shared_ptr<AnimationData> &data) {
if (animationData && animationData->takeOver(data))
return;
animationData = data;
}
AutoCompleteInfo &gdioutput::addAutoComplete(const string &key) {
BaseInfo &bi = getBaseInfo(key.c_str());
RECT rc, rcMain;
GetWindowRect(bi.getControlWindow(), &rc);
GetWindowRect(hWndTarget, &rcMain);
POINT pt;
int height = scaleLength(200);
pt.x = rc.right;
//pt.y = min(rc.top, rcMain.bottom-height);
pt.y = rc.bottom;
if (pt.y + height > rcMain.bottom)
pt.y = rc.top - height;
ScreenToClient(hWndTarget, &pt);
if (pt.y < 0) { //Fallback
pt.x = rc.right;
pt.y = min(rc.top, rcMain.bottom - height);
ScreenToClient(hWndTarget, &pt);
}
if (autoCompleteInfo && autoCompleteInfo->matchKey(key)) {
return *autoCompleteInfo;
}
autoCompleteInfo.reset();
HWND hWnd = CreateWindowEx(WS_EX_CLIENTEDGE, L"AUTOCOMPLETE", L"", WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS| WS_BORDER ,
pt.x, pt.y, scaleLength(350), height, hWndTarget, NULL,
(HINSTANCE)GetWindowLongPtr(hWndTarget, GWLP_HINSTANCE), NULL);
autoCompleteInfo.reset(new AutoCompleteInfo(hWnd, key, *this));
//SendMessage(hWnd, WM_SETFONT, (WPARAM)getGUIFont(), 0);
return *autoCompleteInfo;
}
void gdioutput::clearAutoComplete(const string &key) {
autoCompleteInfo.reset();
}
int gdioutput::getPageY() const {
if (hideBG || backgroundColor1 != -1)
return max(MaxY, 100);
else
return max(MaxY, 100) + scaleLength(60);
}
int gdioutput::getPageX() const {
int xlimit = 100;
for (auto &b : BI)
xlimit = max(b.xp + b.width, xlimit);
if (hideBG || backgroundColor1 != -1 || xlimit >= MaxX)
return max(MaxX, xlimit);
else
return max(MaxX, xlimit) + scaleLength(60);
}
int gdioutput::popupMenu(int x, int y, const vector<pair<wstring, int>> &menuItems) const {
POINT pt;
pt.x = x;
pt.y = y;
ClientToScreen(getHWNDTarget(), &pt);
HMENU hm = CreatePopupMenu();
for (auto &me : menuItems) {
if (me.first.empty())
AppendMenu(hm, MF_SEPARATOR, me.second, L"");
else
AppendMenu(hm, MF_STRING, me.second, lang.tl(me.first).c_str());
}
int res = TrackPopupMenuEx(hm, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_NONOTIFY,
pt.x, pt.y, getHWNDTarget(), nullptr);
DestroyMenu(hm);
return res;
}