Simplify the way that the active csys is handled, and default to

locked on to the XY plane. And simplify the handling of colors in
the text window: identify them by a character, not an integer ID,
since the character is easier to remember.

[git-p4: depot-paths = "//depot/solvespace/": change = 1687]
solver
Jonathan Westhues 2008-04-25 00:26:15 -08:00
parent a7cec38656
commit ebdef1818c
6 changed files with 179 additions and 160 deletions

View File

@ -31,9 +31,6 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "Zoom &Out\t-", MNU_ZOOM_OUT, '-', mView },
{ 1, "Zoom To &Fit\tF", MNU_ZOOM_TO_FIT, 'F', mView },
{ 1, NULL, 0, NULL },
{ 1, "&Onto Plane / Coordinate System\tO", MNU_ORIENT_ONTO, 'O', mView },
{ 1, "&Lock Orientation\tL", MNU_LOCK_VIEW, 'L', mView },
{ 1, NULL, 0, NULL },
{ 1, "Dimensions in &Inches", MNU_UNITS_INCHES, 0, mView },
{ 1, "Dimensions in &Millimeters", MNU_UNITS_MM, 0, mView },
@ -100,6 +97,10 @@ void GraphicsWindow::Init(void) {
EnsureValidActives();
// Start locked on to the XY plane.
hRequest r = Request::HREQUEST_REFERENCE_XY;
activeCsys = r.entity(0);
show2dCsyss = true;
showAxes = true;
showPoints = true;
@ -124,6 +125,42 @@ Point2d GraphicsWindow::ProjectPoint(Vector p) {
return r;
}
void GraphicsWindow::AnimateOnto(Quaternion quatf, Vector offsetf) {
// Get our initial orientation and translation.
Quaternion quat0 = Quaternion::MakeFrom(SS.GW.projRight, SS.GW.projUp);
Vector offset0 = SS.GW.offset;
// Make sure we take the shorter of the two possible paths.
double mp = (quatf.Minus(quat0)).Magnitude();
double mm = (quatf.Plus(quat0)).Magnitude();
if(mp > mm) {
quatf = quatf.ScaledBy(-1);
mp = mm;
}
// Animate transition, unless it's a tiny move.
SDWORD dt = (mp < 0.01) ? (-20) : (SDWORD)(100 + 1000*mp);
SDWORD tn, t0 = GetMilliseconds();
double s = 0;
do {
offset = (offset0.ScaledBy(1 - s)).Plus(offsetf.ScaledBy(s));
Quaternion quat = (quat0.ScaledBy(1 - s)).Plus(quatf.ScaledBy(s));
quat = quat.WithMagnitude(1);
projRight = quat.RotationU();
projUp = quat.RotationV();
PaintGraphics();
tn = GetMilliseconds();
s = (tn - t0)/((double)dt);
} while((tn - t0) < dt);
projRight = quatf.RotationU();
projUp = quatf.RotationV();
offset = offsetf;
InvalidateGraphics();
}
void GraphicsWindow::MenuView(int id) {
switch(id) {
case MNU_ZOOM_IN:
@ -137,69 +174,6 @@ void GraphicsWindow::MenuView(int id) {
case MNU_ZOOM_TO_FIT:
break;
case MNU_LOCK_VIEW:
SS.GW.viewLocked = !SS.GW.viewLocked;
SS.GW.EnsureValidActives();
break;
case MNU_ORIENT_ONTO: {
SS.GW.GroupSelection();
Entity *e = NULL;
if(SS.GW.gs.n == 1 && SS.GW.gs.csyss == 1) {
e = SS.GetEntity(SS.GW.gs.entity[0]);
} else if(SS.GW.activeCsys.v != Entity::NO_CSYS.v) {
e = SS.GetEntity(SS.GW.activeCsys);
}
if(e) {
// A quaternion with our original rotation
Quaternion quat0 = Quaternion::MakeFrom(
SS.GW.projRight, SS.GW.projUp);
// And with our final rotation
Vector pr, pu;
e->Csys2dGetBasisVectors(&pr, &pu);
Quaternion quatf = Quaternion::MakeFrom(pr, pu);
// Make sure we take the shorter of the two possible paths.
double mp = (quatf.Minus(quat0)).Magnitude();
double mm = (quatf.Plus(quat0)).Magnitude();
if(mp > mm) {
quatf = quatf.ScaledBy(-1);
mp = mm;
}
// And also get the offsets.
Vector offset0 = SS.GW.offset;
Vector offsetf = SS.GetEntity(e->assoc[0])->PointGetCoords();
// Animate transition, unless it's a tiny move.
SDWORD dt = (mp < 0.01) ? (-20) : (SDWORD)(100 + 1000*mp);
SDWORD tn, t0 = GetMilliseconds();
double s = 0;
do {
SS.GW.offset =
(offset0.ScaledBy(1 - s)).Plus(offsetf.ScaledBy(s));
Quaternion quat =
(quat0.ScaledBy(1 - s)).Plus(quatf.ScaledBy(s));
quat = quat.WithMagnitude(1);
SS.GW.projRight = quat.RotationU();
SS.GW.projUp = quat.RotationV();
PaintGraphics();
tn = GetMilliseconds();
s = (tn - t0)/((double)dt);
} while((tn - t0) < dt);
SS.GW.projRight = pr;
SS.GW.projUp = pu;
SS.GW.offset = offsetf;
SS.GW.hover.Clear();
SS.GW.ClearSelection();
InvalidateGraphics();
} else {
Error("Select plane or coordinate system before orienting.");
}
break;
}
case MNU_UNITS_MM:
SS.GW.viewUnits = UNIT_MM;
SS.GW.EnsureValidActives();
@ -245,7 +219,6 @@ void GraphicsWindow::EnsureValidActives(void) {
CheckMenuById(MNU_SEL_CSYS, !in3d);
// And update the checked state for various menus
CheckMenuById(MNU_LOCK_VIEW, viewLocked);
switch(viewUnits) {
case UNIT_MM:
case UNIT_INCHES:
@ -315,19 +288,30 @@ void GraphicsWindow::MenuEdit(int id) {
void GraphicsWindow::MenuRequest(int id) {
char *s;
switch(id) {
case MNU_SEL_CSYS:
case MNU_SEL_CSYS: {
SS.GW.GroupSelection();
if(SS.GW.gs.n == 1 && SS.GW.gs.csyss == 1) {
SS.GW.activeCsys = SS.GW.gs.entity[0];
SS.GW.ClearSelection();
} else {
}
if(SS.GW.activeCsys.v == Entity::NO_CSYS.v) {
Error("Select 2d coordinate system (e.g., the XY plane) "
"before locking on.");
break;
}
// Align the view with the selected csys
Entity *e = SS.GetEntity(SS.GW.activeCsys);
Vector pr, pu;
e->Csys2dGetBasisVectors(&pr, &pu);
Quaternion quatf = Quaternion::MakeFrom(pr, pu);
Vector offsetf = SS.GetEntity(e->assoc[0])->PointGetCoords();
SS.GW.AnimateOnto(quatf, offsetf);
SS.GW.EnsureValidActives();
SS.TW.Show();
break;
}
case MNU_NO_CSYS:
SS.GW.activeCsys = Entity::NO_CSYS;
SS.GW.EnsureValidActives();
@ -376,11 +360,11 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
double dy = (y - orig.mouse.y) / scale;
// When the view is locked, permit only translation (pan).
if(!(shiftDown || ctrlDown) || viewLocked) {
if(!(shiftDown || ctrlDown)) {
offset.x = orig.offset.x + dx*projRight.x + dy*projUp.x;
offset.y = orig.offset.y + dx*projRight.y + dy*projUp.y;
offset.z = orig.offset.z + dx*projRight.z + dy*projUp.z;
} else if(ctrlDown && !viewLocked) {
} else if(ctrlDown) {
double theta = atan2(orig.mouse.y, orig.mouse.x);
theta -= atan2(y, x);
@ -389,7 +373,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
projUp = orig.projUp.RotatedAbout(normal, theta);
NormalizeProjectionVectors();
} else if(!viewLocked) {
} else {
double s = 0.3*(PI/180); // degrees per pixel
projRight = orig.projRight.RotatedAbout(orig.projUp, -s*dx);
projUp = orig.projUp.RotatedAbout(orig.projRight, s*dy);

View File

@ -31,16 +31,22 @@ void Group::Draw(void) {
memset(&poly, 0, sizeof(poly));
SEdge error;
if(edges.AssemblePolygon(&poly, &error)) {
glxColor4d(0, 0, 1, 0.15);
glxColor4d(0, 0, 1, 0.1);
glxFillPolygon(&poly);
} else {
glxColor4d(1, 0, 0, 0.3);
glxColor4d(1, 0, 0, 0.2);
glLineWidth(10);
glBegin(GL_LINES);
glxVertex3v(error.a);
glxVertex3v(error.b);
glEnd();
glLineWidth(1);
glxColor3d(1, 0, 0);
glPushMatrix();
glxTranslatev(error.b);
glxOntoCsys(SS.GW.projRight, SS.GW.projUp);
glxWriteText("not closed contour!");
glPopMatrix();
}
poly.Clear();
}

View File

@ -12,8 +12,9 @@ void SolveSpace::Init(char *cmdLine) {
TW.Init();
GW.Init();
TW.Show();
GenerateAll();
TW.Show();
}
void SolveSpace::GenerateAll(void) {
@ -158,6 +159,7 @@ void SolveSpace::MenuFile(int id) {
SS.GenerateAll();
SS.GW.Init();
SS.TW.Init();
SS.TW.Show();
break;
case GraphicsWindow::MNU_OPEN:

View File

@ -3,18 +3,17 @@
#define COLOR_BG_HEADER RGB(50, 20, 50)
const TextWindow::Color TextWindow::colors[] = {
{ RGB(255, 255, 255), COLOR_BG_DEFAULT, }, // 0
{ 'd', RGB(255, 255, 255), COLOR_BG_DEFAULT, }, // default
{ 'l', RGB(100, 100, 255), COLOR_BG_DEFAULT, }, // link
{ RGB(170, 0, 0), COLOR_BG_HEADER, }, // 1 hidden label
{ RGB( 40, 255, 40), COLOR_BG_HEADER, }, // 2 shown label
{ RGB(200, 200, 0), COLOR_BG_HEADER, }, // 3 mixed label
{ RGB(255, 200, 40), COLOR_BG_HEADER, }, // 4 yellow text
{ RGB(255, 255, 255), COLOR_BG_HEADER, }, // 5 white text
{ RGB( 0, 0, 0), COLOR_BG_DEFAULT, }, // 6
{ RGB( 0, 0, 0), COLOR_BG_DEFAULT, }, // 7
// These are for the header
{ 'D', RGB(255, 255, 255), COLOR_BG_HEADER, }, // default
{ 'H', RGB(170, 0, 0), COLOR_BG_HEADER, }, // hidden
{ 'S', RGB( 40, 255, 40), COLOR_BG_HEADER, }, // shown
{ 'M', RGB(200, 200, 0), COLOR_BG_HEADER, }, // mixed h/s
{ 'T', RGB(255, 200, 40), COLOR_BG_HEADER, }, // title
{ RGB(255, 255, 255), COLOR_BG_DEFAULT, }, // 8 title
{ RGB(100, 100, 255), COLOR_BG_DEFAULT, }, // 9 link
{ 0, 0, 0 },
};
void TextWindow::Init(void) {
@ -29,7 +28,7 @@ void TextWindow::ClearScreen(void) {
for(i = 0; i < MAX_ROWS; i++) {
for(j = 0; j < MAX_COLS; j++) {
text[i][j] = ' ';
meta[i][j].color = COLOR_DEFAULT;
meta[i][j].color = 'd';
meta[i][j].link = NOT_A_LINK;
}
}
@ -51,7 +50,7 @@ void TextWindow::Printf(char *fmt, ...) {
meta[r][c].link = NOT_A_LINK;
}
int color = COLOR_DEFAULT;
int color = 'd';
int link = NOT_A_LINK;
DWORD data = 0;
LinkFunction *f = NULL;
@ -81,7 +80,7 @@ void TextWindow::Printf(char *fmt, ...) {
break;
}
case 'E':
color = COLOR_DEFAULT;
color = 'd';
link = NOT_A_LINK;
data = 0;
f = NULL;
@ -93,9 +92,9 @@ void TextWindow::Printf(char *fmt, ...) {
if(*fmt == 'p') {
color = va_arg(vl, int);
} else {
color = *fmt - '0';
color = *fmt;
}
if(color < 0 || color >= arraylen(colors)) color = 0;
if(color < 0 || color > 255) color = 0;
break;
case 'L':
@ -155,15 +154,11 @@ void TextWindow::Show(void) {
} else {
switch(shown->screen) {
default:
shown->screen = SCREEN_ALL_GROUPS;
shown->screen = SCREEN_LIST_OF_GROUPS;
// fall through
case SCREEN_ALL_GROUPS:
ShowAllGroups();
break;
case SCREEN_REQUESTS_IN_GROUP:
ShowRequestsInGroup();
break;
case SCREEN_LIST_OF_GROUPS: ShowListOfGroups(); break;
case SCREEN_GROUP_INFO: ShowGroupInfo(); break;
case SCREEN_REQUEST_INFO: ShowRequestInfo(); break;
}
}
InvalidateText();
@ -181,7 +176,7 @@ void TextWindow::ScreenNavigation(int link, DWORD v) {
default:
case 'h':
SS.TW.OneScreenForward();
SS.TW.shown->screen = SCREEN_ALL_GROUPS;
SS.TW.shown->screen = SCREEN_LIST_OF_GROUPS;
break;
case 'b':
@ -205,7 +200,7 @@ void TextWindow::ShowHeader(void) {
SS.GW.EnsureValidActives();
if(SS.GW.pendingDescription) {
Printf(" %C4 group:%s",
Printf(" %CT group:%s",
SS.group.FindById(SS.GW.activeGroup)->DescriptionString());
} else {
// Navigation buttons
@ -215,7 +210,7 @@ void TextWindow::ShowHeader(void) {
} else {
cd = SS.GetEntity(SS.GW.activeCsys)->DescriptionString();
}
Printf(" %Lb%f<<%E %Lh%fhome%E %C4 csys:%C5 %s",
Printf(" %Lb%f<<%E %Lh%fhome%E %CT csys:%CD %s",
(DWORD)(&TextWindow::ScreenNavigation),
(DWORD)(&TextWindow::ScreenNavigation),
cd);
@ -223,27 +218,27 @@ void TextWindow::ShowHeader(void) {
int datumColor;
if(SS.GW.show2dCsyss && SS.GW.showAxes && SS.GW.showPoints) {
datumColor = COLOR_MEANS_SHOWN;
datumColor = 'S'; // shown
} else if(!(SS.GW.show2dCsyss || SS.GW.showAxes || SS.GW.showPoints)) {
datumColor = COLOR_MEANS_HIDDEN;
datumColor = 'H'; // hidden
} else {
datumColor = COLOR_MEANS_MIXED;
datumColor = 'M'; // mixed
}
#define hs(b) ((b) ? COLOR_MEANS_SHOWN : COLOR_MEANS_HIDDEN)
Printf("%C4show: "
"%Cp%Ll%D%f2d-csys%E%C4 "
"%Cp%Ll%D%faxes%E%C4 "
"%Cp%Ll%D%fpoints%E%C4 "
"%Cp%Ll%fany-datum%E%C4",
#define hs(b) ((b) ? 'S' : 'H')
Printf("%CTshow: "
"%Cp%Ll%D%f2d-csys%E%CT "
"%Cp%Ll%D%faxes%E%CT "
"%Cp%Ll%D%fpoints%E%CT "
"%Cp%Ll%fany-datum%E%CT",
hs(SS.GW.show2dCsyss), (DWORD)&(SS.GW.show2dCsyss), &(SS.GW.ToggleBool),
hs(SS.GW.showAxes), (DWORD)&(SS.GW.showAxes), &(SS.GW.ToggleBool),
hs(SS.GW.showPoints), (DWORD)&(SS.GW.showPoints), &(SS.GW.ToggleBool),
datumColor, &(SS.GW.ToggleAnyDatumShown)
);
Printf("%C4 "
"%Cp%Ll%D%fall-groups%E%C4 "
"%Cp%Ll%D%fconstraints%E%C4",
Printf("%CT "
"%Cp%Ll%D%fall-groups%E%CT "
"%Cp%Ll%D%fconstraints%E%CT",
hs(SS.GW.showAllGroups), (DWORD)(&SS.GW.showAllGroups),
&(SS.GW.ToggleBool),
hs(SS.GW.showConstraints), (DWORD)(&SS.GW.showConstraints),
@ -251,56 +246,68 @@ void TextWindow::ShowHeader(void) {
);
}
void TextWindow::ShowAllGroups(void) {
Printf("%C8[[all groups in sketch follow]]%E");
void TextWindow::ShowListOfGroups(void) {
Printf("%Cd[[all groups in sketch follow]]%E");
int i;
for(i = 0; i <= SS.group.n; i++) {
DWORD v;
for(i = 0; i < SS.group.n; i++) {
char *s;
if(i == SS.group.n) {
s = "all requests from all groups";
v = 0;
} else {
Group *g = &(SS.group.elem[i]);
s = g->DescriptionString();
v = g->h.v;
}
Printf(" %C9%Ll%D%f%s%E", v, (DWORD)(&TextWindow::ScreenSelectGroup), s);
Group *g = &(SS.group.elem[i]);
s = g->DescriptionString();
Printf(" %Cl%Ll%D%f%s%E",
g->h.v, (DWORD)(&TextWindow::ScreenSelectGroup), s);
}
}
void TextWindow::ScreenSelectGroup(int link, DWORD v) {
SS.TW.OneScreenForward();
SS.TW.shown->screen = SCREEN_REQUESTS_IN_GROUP;
SS.TW.shown->screen = SCREEN_GROUP_INFO;
SS.TW.shown->group.v = v;
SS.TW.Show();
}
void TextWindow::ShowRequestsInGroup(void) {
if(shown->group.v == 0) {
Printf("%C8[[requests in all groups]]%E");
void TextWindow::ScreenSelectRequest(int link, DWORD v) {
SS.TW.OneScreenForward();
SS.TW.shown->screen = SCREEN_REQUEST_INFO;
SS.TW.shown->request.v = v;
SS.TW.Show();
}
void TextWindow::ShowGroupInfo(void) {
Group *g = SS.group.FindById(shown->group);
if(SS.GW.activeGroup.v == shown->group.v) {
Printf("%Cd[[this is the active group]]");
} else if(shown->group.v == Group::HGROUP_REFERENCES.v) {
Printf("%Cd[[this group contains the references]]");
} else {
Group *g = SS.group.FindById(shown->group);
if(SS.GW.activeGroup.v == shown->group.v) {
Printf("%C8[[this is the active group]]");
} else if(shown->group.v == Group::HGROUP_REFERENCES.v) {
Printf("%C8[[this group contains the references]]");
} else {
Printf("%C8[[not active; %C9%Llactivate this group%E%C8]]");
}
Printf("%C8[[requests in group %s]]%E", g->DescriptionString());
Printf("%Cd[[not active; %Cl%Llactivate this group%E%Cd]]");
}
Printf("%Cd[[requests in group %s]]%E", g->DescriptionString());
int i;
for(i = 0; i < SS.request.n; i++) {
Request *r = &(SS.request.elem[i]);
if(r->group.v == shown->group.v || shown->group.v == 0) {
if(r->group.v == shown->group.v) {
char *s = r->DescriptionString();
Printf(" %s", s);
Printf(" %Cl%Ll%D%f%s%E",
r->h.v, (DWORD)(&TextWindow::ScreenSelectRequest), s);
}
}
}
void TextWindow::ShowRequestInfo(void) {
Request *r = SS.GetRequest(shown->request);
char *s;
switch(r->type) {
case Request::CSYS_2D: s = "2d coordinate system"; break;
case Request::DATUM_POINT: s = "datum point"; break;
case Request::LINE_SEGMENT: s = "line segment"; break;
default: oops();
}
Printf("%Cd[[request for %s]]%E", s);
}

27
ui.h
View File

@ -13,14 +13,11 @@ public:
static const int COLOR_BG_DEFAULT = RGB( 15, 15, 0);
typedef struct {
char c;
int fg;
int bg;
} Color;
static const Color colors[];
static const int COLOR_DEFAULT = 0;
static const int COLOR_MEANS_HIDDEN = 1;
static const int COLOR_MEANS_SHOWN = 2;
static const int COLOR_MEANS_MIXED = 3;
// The rest of the window, text displayed in response to typed commands;
// some of this might do something if you click on it.
@ -45,11 +42,14 @@ public:
void Show(void);
// State for the screen that we are showing in the text window.
static const int SCREEN_ALL_GROUPS = 0;
static const int SCREEN_REQUESTS_IN_GROUP = 1;
static const int SCREEN_LIST_OF_GROUPS = 0;
static const int SCREEN_GROUP_INFO = 1;
static const int SCREEN_REQUEST_INFO = 2;
static const int SCREEN_ENTIY_INFO = 3;
typedef struct {
int screen;
hGroup group;
int screen;
hGroup group;
hRequest request;
} ShownState;
static const int HISTORY_LEN = 16;
ShownState showns[HISTORY_LEN];
@ -60,11 +60,14 @@ public:
void ShowHeader(void);
// These are self-contained screens, that show some information about
// the sketch.
void ShowAllGroups(void);
void ShowRequestsInGroup(void);
void ShowListOfGroups(void);
void ShowGroupInfo(void);
void ShowRequestInfo(void);
void ShowEntityInfo(void);
void OneScreenForward(void);
static void ScreenSelectGroup(int link, DWORD v);
static void ScreenSelectRequest(int link, DWORD v);
static void ScreenNavigation(int link, DWORD v);
};
@ -84,8 +87,6 @@ public:
MNU_ZOOM_IN,
MNU_ZOOM_OUT,
MNU_ZOOM_TO_FIT,
MNU_ORIENT_ONTO,
MNU_LOCK_VIEW,
MNU_UNSELECT_ALL,
MNU_UNITS_INCHES,
MNU_UNITS_MM,
@ -133,10 +134,10 @@ public:
Vector projUp;
Point2d mouse;
} orig;
bool viewLocked;
void NormalizeProjectionVectors(void);
Point2d ProjectPoint(Vector p);
void AnimateOnto(Quaternion quatf, Vector offsetf);
typedef enum {
UNIT_MM = 0,

View File

@ -16,12 +16,18 @@
#define TEXT_HEIGHT 18
#define TEXT_WIDTH 9
// There's a half-line offset between the header and the rest of the window
#define OFFSET_LINE 3
#define OFFSET_HEIGHT 9
HINSTANCE Instance;
HWND TextWnd;
HWND TextWndScrollBar;
int TextWndScrollPos;
int TextWndRows;
COLORREF BgColor[256];
COLORREF FgColor[256];
HWND GraphicsWnd;
HWND GraphicsEditControl;
@ -83,6 +89,15 @@ void MemFree(void *p) { free(p); }
static void PaintTextWnd(HDC hdc)
{
// Generate the color table.
int i;
for(i = 0; SS.TW.colors[i].c != 0; i++) {
int c = SS.TW.colors[i].c;
if(c < 0 || c > 255) oops();
BgColor[c] = SS.TW.colors[i].bg;
FgColor[c] = SS.TW.colors[i].fg;
}
RECT rect;
GetClientRect(TextWnd, &rect);
@ -122,10 +137,9 @@ static void PaintTextWnd(HDC hdc)
if(r >= SS.TW.MAX_ROWS) continue;
for(c = 0; c < SS.TW.MAX_COLS; c++) {
char v = '0' + (c % 10);
int color = SS.TW.meta[r][c].color;
SetTextColor(backDc, SS.TW.colors[color].fg);
SetBkColor(backDc, SS.TW.colors[color].bg);
SetTextColor(backDc, FgColor[color]);
SetBkColor(backDc, BgColor[color]);
if(SS.TW.meta[r][c].link) {
SelectObject(backDc, LinkFont);
@ -134,9 +148,10 @@ static void PaintTextWnd(HDC hdc)
}
int x = 4 + c*TEXT_WIDTH;
int y = (r-TextWndScrollPos)*TEXT_HEIGHT + 1 + (r >= 3 ? 9 : 0);
int y = (r-TextWndScrollPos)*TEXT_HEIGHT + 1 +
(r >= OFFSET_LINE ? OFFSET_HEIGHT : 0);
HBRUSH b = CreateSolidBrush(SS.TW.colors[color].bg);
HBRUSH b = CreateSolidBrush(BgColor[color]);
RECT a;
a.left = x; a.right = x+TEXT_WIDTH;
a.top = y; a.bottom = y+TEXT_HEIGHT;
@ -242,6 +257,10 @@ LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
int x = LOWORD(lParam);
int y = HIWORD(lParam);
if(y >= TEXT_HEIGHT*OFFSET_LINE) {
y -= OFFSET_HEIGHT;
}
// Find the corresponding character in the text buffer
int r = (y / TEXT_HEIGHT);
int c = (x / TEXT_WIDTH);