#include "tableedit.h"
#include <commctrl.h>
#include <shlwapi.h>
#include "wutils.h"
#include "vector.h"
#include "plot.h"
/*************
* tableedit *
*************/
class tableedit{
struct tableaxis{
int flags; // Spalte: Bit 1:0 = Ausrichtung links(00),rechts(01),dezimal(10),Mitte(11)
// Bit 4 = Ausrichtungs-Override für Zeilen
// Bit 5 = Header-Spalte (grau)
// Bit 6 = Spalte klebt am linken/rechten Fensterrand
// Bit 7 = Spalte nicht editierbar
// Zeile: Bit 4 = Ausrichtungs-Override für Spalten (gewinnt über Bit 2 der Spalte)
// Bit 1:0 = Ausrichtung wie oben
// Bit 5 = Header-Zeile (grau, nicht editierbar)
// Bit 6 = Zeile klebt am oberen/unteren Fensterrand
// Bit 7 = Zeile nicht editierbar
int width; // ohne Rand
};
struct intpair{int a,b;};
tableedit(HWND);
~tableedit();
HWND wnd,edit;
WPARAM editindex;
WNDPROC orgeditproc;
static LRESULT CALLBACK editsubproc(HWND,UINT,WPARAM,LPARAM);
LRESULT editsubproc(UINT,WPARAM,LPARAM);
HFONT deffont,boldfont;
std::vector<tableaxis>cols,rows;
TE_ITEM*items; // Ausdehnung dieses Arrays je nach cols.length × rows.length!
TE_METRICS m; // Alles was keine Handles sind
bool initstorage(UINT,UINT);
bool insertcol(UINT,UINT=1);
bool insertrow(UINT,UINT=1);
bool deletecol(UINT,UINT=1);
bool deleterow(UINT,UINT=1);
WPARAM itemindex(UINT,UINT);
WPARAM itemindex(WPARAM);
void itemmove(TE_ITEM*&dst,const TE_ITEM*&src,WPARAM count) const;
void itemfill(TE_ITEM*&dst,WPARAM count) const;
void setedit(WPARAM);
void killedit(bool save=true);
bool okaytoedit(WPARAM);
static int gettext(char*,TCHAR*,int);
char hittest(POINT&); // Bit 0: column valid, Bit 1: row valid, Bit 2: horizontal divider, Bit 3: vertical divider
char getalign(UINT,UINT); // effektive Ausrichtung für die Zelle
bool getsplit(UINT,UINT);
COLORREF getbcolor(UINT,UINT);
COLORREF getfcolor(UINT,UINT);
int gettext(TE_ITEM&,TCHAR*,int);
void itemrect(UINT,UINT,Rect&);
void itempaint(HDC,UINT,UINT,Rect&,int,TE_ITEM&);
void itemsize(HDC,UINT,UINT,SIZE&,intpair&,TE_ITEM&);
void textsize(HDC,char*,SIZE&,intpair&,bool);
int space_nk(HDC,TCHAR*,int);
void onSize(int,int);
void onPaint(HDC);
void setfont(HFONT);
static LRESULT CALLBACK wndproc(HWND,UINT,WPARAM,LPARAM);
LRESULT wndproc(UINT,WPARAM,LPARAM);
public:
static void init();
};
void tableedit::init() {
static const WNDCLASS wc={
CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS|CS_PARENTDC,
wndproc,
0, // ClassExtra
sizeof(tableedit*),// WindowExtra
0, // hInstance
0, // hIcon
0,
(HBRUSH)(COLOR_WINDOW+1),
0,
TEXT("table")
};
RegisterClass(&wc);
}
LRESULT CALLBACK tableedit::editsubproc(HWND Wnd,UINT msg,WPARAM wParam,LPARAM lParam) {
tableedit*self=(tableedit*)GetWindowLongPtr(GetParent(Wnd),0);
LRESULT r=self->editsubproc(msg,wParam,lParam);
if (r) return r;
return CallWindowProc(self->orgeditproc,Wnd,msg,wParam,lParam);
}
LRESULT tableedit::editsubproc(UINT msg,WPARAM wParam,LPARAM lParam) {
switch (msg) {
case WM_KEYDOWN: switch (wParam) {
case VK_DELETE: // Zeile/Spalte einfügen/löschen per Tastatur
case VK_INSERT: if (GetKeyState(VK_SHIFT)<0 || GetKeyState(VK_CONTROL)<0) goto post; break;
case VK_LEFT: if (LOWORD(Edit_GetSel(edit))==0) goto post; break;
case VK_RIGHT: if (HIWORD(Edit_GetSel(edit))==Edit_GetTextLength(edit)) goto post; break;
case VK_DOWN:
case VK_UP: post: PostMessage(wnd,WM_COMMAND,MAKELONG(wParam,EN_VSCROLL),(LPARAM)edit); return 1;
}break;
}
return 0;
}
LRESULT CALLBACK tableedit::wndproc(HWND Wnd,UINT msg,WPARAM wParam,LPARAM lParam) {
tableedit*self=(tableedit*)GetWindowLongPtr(Wnd,0);
if (!self) {
self=new tableedit(Wnd);
SetWindowLong(Wnd,0,(LONG_PTR)self);
}
LRESULT r=self->wndproc(msg,wParam,lParam);
if (msg==WM_NCDESTROY) delete self;
return r;
}
void tableedit::onSize(int,int) {
}
void tableedit::onPaint(HDC dc) {
HFONT ofont=SelectFont(dc,deffont);
// Alle Items messen und Maxima ermitteln
std::vector<intpair>colw;
colw.resize(cols.size());
memset(colw.begin(),0,colw.size()*sizeof(intpair));
for each (tableaxis col in cols) col.width=m.minW;
TE_ITEM*itemp=items;
for (int j=0; j<(int)rows.size(); j++) {
tableaxis&row=rows[j];
row.width=m.minH;
for (int i=0; i<(int)cols.size(); i++) {
tableaxis&col=cols[i];
SIZE siz;
itemsize(dc,i,j,siz,colw[i],*itemp);
if (col.width<siz.cx) col.width=siz.cx; // Gesamtbreite
if (row.width<siz.cy) row.width=siz.cy; // Höhe
itemp++;
}
}
for (int i=0; i<(int)cols.size(); i++) {
tableaxis&col=cols[i];
int sum=colw[i].a+colw[i].b;
if (col.width<sum) col.width=sum; // Gesamtbreite
// Ab jetzt wird nur noch colw[i].b benutzt - und die breiteste Zahl zentriert
int space=col.width-sum;
colw[i].b+=space>>1; // Position nach links schieben
}
HPEN pen=CreatePen(PS_SOLID,m.borW,m.border);
HPEN open=SelectPen(dc,pen);
Rect r;
GetClientRect(wnd,&r);
HRGN hrgn=CreateRectRgn(r.left,r.top,r.right,r.bottom);
SelectClipRgn(dc,hrgn);
DeleteRgn(hrgn);
POINT pt[2];
// Ränder zeichnen
pt[0].y=r.top;
pt[1].y=r.bottom;
for (int x=0, i=0;; x+=cols[i++].width+m.padX+m.borW+m.padX) {
pt[1].x=pt[0].x=x;
Polyline(dc,pt,2);
if (i==cols.size()) break;
}
pt[0].x=r.left;
pt[1].x=r.right;
for (int y=0, j=0;; y+=rows[j++].width+m.padY+m.borW+m.padY) {
pt[1].y=pt[0].y=y;
Polyline(dc,pt,2);
if (j==rows.size()) break;
}
SelectPen(dc,open);
DeletePen(pen);
// Inhalte zeichnen
itemp=items;
for (int y=m.borW, h, j=0; j<(int)rows.size(); y+=h+m.padY+m.borW+m.padY,j++) {
h=rows[j].width;
for (int x=m.borW, w, i=0; i<(int)cols.size(); x+=w+m.padX+m.borW+m.padX,i++) {
w=cols[i].width;
SetRect(&r,x,y,x+w+m.padX+m.padX,y+h+m.padY+m.padY);
itempaint(dc,i,j,r,colw[i].b,*itemp);
itemp++;
}
}
SelectObject(dc,ofont);
}
int tableedit::gettext(char*s,TCHAR*buf,int buflen) {
if (!s) return 0;
return MultiByteToWideChar(CP_UTF8,0,s,strlen(s),buf,buflen);
}
int tableedit::gettext(TE_ITEM&item,TCHAR*buf,int buflen) {
if (item.s!=LPSTR_TEXTCALLBACKA) return gettext(item.s,buf,buflen);
return 0;
}
int tableedit::space_nk(HDC dc, TCHAR*buf, int l) {
TCHAR*p=StrRChr(buf,buf+l,m.DecimalChar); // Position des letzten Dezimaltrennzeichens
if (p) {
SIZE siz;
GetTextExtentPoint32(dc,p,buf+l-p,&siz); // Länge des Strings am Ende einschließlich Dezimaltrennzeichen
return siz.cx; // diesen Wert zurückgeben
}
return 0;
}
void tableedit::textsize(HDC dc, char*s, SIZE&sz, intpair&pair, bool split) {
TCHAR buf[256];
int l=gettext(s,buf,elemof(buf));
GetTextExtentPoint32(dc,buf,l,&sz);
if (split) { // "pair" nur bei Dezimalpunktausrichtung beachten
int b=space_nk(dc,buf,l);
int a=sz.cx-b;
if (pair.a<a) pair.a=a; // Größen maximieren
if (pair.b<b) pair.b=b;
}
}
void tableedit::itemsize(HDC dc, UINT x, UINT y, SIZE&sz, intpair&pair, TE_ITEM&item) {
sz.cx=sz.cy=0;
if (item.s) textsize(dc,item.s,sz,pair,getsplit(x,y));
}
bool tableedit::getsplit(UINT x, UINT y) {
int fx=cols[x].flags;
int fy=rows[y].flags;
if (fy&0x10) return (fy&3)==2;
return (fx&3)==2;
}
char tableedit::getalign(UINT x, UINT y) {
char fx=cols[x].flags;
char fy=rows[y].flags;
if (fy&0x10) fx=fy; // Override
fx&=3;
if (fx==2) fx=1; // TA_RIGHT vorbereiten
return fx<<1; // das ergibt nun TA_LEFT, TA_RIGHT und TA_CENTER
}
COLORREF tableedit::getfcolor(UINT x, UINT y) {
if (items[itemindex(x,y)].state&TEIS_ERROR) return m.fgError;
return GetSysColor(COLOR_WINDOWTEXT);
}
COLORREF tableedit::getbcolor(UINT x, UINT y) {
if (items[itemindex(x,y)].state&TEIS_ERROR) return m.bgError;
char fx=cols[x].flags;
char fy=rows[y].flags;
if ((fx|fy)&0x20) return m.border;
return GetSysColor(COLOR_WINDOW);
}
void tableedit::itemrect(UINT x, UINT y, Rect&r) { // ohne Border, aber mit Padding
r.right=0;
for (UINT i=0; i<=x; i++) {
r.left=r.right+m.borW;
r.right=r.left+m.padX+cols[i].width+m.padX;
}
r.bottom=0;
for (UINT j=0; j<=y; j++) {
r.top=r.bottom+m.borW;
r.bottom=r.top+m.padY+rows[j].width+m.padY;
}
}
void tableedit::itempaint(HDC dc, UINT x, UINT y, Rect&r, int xorg, TE_ITEM&item) {
if (item.s) {
char ali=getalign(x,y);
SetTextAlign(dc,ali);
TCHAR buf[256];
int l=gettext(item,buf,elemof(buf));
if (getsplit(x,y))
xorg=r.width()-m.padX-xorg+space_nk(dc,buf,l); // Ursprung nach rechts rücken, wenn Dezimaltrennzeichen vorhanden
else switch (ali) {
case TA_LEFT: xorg=m.padX; break;
case TA_RIGHT: xorg=r.width()-m.padX; break;
default: xorg=r.width()>>1;
}
SetTextColor(dc,getfcolor(x,y));
SetBkColor(dc,getbcolor(x,y)); // für Textausgabe
ExtTextOut(dc,r.left+xorg,r.top+m.padY,ETO_OPAQUE|ETO_CLIPPED,&r,buf,l,0);
}else{
NMCUSTOMDRAW cd;
cd.hdr.hwndFrom=wnd;
cd.hdr.idFrom=GetWindowLong(wnd,GWL_ID);
cd.hdr.code=NM_CUSTOMDRAW;
cd.dwDrawStage=CDDS_ITEM;
cd.hdc=dc;
CopyRect(&cd.rc,&r);
cd.dwItemSpec=MAKELONG(y,x);
cd.uItemState=item.state;
cd.lItemlParam=item.lParam;
SendMessage(GetParent(wnd),WM_NOTIFY,cd.hdr.idFrom,(LPARAM)&cd);
}
}
WPARAM tableedit::itemindex(WPARAM wParam) {
return itemindex(LOWORD(wParam),HIWORD(wParam));
}
WPARAM tableedit::itemindex(UINT x, UINT y) {
if (x>=cols.size()) return -1;
if (y>=rows.size()) return -1;
return (WPARAM)y*cols.size()+x;
}
bool tableedit::initstorage(UINT x, UINT y) {
if (items) {
int l=cols.size()*rows.size();
for (int i=0; i<l; i++) if (items[i].s) delete[] items[i].s;
delete[] items; items=0;
}
tableaxis def={m.defXStyle,m.minW};
cols.resize(x,def);
def.flags=m.defYStyle;
def.width=m.minH;
rows.resize(y,def);
WPARAM l=(WPARAM)x*y;
if (l) {
items=new TE_ITEM[l];
if (!items) return false;
TE_ITEM*itemp=items;
itemfill(itemp,l);
}
return true;
}
void tableedit::itemfill(TE_ITEM*&dst,WPARAM count) const{
memset(dst,0,count*sizeof(TE_ITEM));
dst+=count;
}
void tableedit::itemmove(TE_ITEM*&dst,const TE_ITEM*&src,WPARAM count) const{
memcpy(dst,src,count*sizeof(TE_ITEM));
dst+=count;
src+=count;
}
bool tableedit::insertcol(UINT ii, UINT mm) {
tableaxis defcol={m.defXStyle,m.minW};
cols.insert(&cols[ii],mm,defcol);
TE_ITEM*newitems=new TE_ITEM[(WPARAM)cols.size()*rows.size()];
if (!newitems) return false;
TE_ITEM*newitemp=newitems; // Ziel der Kopieraktion
const TE_ITEM*olditemp=items; // Quelle der Kopieraktion
for (int j=0; j<(int)rows.size(); j++) {
itemmove(newitemp,olditemp,ii);
itemfill(newitemp,mm); // Flache Kopie mit Einfügungen von Nullen
itemmove(newitemp,olditemp,cols.size()-(ii+mm));
}
olditemp=items;
items=newitems;
delete[] olditemp;
InvalidateRect(wnd,0,TRUE);
return true;
}
bool tableedit::deletecol(UINT ii, UINT mm) {
cols.erase(&cols[ii],&cols[ii+mm]);
TE_ITEM*newitems=new TE_ITEM[(WPARAM)cols.size()*rows.size()];
if (!newitems) return false;
TE_ITEM*newitemp=newitems; // Ziel der Kopieraktion
const TE_ITEM*olditemp=items; // Quelle der Kopieraktion
for (int j=0; j<(int)rows.size(); j++) {
itemmove(newitemp,olditemp,ii); // Flache Kopie
olditemp+=mm;
itemmove(newitemp,olditemp,cols.size()-ii);
}
olditemp=items;
items=newitems;
delete[] olditemp;
InvalidateRect(wnd,0,TRUE);
return true;
}
bool tableedit::insertrow(UINT jj, UINT nn) {
tableaxis defrow={m.defYStyle,m.minH};
rows.insert(&rows[jj],nn,defrow);
TE_ITEM*newitems=new TE_ITEM[(WPARAM)cols.size()*rows.size()];
if (!newitems) return false;
TE_ITEM*newitemp=newitems;
const TE_ITEM*olditemp=items;
itemmove(newitemp,olditemp,cols.size()*jj);
itemfill(newitemp,cols.size()*nn);
itemmove(newitemp,olditemp,cols.size()*(rows.size()-(jj+nn)));
olditemp=items;
items=newitems;
delete[] olditemp;
InvalidateRect(wnd,0,TRUE);
return true;
}
bool tableedit::deleterow(UINT jj, UINT nn) {
rows.erase(&rows[jj],&rows[jj+nn]);
TE_ITEM*newitems=new TE_ITEM[(WPARAM)cols.size()*rows.size()];
if (!newitems) return false;
TE_ITEM*newitemp=newitems;
const TE_ITEM*olditemp=items;
itemmove(newitemp,olditemp,cols.size()*jj);
olditemp+=cols.size()*nn;
itemmove(newitemp,olditemp,cols.size()*(rows.size()-jj));
olditemp=items;
items=newitems;
delete[] olditemp;
InvalidateRect(wnd,0,TRUE);
return true;
}
void tableedit::setfont(HFONT font) {
deffont=font;
if (boldfont) DeleteFont(boldfont);
boldfont=0;
if (font) {
LOGFONT fnt;
GetObject(font,sizeof fnt,&fnt);
fnt.lfWeight=700;
boldfont=CreateFontIndirect(&fnt);
}
}
char tableedit::hittest(POINT&pt) {
// pt-Input: Mauskoordinaten
// pt-Output: Tabellenkoordinaten (0..(cols/rows).size())
// Wenn Ausgabewert == rows/cols.size, dann Gültigkeitsbit nicht gesetzt.
// Das wäre ein Klick in den Freiraum, um neue Zeile/Spalten zu adressieren.
// Zurzeit ohne Scrollbar!!
int x=m.borW+m.mouE,y=m.borW+m.mouE;
UINT i=(UINT)-1,j=(UINT)-1;
char ret=0;
while(++i<cols.size()) {
x+=m.padX+cols[i].width+m.padX+m.borW;
if (pt.x<x) {
ret|=1;
if (pt.x>=x-m.borW-m.mouE-m.mouE) ret|=4; // Ost-West-Pfeil
break;
}
}
while(++j<rows.size()) {
y+=m.padY+rows[j].width+m.padY+m.borW;
if (pt.y<y) {
ret|=2;
if (pt.y>=y-m.borW-m.mouE-m.mouE) ret|=8; // Nord-Süd-Pfeil
break;
}
}
pt.x=i;
pt.y=j;
return ret;
}
bool tableedit::okaytoedit(WPARAM ii) {
UINT y=ii/cols.size();
if (y>=rows.size()) return false;
UINT x=ii%cols.size();
if (cols[x].flags&TEAS_NOEDIT) return false;
if (rows[y].flags&TEAS_NOEDIT) return false;
return true;
}
void tableedit::killedit(bool save) { // "edit" muss gültig sein!
if (save && SendMessage(edit,EM_GETMODIFY,0,0)) {
TE_ITEM&item=items[editindex];
TCHAR buf[256];
GetWindowText(edit,buf,elemof(buf));
if (item.s) delete[] item.s;
#ifdef UNICODE
int l=WideCharToMultiByte(CP_UTF8,0,buf,-1,0,0,0,0);
item.s=(char*)malloc(l);
WideCharToMultiByte(CP_UTF8,0,buf,-1,item.s,l,0,0);
#else
item.s=newstr(buf);
#endif
SendMessage(GetParent(wnd),WM_COMMAND,MAKELONG(GetWindowID(wnd),EN_CHANGE),(LPARAM)wnd);
}
DestroyWindow(edit); edit=0;
}
void tableedit::setedit(WPARAM ii) { // "edit" muss ungültig sein!
while (!okaytoedit(ii)) ii++;
editindex=ii;
UINT y=ii/cols.size();
UINT x=ii%cols.size();
if (y>=rows.size()) return;
Rect r;
itemrect(x,y,r);
TCHAR buf[256];
buf[gettext(items[ii],buf,elemof(buf)-1)]=0; // Hier wird's nullterminiert benötigt!
DWORD sty=WS_VISIBLE|WS_CHILD|ES_AUTOHSCROLL|ES_AUTOVSCROLL|WS_BORDER|ES_WANTRETURN;
char ali=getalign(x,y);
switch (ali) {
case 6: sty|=ES_CENTER; break;
case 0: break;
default: sty|=ES_RIGHT;
}
InflateRect(&r,-1,-1);
edit=CreateWindowEx(0,TEXT("Edit"),buf,sty,
r.left,r.top,r.width(),r.height(),
wnd,0,0,0);
orgeditproc=SubclassWindow(edit,editsubproc);
SetWindowFont(edit,deffont,FALSE);
Edit_SetSel(edit,0,-1);
SetFocus(edit);
}
LRESULT tableedit::wndproc(UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case TE_INITSTORAGE: return initstorage(LOWORD(wParam),HIWORD(wParam));
case TE_SETAXIS: {
std::vector<tableaxis>*axis=HIWORD(wParam)?&rows:&cols;
if (LOWORD(wParam)>=axis->size()) return FALSE;
(axis->operator[](LOWORD(wParam))).flags=(int)lParam;
}return TRUE;
case TE_SETTEXT: {
int i=itemindex(wParam); if (i<0) return FALSE;
int l=strlen((char*)lParam)+1;
char*p=(char*)malloc(l);
if (!p) return FALSE;
memcpy(p,(char*)lParam,l);
delete[] items[i].s;
items[i].s=p;
InvalidateRect(wnd,NULL,TRUE);
}return TRUE;
case TE_GETTEXT: {
int i=itemindex(wParam); if (i<0) return FALSE;
strcpy((char*)lParam,items[i].s);
}return TRUE;
case TE_GETTEXTLEN: {
int i=itemindex(wParam); if (i<0) return 0;
return strlen(items[i].s)+1; // hier: inklusive Null
}
case TE_GETTEXTPTR: {
int i=itemindex(wParam); if (i<0) return 0;
return (LRESULT)items[i].s;
}
case TE_SETTEXTPTR: {
int i=itemindex(wParam); if (i<0) return 0;
delete[] items[i].s;
items[i].s=(char*)lParam;
InvalidateRect(wnd,NULL,TRUE);
}return 0;
case TE_GETCOUNT: return MAKELONG(cols.size(),rows.size());
case TE_HITTEST: return hittest(*(POINT*)lParam);
case TE_INSERT: return HIWORD(wParam) ? insertrow(LOWORD(wParam)) : insertcol(LOWORD(wParam));
case TE_DELETE: return HIWORD(wParam) ? deleterow(LOWORD(wParam)) : deletecol(LOWORD(wParam));
case TE_GETITEM: {
WPARAM i=itemindex(wParam); // Zeiger in Item-Liste
if (i==(WPARAM)-1) return 0;
return LRESULT(items+i);
}
case TE_SETITEM: {
WPARAM i=itemindex(wParam); // Zeiger in Item-Liste
if (i==(WPARAM)-1) return 0;
TE_ITEM*lp=(TE_ITEM*)lParam;
if (items[i].s) delete[] items[i].s;
items[i]=*lp; // mit Zeiger kopieren
items[i].s=newstr(lp->s);
}return TRUE;
case TE_GETMETRICS: return LRESULT(&m);
case TE_SETMETRICS: m=*(TE_METRICS*)lParam; return TRUE;
case TE_INITHEADER: {
m.defXStyle=TEAS_DECALIGN; // für Zahlen vorbereiten
initstorage((UINT)wParam,1);
const char*p=(const char*)lParam;
for (UINT i=0;i<(UINT)wParam;i++) {
int l=strlen(p)+1;
items[i].s=newcopy(p,l); p+=l;
}
m.yMin=1; // Nicht reduzieren
rows[0].flags=TEAS_HEADER|TEAS_STICKY|TEAS_NOEDIT|TEAS_SETALIGN|TEAS_LEFTALIGN;
m.xMin=m.xMax=(UINT)wParam; // Spalten fixieren
}return TRUE;
case TE_INITHEADERFOOTER: {
m.defXStyle=TEAS_DECALIGN; // für Zahlen vorbereiten
initstorage((UINT)wParam,2);
const char*p=(const char*)lParam;
for (UINT i=0;i<(UINT)wParam;i++) {
int l=strlen(p)+1;
items[i].s=newcopy(p,l); p+=l;
l=strlen(p)+1;
items[(UINT)wParam+i].s=newcopy(p,l); p+=l;
}
m.yMin=2; // Nicht reduzieren
rows[1].flags=TEAS_HEADER|TEAS_STICKY|TEAS_NOEDIT|TEAS_SETALIGN|TEAS_RIGHTALIGN;
rows[0].flags=TEAS_HEADER|TEAS_STICKY|TEAS_NOEDIT|TEAS_SETALIGN|TEAS_LEFTALIGN;
m.xMin=m.xMax=(UINT)wParam; // Spalten fixieren
}return TRUE;
case WM_SIZE: {
onSize(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));
}return 0;
case WM_PRINTCLIENT: {
onPaint((HDC)wParam);
}return 0;
case WM_PAINT: {
PAINTSTRUCT ps;
BeginPaint(wnd,&ps);
onPaint(ps.hdc);
EndPaint(wnd,&ps);
}return 0;
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
case WM_MOUSEWHEEL:
case WM_MOUSEMOVE: {
POINT pos={GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)};
char hit=hittest(pos);
switch (msg) {
case WM_MOUSEMOVE: {
BYTE cursor=0x88; // IDC_NO;
static const BYTE cursors[]={0x00,0x84,0x85,0x82}; // Offsets zu 0x7F00
if ((hit&3)==3) cursor=cursors[hit>>2&3];
SetCursor(LoadCursor(0,MAKEINTRESOURCE(0x7F00+cursor)));
}break;
case WM_LBUTTONDOWN: if (hit==3) { // Treffer, nicht auf Rand
WPARAM ii=itemindex(pos.x,pos.y);
if (okaytoedit(ii)) {SetFocus(0); setedit(ii);}
}break;
}
}break;
case WM_SETFOCUS: setedit(editindex); break;
case WM_COMMAND: switch (HIWORD(wParam)) {
case EN_KILLFOCUS: killedit(); break;
case EN_VSCROLL: {
WPARAM nitems=(WPARAM)cols.size()*rows.size();
WPARAM cur=editindex;
do{
switch (LOWORD(wParam)) {
case VK_LEFT: cur=(cur?cur:nitems)-1; break;
case VK_RIGHT: if (++cur==nitems) cur=0; break;
case VK_UP: cur-=cols.size(); if ((INT_PTR)cur<0) cur+=nitems; break;
case VK_DOWN: cur+=cols.size(); if (cur>=nitems) cur-=nitems; break;
}
}while (!okaytoedit(cur));
SetFocus(0);
setedit(cur);
}break;
}break;
case WM_SETFONT: {
setfont((HFONT)wParam);
if (LOWORD(lParam)) InvalidateRect(wnd,0,TRUE);
}return 0;
case WM_CONTEXTMENU: {
if ((HWND)wParam==wnd) {
POINT pt={GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)},pt2=pt;
ScreenToClient(wnd,&pt);
char hit=hittest(pt);
if (!(hit&0xC0)) { // Nicht auf Rand
HMENU menu=LoadMenu(0,MAKEINTRESOURCE(12));
if (cols.size()<=m.xMin
|| !(hit&1)
|| cols[pt.x].flags&(TEAS_NOEDIT|TEAS_STICKY))
EnableMenuItem(menu,4,MF_GRAYED);
if (cols.size()>=m.xMax
|| !pt.x && cols[0].flags&TEAS_HEADER
|| pt.x && pt.x==cols.size() && cols[pt.x-1].flags&TEAS_HEADER)
EnableMenuItem(menu,3,MF_GRAYED);
if (rows.size()<=m.yMin
|| !(hit&2)
|| rows[pt.y].flags&(TEAS_NOEDIT|TEAS_STICKY))
EnableMenuItem(menu,2,MF_GRAYED);
if (rows.size()>=m.yMax
|| !pt.y && rows[0].flags&TEAS_HEADER
|| pt.y && pt.y==rows.size() && rows[pt.y-1].flags&TEAS_HEADER)
EnableMenuItem(menu,1,MF_GRAYED);
switch (TrackPopupMenu(GetSubMenu(menu,0),TPM_RIGHTBUTTON|TPM_NONOTIFY|TPM_RETURNCMD,pt2.x,pt2.y,0,wnd,0)) {
case 1: insertrow(pt.y); break;
case 2: deleterow(pt.y); break;
case 3: insertcol(pt.x); break;
case 4: deletecol(pt.x); break;
}
DestroyMenu(menu);
}
}
}break;
}
return DefWindowProc(wnd,msg,wParam,lParam);
}
tableedit::tableedit(HWND hwnd) {
wnd=hwnd;
items=0;
editindex=0;
memset(&m,0,sizeof m);
m.border=GetSysColor(COLOR_3DLIGHT);
m.fgError=RGB(0,0,0);
m.bgError=RGB(255,128,128);
static const BYTE defMetrics[6]={/*borW*/1,/*padX*/5,/*padY*/3,/*minW*/8,/*minH*/12,/*mouE*/1};
memcpy(&m.borW,defMetrics,sizeof defMetrics);
m.yMax=m.xMax=(UINT)-1;
m.DecimalChar=::DecimalChar;
setfont((HFONT)SendMessage(GetParent(hwnd),WM_GETFONT,0,0));
}
tableedit::~tableedit() {
initstorage(0,0);
setfont(0);
}
void TableInit(void) { // Klassen-Registrierung
tableedit::init();
}
Detected encoding: UTF-8 | 0
|