/************************************************************************
MeOS - Orienteering Software
Copyright (C) 2009-2018 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 .
Melin Software HB - software@melin.nu - www.melin.nu
Eksoppsvägen 16, SE-75646 UPPSALA, Sweden
************************************************************************/
#include "stdafx.h"
#include "Table.h"
#include "gdioutput.h"
#include "meos_util.h"
#include
#include
#include
#include "oEvent.h"
#include "localizer.h"
#include
#include "gdiconstants.h"
#include "meosexception.h"
#include "recorder.h"
#include "oDataContainer.h"
extern HINSTANCE hInst;
const char *tId="_TABLE_SEL";
const Table *TableSortIndex::table = 0;
int Table::uniqueId = 1;
Table::Table(oEvent *oe_, int rowH,
const wstring &name, const string &iName)
{
id = uniqueId++;
commandLock = false;
oe=oe_;
tableName=name;
internalName = iName;
nTitles=0;
PrevSort=-1;
baseRowHeight=rowH;
rowHeight = 0;
highRow=-1;
highCol=-1;
colSelected=-1;
editRow=-1;
editCol=-1;
drawFilterLabel=false;
currentSortColumn=-1;
hEdit=0;
hdcCompatible=0;
hbmStored=0;
hdcCompatibleCell = 0;
hbmStoredCell = 0;
partialCell = false;
startSelect = false;
ownerCounter = 0;
clearCellSelection(0);
tableProp = -1;
dataPointer = -1;
clearOnHide = true;
doAutoSelectColumns = true;
generator = 0;
generatorPtr = 0;
}
Table::~Table(void)
{
assert(ownerCounter == 0);
if (hEdit)
DestroyWindow(hEdit);
}
void Table::releaseOwnership() {
ownerCounter--;
if (ownerCounter == 0)
delete this;
}
void Table::clearCellSelection(gdioutput *gdi) {
upperRow = -1;
lowerRow = -1;
upperCol = -1;
lowerCol = -1;
if (gdi) {
HDC hDC = GetDC(gdi->getHWNDTarget());
clearSelectionBitmap(gdi, hDC);
ReleaseDC(gdi->getHWNDTarget(), hDC);
}
}
int Table::addColumn(const string &Title, int width, bool isnum, bool formatRight) {
ColInfo ri;
wcscpy_s(ri.name, lang.tl(Title).c_str());
ri.baseWidth = width;
ri.width = 0;
ri.padWidthZeroSort = 0;
ri.isnumeric = isnum;
ri.formatRight = formatRight;
Titles.push_back(ri);
columns.push_back(nTitles);
nTitles++;
return Titles.size()-1;
}
int Table::addColumnPaddedSort(const string &title, int width, int padding, bool formatRight) {
ColInfo ri;
wcscpy_s(ri.name, lang.tl(title).c_str());
ri.baseWidth = width;
ri.width = 0;
ri.padWidthZeroSort = padding;
ri.isnumeric = false;
ri.formatRight = formatRight;
Titles.push_back(ri);
columns.push_back(nTitles);
nTitles++;
return Titles.size()-1;
}
void Table::moveColumn(int src, int target)
{
if (src==target)
return;
vector::iterator it_s=find(columns.begin(), columns.end(), src);
vector::iterator it_t=find(columns.begin(), columns.end(), target);
if (it_s!=columns.end()) {
if (it_s0 && idToRow.lookup(rowId, ix)) {
dataPointer = ix;
return;
}
TableRow tr(nTitles, object);
tr.height=rowHeight;
tr.id = rowId;
TableSortIndex tsi;
if (Data.empty()) {
sortIndex.clear();
tsi.index=0;
sortIndex.push_back(tsi);
tsi.index=1;
sortIndex.push_back(tsi);
tsi.index=2;
Data.resize(2, tr);
for (unsigned i=0;i0)
idToRow[rowId]=Data.size();
dataPointer = Data.size();
sortIndex.push_back(tsi);
Data.push_back(tr);
}
void Table::set(int column, oBase &owner, int id, const wstring &data, bool canEdit, CellType type)
{
if (dataPointer >= Data.size() || dataPointer<2)
throw std::exception("Internal table error: wrong data pointer");
TableRow &row=Data[dataPointer];
TableCell &cell=row.cells[column];
cell.contents=data;
cell.owner=&owner;
cell.id=id;
cell.canEdit=canEdit;
cell.type=type;
}
void Table::filter(int col, const wstring &filt, bool forceFilter)
{
const wstring &oldFilter=Titles[col].filter;
vector baseIndex;
if (filt==oldFilter && (!forceFilter || filt.empty()))
return;
else if (wcsncmp(oldFilter.c_str(), filt.c_str(), oldFilter.length())==0) {
if (sortIndex.empty())
return;
//Filter more...
baseIndex.resize(2);
baseIndex[0]=sortIndex[0];
baseIndex[1]=sortIndex[1];
swap(baseIndex, sortIndex);
Titles[col].filter=filt;
}
else {
//Filter less -> refilter all!
Titles[col].filter=filt;
sortIndex.resize(Data.size());
for (size_t k=0; k '9'))
i++;
int key = _wtoi(str + i);
Data[sortIndex[k].index].intKey = key;
if (key == 0)
Data[sortIndex[k].index].key = Data[sortIndex[k].index].cells[col].contents;
}
if (hasDeci) { // Times etc.
for(size_t k=2; k '9'))
i++;
int key = 0;
while (str[i] >= '0' && str[i] <= '9') {
key = key * 10 + (str[i] - '0');
i++;
}
if (str[i] == ':' || str[i]==',' || str[i] == '.' || (str[i] == '-' && key != 0)) {
bool valid = true;
for (int j = 1; j <= 4; j++) {
if (valid && str[i+j] >= '0' && str[i+j] <= '9')
key = key * 10 + (str[i+j] - '0');
else {
key *= 10;
valid = false;
}
}
}
else {
key *= 10000;
}
Data[sortIndex[k].index].intKey = key;
if (key == 0)
Data[sortIndex[k].index].key = Data[sortIndex[k].index].cells[col].contents;
}
}
}
else {
if (Titles[col].padWidthZeroSort) {
for (size_t k=2; k 1) {
intKey |= unsigned(strBuff[1]);
}
}
}
else {
for (size_t k=2; k 1) {
intKey |= unsigned(strBuff[1])<<8;
//intKey |= unsigned(strBuff[2]);
}
}
else {
intKey = 0xFEFEFEFE;
}
}
}
}
assert(TableSortIndex::table == 0);
TableSortIndex::table = this;
//DWORD sStart = GetTickCount();
std::stable_sort(sortIndex.begin()+2, sortIndex.end());
//DWORD sEnd = GetTickCount();
//string st = itos(sEnd-sStart);
TableSortIndex::table = 0;
PrevSort=col;
if (reverse)
std::reverse(sortIndex.begin()+2, sortIndex.end());
}
else {
std::reverse(sortIndex.begin()+2, sortIndex.end());
if (PrevSort==col)
PrevSort=-(10+col);
else
PrevSort=col;
}
}
int TablesCB(gdioutput *gdi, int type, void *data)
{
if (type!=GUI_LINK || gdi->Tables.empty())
return 0;
TableInfo &tai=gdi->Tables.front();
Table *t=tai.table;
TextInfo *ti=(TextInfo *)data;
if (ti->id.substr(0,4)=="sort"){
int col=atoi(ti->id.substr(4).c_str());
t->sort(col);
}
gdi->refresh();
//gdi->Restore();
//t->Render(*gdi);
//*/
return 0;
}
void Table::getDimension(gdioutput &gdi, int &dx, int &dy, bool filteredResult) const
{
rowHeight = gdi.scaleLength(baseRowHeight);
if (filteredResult)
dy = rowHeight * (1+sortIndex.size());
else
dy = rowHeight * (1+max(2,Data.size()));
dx=1;
for(size_t i=0;i=rc.right || yrc.bottom)
row=-1;
bool ret = false;
if (startSelect) {
int c = getColumn(x, true);
if (c != -1)
upperCol = c;
c = getRow(y, true);
if (c != -1 && c>=0) {
upperRow = max(c, 2);
}
HDC hDC=GetDC(hWnd);
if (unsigned(highRow)=0 && col>=0) {
POINT pt = {x, y};
ClientToScreen(hWnd, &pt);
HWND hUnder = WindowFromPoint(pt);
if (hUnder == hWnd) {
//int index=sortIndex[row].index;
TableRow &trow=Data[row];
TableCell &cell=trow.cells[col];
if (highRow!=row || highCol!=col) {
HDC hDC=GetDC(hWnd);
if (unsigned(highRow)= 2) {
DWORD c;
if (cell.canEdit)
c=RGB(240, 240, 150);
else
c=RGB(240, 200, 140);
highlightCell(hDC, gdi, cell, c, 0,0);
}
ReleaseDC(hWnd, hDC);
SetCapture(hWnd);
highCol=col;
highRow=row;
}
ret = true;
}
}
if (ret)
return true;
if (unsigned(highRow)(data);
Table &t = gdi->getTable();
t.selection(*gdi, lbi.text, lbi.data);
}
return 0;
}
void Table::selection(gdioutput &gdi, const wstring &text, int data) {
if (size_t(selectionRow) >= Data.size() || size_t(selectionCol) >= Titles.size())
throw std::exception("Index out of bounds.");
TableCell &cell = Data[selectionRow].cells[selectionCol];
int id = Data[selectionRow].id;
cell.owner->inputData(cell.id, text, data, cell.contents, false);
reloadRow(id);
RECT rc;
getRowRect(selectionRow, rc);
InvalidateRect(gdi.getHWNDTarget(), &rc, false);
}
#ifndef MEOSDB
bool Table::keyCommand(gdioutput &gdi, KeyCommandCode code) {
if (commandLock)
return false;
commandLock = true;
try {
if (code == KC_COPY && hEdit == 0)
exportClipboard(gdi);
else if (code == KC_PASTE && hEdit == 0) {
importClipboard(gdi);
}else if (code == KC_DELETE && hEdit == 0) {
deleteSelection(gdi);
}
else if (code == KC_REFRESH) {
gdi.setWaitCursor(true);
update();
autoAdjust(gdi);
gdi.refresh();
}
else if (code == KC_INSERT) {
insertRow(gdi);
gdi.refresh();
}
else if (code == KC_PRINT) {
gdioutput gdiPrint("temp", gdi.getScale());
gdiPrint.clearPage(false);
gdiPrint.print(getEvent(), this);
}
}
catch (...) {
commandLock = false;
throw;
}
commandLock = false;
return false;
}
#endif
bool Table::deleteSelection(gdioutput &gdi) {
int r1, r2;
getRowRange(r1, r2);
if (r1 != -1 && r2 != -1 && r1<=r2) {
if (!gdi.ask(L"Vill du radera X rader från tabellen?#" + itow(r2-r1+1)))
return false;
gdi.setWaitCursor(true);
int failed = deleteRows(r1, r2);
gdi.refresh();
gdi.setWaitCursor(false);
if (failed > 0)
gdi.alert("X rader kunde inte raderas.#" + itos(failed));
}
return true;
}
void Table::hide(gdioutput &gdi) {
try {
destroyEditControl(gdi);
}
catch (meosException &ex) {
gdi.alert(ex.wwhat());
}
catch (std::exception &ex) {
gdi.alert(ex.what());
}
clearCellSelection(0);
ReleaseCapture();
if (clearOnHide) {
Data.clear();
sortIndex.clear();
idToRow.clear();
clear();
}
}
void Table::clear() {
Data.clear();
sortIndex.clear();
idToRow.clear();
}
bool Table::destroyEditControl(gdioutput &gdi) {
colSelected=-1;
if (hEdit) {
try {
if (!enter(gdi))
return false;
}
catch (std::exception &) {
if (hEdit) {
DestroyWindow(hEdit);
hEdit=0;
}
throw;
}
if (hEdit) {
DestroyWindow(hEdit);
hEdit=0;
}
}
gdi.removeControl(tId);
if (drawFilterLabel) {
drawFilterLabel=false;
gdi.refresh();
}
return true;
}
bool Table::mouseLeftDown(gdioutput &gdi, int x, int y) {
partialCell = true;
clearCellSelection(&gdi);
if (!destroyEditControl(gdi))
return false;
if (highRow==0) {
colSelected=highCol;
startX=x;
startY=y;
//sort(highCol);
//gdi.refresh();
//mouseMove(gdi, x, y);
}
else if (highRow==1) {
//filter(highCol, "lots");
RECT rc=Data[1].cells[columns[0]].absPos;
//rc.right=rc.left+tableWidth;
editRow=highRow;
editCol=highCol;
hEdit=CreateWindowEx(0, L"EDIT", Titles[highCol].filter.c_str(),
WS_TABSTOP|WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL|WS_BORDER,
rc.left+105, rc.top, tableWidth-105, (rc.bottom-rc.top-1), gdi.getHWNDTarget(),
0, hInst, 0);
drawFilterLabel=true;
SendMessage(hEdit, EM_SETSEL, 0, -1);
SetFocus(hEdit);
SendMessage(hEdit, WM_SETFONT, (WPARAM) gdi.getGUIFont(), 0);
gdi.refresh();
}
else {
SetFocus(gdi.getHWNDTarget());
SetCapture(gdi.getHWNDTarget());
lowerCol = getColumn(x);
lowerRow = getRow(y);
startSelect = true;
}
return false;
}
bool Table::mouseLeftDblClick(gdioutput &gdi, int x, int y)
{
clearCellSelection(&gdi);
if (!destroyEditControl(gdi))
return false;
if (unsigned(highRow)>=Data.size() || unsigned(highCol)>=Titles.size()) {
return false;
}
if (highRow != 0 && highRow != 1) {
if (editCell(gdi, highRow, highCol))
return true;
}
return false;
}
bool Table::editCell(gdioutput &gdi, int row, int col) {
TableCell &cell = Data[row].cells[col];
if (cell.type == cellAction) {
ReleaseCapture();
gdi.makeEvent("CellAction", internalName, cell.id, cell.owner ? cell.owner->getId() : 0, false);
return true;
}
if (!cell.canEdit) {
MessageBeep(-1);
return true;
}
ReleaseCapture();
editRow = row;
editCol = col;
RECT &rc=cell.absPos;
if (cell.type == cellSelection || cell.type == cellCombo) {
selectionRow = row;
selectionCol = col;
vector< pair > out;
size_t selected = 0;
cell.owner->fillInput(cell.id, out, selected);
int width = 40;
for (size_t k = 0; k(width, 8*out[k].first.length());
if (cell.type == cellSelection) {
gdi.addSelection(rc.left+gdi.OffsetX, rc.top+gdi.OffsetY, tId,
max(int((rc.right-rc.left+1)/gdi.scale), width), (rc.bottom-rc.top)*10,
tblSelectionCB).setExtra(id);
}
else {
gdi.addCombo(rc.left+gdi.OffsetX, rc.top+gdi.OffsetY, tId,
max(int((rc.right-rc.left+1)/gdi.scale), width), (rc.bottom-rc.top)*10,
tblSelectionCB).setExtra(id);
}
gdi.addItem(tId, out);
gdi.selectItemByData(tId, selected);
return true;
}
else if (cell.type==cellEdit) {
hEdit=CreateWindowEx(0, L"EDIT", cell.contents.c_str(),
WS_TABSTOP|WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL|WS_BORDER,
rc.left, rc.top, rc.right-rc.left+1, (rc.bottom-rc.top), gdi.getHWNDTarget(),
0, hInst, 0);
SendMessage(hEdit, EM_SETSEL, 0, -1);
SetFocus(hEdit);
SendMessage(hEdit, WM_SETFONT, (WPARAM) gdi.getGUIFont(), 0);
return true;
}
return false;
}
RGBQUAD RGBA(BYTE r, BYTE g, BYTE b, BYTE a) {
RGBQUAD q = {b,g,r,a};
return q;
}
RGBQUAD RGBA(DWORD c) {
RGBQUAD q = {GetBValue(c),GetGValue(c),GetRValue(c),0};
return q;
}
RGBQUAD transform(RGBQUAD src, double scale) {
src.rgbRed = BYTE(min(src.rgbRed * scale, 255.0));
src.rgbGreen = BYTE(min(src.rgbGreen * scale, 255.0));
src.rgbBlue = BYTE(min(src.rgbBlue * scale, 255.0));
return src;
}
void gradRect(HDC hDC, int left, int top, int right, int bottom,
bool horizontal, RGBQUAD c1, RGBQUAD c2) {
TRIVERTEX vert[2];
vert [0] .x = left;
vert [0] .y = top;
vert [0] .Red = 0xff00 & (DWORD(c1.rgbRed)<<8);
vert [0] .Green = 0xff00 & (DWORD(c1.rgbGreen)<<8);
vert [0] .Blue = 0xff00 & (DWORD(c1.rgbBlue)<<8);
vert [0] .Alpha = 0xff00 & (DWORD(c1.rgbReserved)<<8);
vert [1] .x = right;
vert [1] .y = bottom;
vert [1] .Red = 0xff00 & (DWORD(c2.rgbRed)<<8);
vert [1] .Green = 0xff00 & (DWORD(c2.rgbGreen)<<8);
vert [1] .Blue = 0xff00 & (DWORD(c2.rgbBlue)<<8);
vert [1] .Alpha = 0xff00 & (DWORD(c2.rgbReserved)<<8);
GRADIENT_RECT gr[1];
gr[0].UpperLeft=0;
gr[0].LowerRight=1;
if (horizontal)
GradientFill(hDC,vert, 2, gr, 1,GRADIENT_FILL_RECT_H);
else
GradientFill(hDC,vert, 2, gr, 1,GRADIENT_FILL_RECT_V);
}
void Table::initEmpty() {
if (Data.empty()) {
addRow(0,0);
Data.resize(2, TableRow(nTitles,0));
sortIndex.resize(2);
}
}
void drawSymbol(gdioutput &gdi, HDC hDC, int height,
const TextInfo &ti, const wstring &symbol, bool highLight) {
int cx = ti.xp - gdi.GetOffsetX() + ti.xlimit/2;
int cy = ti.yp - gdi.GetOffsetY() + height/2 - 2;
int h = int(height * 0.4);
int w = h/3;
h-=2;
RGBQUAD a,b;
if (highLight) {
a = RGBA(32, 114, 14, 0);
b = RGBA(64, 211, 44, 0);
}
else {
a = RGBA(32, 114, 114, 0);
b = RGBA(84, 231, 64, 0);
}
gradRect(hDC, cx-w, cy-h, cx+w, cy+h, false, a, b);
gradRect(hDC, cx-h, cy-w, cx+h, cy+w, false, a, b);
POINT pt;
MoveToEx(hDC, cx-w, cy-h, &pt);
SelectObject(hDC, GetStockObject(DC_PEN));
SetDCPenColor(hDC, RGB(14, 80, 7));
LineTo(hDC, cx+w, cy-h);
LineTo(hDC, cx+w, cy-w);
LineTo(hDC, cx+h, cy-w);
LineTo(hDC, cx+h, cy+w);
LineTo(hDC, cx+w, cy+w);
LineTo(hDC, cx+w, cy+h);
LineTo(hDC, cx-w, cy+h);
LineTo(hDC, cx-w, cy+w);
LineTo(hDC, cx-h, cy+w);
LineTo(hDC, cx-h, cy-w);
LineTo(hDC, cx-w, cy-w);
LineTo(hDC, cx-w, cy-h);
}
void Table::highlightCell(HDC hDC, gdioutput &gdi, const TableCell &cell, DWORD color, int dx, int dy)
{
SelectObject(hDC, GetStockObject(DC_BRUSH));
SelectObject(hDC, GetStockObject(NULL_PEN));
SetDCBrushColor(hDC, color);
RECT rc=cell.absPos;
rc.left+=dx;
rc.right+=dx;
rc.top+=dy;
rc.bottom+=dy;
Rectangle(hDC, rc.left+1, rc.top,
rc.right+2, rc.bottom);
TextInfo ti;
if (cell.type == cellAction && cell.contents[0] == '@') {
ti.xp = rc.left + gdi.OffsetX;
ti.yp = rc.top + gdi.OffsetY;
ti.xlimit = rc.right - rc.left;
drawSymbol(gdi, hDC, rowHeight, ti, cell.contents, true);
//SetDCPenColor(hDC, RGB(190,190,190));
}
else {
gdi.formatString(ti, hDC);
SetBkMode(hDC, TRANSPARENT);
rc.left+=4;
rc.top+=2;
DrawText(hDC, cell.contents.c_str(), -1, &rc, DT_LEFT|DT_NOPREFIX);
}
}
void Table::draw(gdioutput &gdi, HDC hDC, int dx, int dy, const RECT &screen)
{
gdi.resetLast();
initEmpty();
updateDimension(gdi);
table_xp=dx-gdi.OffsetX;
table_yp=dy-gdi.OffsetY;
if (currentSortColumn==-1)
sort(0);
int wi, he;
getDimension(gdi, wi, he, true);
tableWidth=wi;
tableHeight=he;
//Find first and last row
int yreltop=(screen.top-(dy-gdi.OffsetY)-rowHeight+1)/rowHeight;
int yrelbottom=(screen.bottom-(dy-gdi.OffsetY)-1)/rowHeight;
int firstRow=max(yreltop,0);
int lastRow=min(yrelbottom, int(sortIndex.size()));
int firstCol=0;
int lastCol=columns.size();
xpos.resize(columns.size()+1);
xpos[0]=dx-gdi.OffsetX;
for (size_t i=1;i<=columns.size();i++)
xpos[i]=xpos[i-1]+Titles[columns[i-1]].width+1;
//Find first and last visible column
while(firstCol0 && xpos[lastCol-1]>screen.right)
lastCol--;
SelectObject(hDC, GetStockObject(DC_BRUSH));
SelectObject(hDC, GetStockObject(DC_PEN));
SetDCPenColor(hDC, RGB(190,190,190));
SetDCBrushColor(hDC, RGB(60,25,150));
//Title
TextInfo ti;
gdi.formatString(ti, hDC);
HLS hls;
hls.RGBtoHLS(GetSysColor(COLOR_ACTIVECAPTION));
hls.lighten(2);
hls.saturation = min(90,hls.saturation);
DWORD lightColor = hls.HLStoRGB();
if (firstRow==0 && lastRow>=0) {
//Rectangle(hDC, dx-gdi.OffsetX, dy-gdi.OffsetY,
// dx+tableWidth-gdi.OffsetX, dy+rowHeight-gdi.OffsetY);
RGBQUAD c1 = RGBA(GetSysColor(COLOR_ACTIVECAPTION));
gradRect(hDC, dx-gdi.OffsetX, dy-gdi.OffsetY,
dx+tableWidth-gdi.OffsetX, dy+rowHeight-gdi.OffsetY, false,
c1, transform(c1, 0.7));
RECT rc;
rc.left=dx+5-gdi.OffsetX;
rc.right=rc.left+gdi.scaleLength(150);
rc.top=dy+3-gdi.OffsetY;
rc.bottom=rc.top+rowHeight;
ti.format = 1;
gdi.formatString(ti, hDC);
SetTextColor(hDC, RGB(255,255,255));
DrawText(hDC, lang.tl(tableName).c_str(), -1, &rc, DT_LEFT|DT_NOPREFIX);
DrawText(hDC, lang.tl(tableName).c_str(), -1, &rc, DT_CALCRECT|DT_NOPREFIX);
ti.format = 0;
gdi.formatString(ti, hDC);
SetTextColor(hDC, RGB(255,255,255));
wchar_t bf[256];
wstring info = lang.tl(wstring(L"sortering: X, antal rader: Y#") + Titles[currentSortColumn].name + L"#" + itow(sortIndex.size()-2));
//sprintf_s(bf, .c_str(),
// Titles[currentSortColumn].name, int(sortIndex.size())-2);
rc.left=rc.right+30;
rc.right=dx + tableWidth - gdi.OffsetX - 10;
DrawText(hDC, info.c_str(), info.length(), &rc, DT_LEFT|DT_NOPREFIX);
SetTextColor(hDC, RGB(0,0,0));
if (drawFilterLabel) {
SetDCBrushColor(hDC, RGB(100,200,100));
Rectangle(hDC, dx-gdi.OffsetX, dy+2*rowHeight-gdi.OffsetY,
dx+tableWidth-gdi.OffsetX, dy+3*rowHeight-gdi.OffsetY);
rc.left=dx+5-gdi.OffsetX;
rc.right=rc.left+100;
rc.top=dy+3+2*rowHeight-gdi.OffsetY;
rc.bottom=rc.top+rowHeight;
wchar_t tbf[2];
tbf[0]=Titles[editCol].name[0];
tbf[1]=0;
CharLower(tbf);
wstring filter = lang.tl("Urval %c%s: ");
swprintf_s(bf, filter.c_str(), tbf[0],
Titles[editCol].name+1, int(sortIndex.size())-2);
DrawText(hDC, bf, -1, &rc, DT_RIGHT|DT_NOPREFIX);
}
}
int yp;
yp=dy+rowHeight;
if (firstRow<=2 && lastRow>=1) {
wstring filterText = lang.tl("Urval...");
for (int k=firstCol;k(lastRow + margin, sortIndex.size());
for (int k1 = rStart; k1 < rEnd; k1++){
int yp = dy + rowHeight*(k1+1);
TableRow &tr = Data[sortIndex[k1].index];
for(size_t k=0;k= sortIndex.size())
throw std::exception("Index out of range");
const TableRow &tr = Data[sortIndex[row].index];
if ( size_t(col) >= columns.size())
throw std::exception("Index out of range");
col = columns[col];
return *((TableCell *)&tr.cells[col]);
}
void Table::clearSelectionBitmap(gdioutput *gdi, HDC hDC)
{
if (gdi)
restoreSelection(*gdi, hDC);
if (hdcCompatibleCell) {
DeleteDC(hdcCompatibleCell);
hdcCompatibleCell = 0;
}
if (hbmStoredCell) {
DeleteObject(hbmStoredCell);
hbmStoredCell = 0;
}
}
void Table::restoreSelection(gdioutput &gdi, HDC hDC) {
if (partialCell) {
RECT rc = lastCell;
rc.left -= gdi.OffsetX;
rc.right -= gdi.OffsetX;
rc.top -= gdi.OffsetY;
rc.bottom -= gdi.OffsetY;
InvalidateRect(gdi.getHWNDTarget(), &rc, false);
partialCell = false;
}
else if (hdcCompatibleCell) {
//Restore bitmap
int cx = lastCell.right - lastCell.left + 1;
int cy = lastCell.bottom - lastCell.top + 1;
int x = lastCell.left - 1 - gdi.OffsetX;
int y = lastCell.top - 1 - gdi.OffsetY;
BitBlt(hDC, x, y, cx, cy, hdcCompatibleCell, 0, 0, SRCCOPY);
}
}
void Table::drawSelection(gdioutput &gdi, HDC hDC, bool forceDraw) {
bool modified = false;
if (lowerColOld != lowerCol || upperCol != upperColOld ||
lowerRow != lowerRowOld || upperRow != upperRowOld) {
modified = true;
restoreSelection(gdi, hDC);
}
if (lowerCol != -1 && upperCol != -1 &&
lowerRow != -1 && upperRow != -1 &&
(forceDraw || modified)) {
TableCell &c1 = Data[lowerRow].cells[lowerCol];
TableCell &c2 = Data[upperRow].cells[upperCol];
RECT rc;
rc.top = min(c1.absPos.top, c2.absPos.top) + gdi.OffsetY;
rc.left = min(c1.absPos.left, c2.absPos.left) + gdi.OffsetX;
rc.right = max(c1.absPos.right, c2.absPos.right) + 1 + gdi.OffsetX;
rc.bottom = max(c1.absPos.bottom, c2.absPos.bottom) + gdi.OffsetY;
if (modified) {
int cx=rc.right-rc.left + 1;
int cy=rc.bottom-rc.top + 1;
clearSelectionBitmap(&gdi, hDC);
lastCell = rc;
int x = lastCell.left - 1 - gdi.OffsetX;
int y = lastCell.top - 1 - gdi.OffsetY;
int maxX, maxY;
gdi.getTargetDimension(maxX, maxY);
if (x<=0 || y<=0 || (x+cx)>=maxX || (y+cy)>=maxY) {
partialCell = true;
}
else {
hdcCompatibleCell = CreateCompatibleDC(hDC);
hbmStoredCell = CreateCompatibleBitmap(hDC, cx, cy);
// Select the bitmaps into the compatible DC.
SelectObject(hdcCompatibleCell, hbmStoredCell);
BitBlt(hdcCompatibleCell, 0, 0, cx, cy, hDC, x, y, SRCCOPY);
partialCell = false;
}
}
SelectObject(hDC, GetStockObject(NULL_BRUSH));
SelectObject(hDC, GetStockObject(DC_PEN));
SetDCPenColor(hDC, RGB(0,0, 128));
Rectangle(hDC, rc.left - gdi.OffsetX, rc.top - gdi.OffsetY,
rc.right - gdi.OffsetX, rc.bottom - gdi.OffsetY);
lowerColOld = lowerCol;
upperColOld = upperCol;
lowerRowOld = lowerRow;
upperRowOld = upperRow;
}
}
void Table::scrollToCell(gdioutput &gdi, int row, int col) {
if (size_t(row) >= Data.size() || size_t(col) >= Data[row].cells.size())
return;
const RECT &rc = Data[row].cells[col].absPos;
int maxX, maxY;
gdi.getTargetDimension(maxX, maxY);
int xo = gdi.OffsetX;
int yo = gdi.OffsetY;
if (rc.right > maxX) {
xo = gdi.OffsetX + (rc.right - maxX) + 10;
}
else if (rc.left < 0) {
xo = gdi.OffsetX + rc.left - 10;
}
if (rc.bottom > maxY) {
yo = gdi.OffsetY + (rc.bottom - maxY) + 10;
}
else if (rc.top < 0) {
yo = gdi.OffsetY + rc.top - 10;
}
if (xo != gdi.OffsetX || yo != gdi.OffsetY) {
gdi.setOffset(xo, yo, true);
//gdi.refreshFast();
//Sleep(300);
}
}
void Table::print(gdioutput &gdi, HDC hDC, int dx, int dy)
{
vector widths(columns.size());
vector skip(columns.size(), true);
int rh = 0;
for (size_t j=0;j(widths[j], ti.textRect.right-ti.textRect.left);
rh = max(rh, ti.textRect.bottom-ti.textRect.top);
}
const int extra = 10;
for (size_t j=0;j(widths[j],
int(1.1*(ti.textRect.right-ti.textRect.left)+extra));
rh = max(rh, ti.textRect.bottom-ti.textRect.top);
}
}
}
rh = int(rh*1.3);
vector adj_xp(columns.size());
int w = widths[0];
for (size_t j=1;j= Data.size() || size_t(editCol) >= Data[editRow].cells.size())
throw std::exception("Index out of bounds");
wstring output;
TableCell &cell=Data[editRow].cells[editCol];
cell.owner->inputData(cell.id, bf, 0, output, false);
cell.contents = output;
if (hEdit != 0)
DestroyWindow(hEdit);
hEdit=0;
reloadRow(Data[editRow].id);
RECT rc;
getRowRect(editRow, rc);
InvalidateRect(gdi.getHWNDTarget(), &rc, false);
}
const wstring &Table::getTableText(gdioutput &gdi, int editRow, int editCol) {
if (size_t(editRow) >= Data.size() || size_t(editCol) >= Data[editRow].cells.size())
throw std::exception("Index out of bounds");
string output;
TableCell &cell=Data[editRow].cells[editCol];
return cell.contents;
}
bool Table::enter(gdioutput &gdi)
{
if (hEdit) {
if (unsigned(editRow)=2) {
string cmd;
if (gdi.getRecorder().recording())
cmd = "setTableText(" + itos(editRow) + ", " + itos(editCol) + ", \"" + gdi.narrow(bf) + "\");";
setTableText(gdi, editRow, editCol, bf);
gdi.getRecorder().record(cmd);
return true;
}
else if (editRow==1) {//Filter
filter(editCol, bf);
DestroyWindow(hEdit);
hEdit=0;
drawFilterLabel = false;
gdi.refresh();
return true;
}
}
}
else if (gdi.hasField(tId)) {
ListBoxInfo lbi;
gdi.getSelectedItem(tId, lbi);
if (lbi.isCombo()) {
if (size_t(selectionRow) < Data.size() && size_t(selectionCol) < Titles.size()) {
selection(gdi, lbi.text, lbi.data);
}
}
}
return false;
}
void Table::escape(gdioutput &gdi)
{
if (hEdit) {
DestroyWindow(hEdit);
hEdit = 0;
}
gdi.removeControl(tId);
drawFilterLabel=false;
gdi.refresh();
}
bool Table::inputChange(gdioutput &gdi, HWND hdt)
{
if (hEdit==hdt) {
if (drawFilterLabel) {
wchar_t bf[256];
GetWindowText(hEdit, bf, 256);
filter(editCol, bf);
updateDimension(gdi);
gdi.refresh();
}
return true;
}
return false;
}
int Table::getColumn(int x, bool limit) const
{
if (columns.empty())
return -1;
if (x=int(sortIndex.size()))
r = sortIndex.size() - 1;
}
if (r>=0 && r= int(Data.size()))
throw std::exception("Index out of bounds.");
TableRow &row=Data[index];
oBase *obj = row.getObject();
if (obj) {
TableUpdateInfo tui;
tui.object = obj;
tui.id = rowId;
oe->generateTableData(internalName, *this, tui);
}
}
vector Table::getColumns() const
{
vector cols(Titles.size());
// Get selected columns
for (size_t k=0; k &sel)
{
vector newcols;
for (size_t k=0; k::const_iterator it = sel.begin();
while(it != sel.end()) {
if (count(newcols.begin(), newcols.end(), *it)==0)
newcols.push_back(*it);
++it;
}
swap(columns, newcols);
doAutoSelectColumns = false;
}
void Table::resetColumns()
{
columns.resize(Titles.size());
for (size_t k=0;kprepare(oe);
int oldSort = PrevSort;
Data.clear();
sortIndex.clear();
idToRow.clear();
clearCellSelection(0);
if (generator == 0) {
TableUpdateInfo tui;
oe->generateTableData(internalName, *this, tui);
}
else {
generator(*this, generatorPtr);
}
//Refilter all
for (size_t k=0;k";
txt = L"";
for (size_t k = row1; k<=size_t(row2); k++) {
if ( k >= sortIndex.size())
throw std::exception("Index out of range");
const TableRow &tr = Data[sortIndex[k].index];
html += L"";
for (size_t j = col1; j<= size_t(col2); j++) {
if ( j >= columns.size())
throw std::exception("Index out of range");
int col = columns[j];
const TableCell &cell = tr.cells[col];
html += L"| " + cell.contents + L" | ";
if (j == col1)
txt += cell.contents;
else
txt += L"\t" + cell.contents;
}
txt += L"\r\n";
html += L"
";
}
html += L"