#! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # laf.h # laf.cpp # This archive created: Fri Apr 30 09:48:57 1993 PATH=/bin:/usr/bin:$PATH ;export PATH if test -f 'laf.h' then echo shar: "will not over-write existing file 'laf.h'" else cat << \SHAR_EOF > 'laf.h' //====================================================================== // File: laf.h // Author: Timothy P. Justice // Created: March 24, 1992 // Revised: November 2, 1992 // Description: This file contains the interface of the // classes for the Little Application Framework. //---------------------------------------------------------------------- // Copyright (c) 1992 by Timothy P. Justice. // // This file is part of LAF, the Little Application Framework. // // Permission is hereby granted to everyone to copy and distribute // without fee verbatim copies of this file. This copyright notice // must be retained on all copies. No warranty expressed or implied // is made concerning this software. // // Author's e-mail address: justict@storm.cs.orst.edu //====================================================================== #ifndef LAF_H #define LAF_H #include // Microsoft Windows interface #include // C++ standard i/o library #include // C++ standard library #include // C++ string library #include // C++ string streams // Class Hierarchy: Little Application Framework class wostream; class window; class application; class button; class editText; class staticText; class listBox; // Other Types enum streamBase { octBase, decBase, hexBase }; enum color { black, blue, green, cyan, red, magenta, brown, gray, white, brightBlue, brightGreen, brightCyan, brightRed, brightMagenta, brightYellow, brightGray }; enum lineStyle { solidLine, dashedLine, dottedLine, nullLine }; enum textAlign { leftText, centerText, rightText }; struct menuItem { char * name; int number; }; // --- Window Stream Class --------------------------------------------- // // wostream is used to simulate the behavior of C++ output // streams in a window. //---------------------------------------------------------------------- class wostream { public: wostream(window *); streamBase base() const; streamBase base(streamBase); int precision() const; int precision(int); int width() const; int width(int); window * outputWindow(); wostream & operator << (char); wostream & operator << (char *); wostream & operator << (double); wostream & operator << (float); wostream & operator << (int); wostream & operator << (long); private: int fieldWidth; int floatPrecision; streamBase numberBase; window * winptr; }; // --- Window Class ---------------------------------------------------- // // Window is an abstract class that defines the behavior common // to all windows. //---------------------------------------------------------------------- class window { public: friend class wostream; window(); int avgCharWidth(); int bottom(); void cartesianOff(); void cartesianOn(color, lineStyle, int); void circle(const int, const int, const int); void circle(const POINT &, const int); void checkMenuItem(int); void clearAndUpdate(); virtual void command(int) = 0; virtual void create() = 0; button createButton(char *, int, int, int, int, int); editText createEditText(int, int, int, int, int, textAlign = leftText, int = 1, char * = 0); listBox createListBox(int, int, int, int, int, int = 1); staticText createStaticText(int, int, int, int, textAlign = leftText, int = 0, char * = 0); virtual void doubleClick(int, int) = 0; void gridOn(color, lineStyle, int); void gridOff(); HWND handle(); int height(); HANDLE instance(); int left(); void line(const int, const int, const int, const int); void line(const POINT &, const POINT &); virtual void mouseDown(int, int) = 0; virtual void paint() = 0; void point(const int, const int); void polygon(POINT [], int); long process(HWND, WORD, WORD, LONG); void rectangle(const int, const int, const int, const int); int right(); void scrollToBottom(); void scrollToLeft(); void scrollToRight(); void scrollToTop(); void setBrush(color); void setMaxX(int); void setMaxY(int); void setMenu(menuItem [], int); void setPen(color, lineStyle, int); void setTextPosition(int, int); int show(int); virtual void size(int, int) = 0; int textHeight(); int textWidth(char *); void textPosition(int &, int &); virtual void timer() = 0; int top(); void uncheckMenuItem(int); void update(); void vector(const int, const int, const int, const int); int width(); wostream wout; protected: void createBrush(); void createPen(); void deleteBrush(); void deletePen(); void print(char *); COLORREF brushColor; color cartesianColor; lineStyle cartesianStyle; int cartesianWidth; int clientX; int clientY; HBRUSH currentBrush; HPEN currentPen; HDC device; color gridColor; lineStyle gridStyle; int gridWidth; HMENU hmenu; int hScrollPos; HWND hwnd; int initialHeight; int initialWidth; POINT lowerRight; int maxX; int maxY; TEXTMETRIC metric; char * name; POINT nextPos; COLORREF penColor; int penStyle; int penWidth; POINT upperLeft; int vScrollPos; HANDLE prevInstance; POINT prevPos; int tabStop[10]; HANDLE thisInstance; char * title; }; // --- Application Class ----------------------------------------------- // // Application is the basic framework class. It is a special kind // of window that is opened when the application is started. By // itself, application is not very functional. If you create an // instance of application and send it the message "run", a blank // window is displayed. The only functionality available is to // resize, move, minimize, maximize, and close the window (i.e., // terminate the application). The main work done by an application // that is visible to the user is performed by the "paint" method. // Normally, you will create a subclass of application and override // the "paint" method (and probably other methods) to perform the // processing required by your application. //---------------------------------------------------------------------- class application : public window { public: application(char *, char *, HANDLE, HANDLE, int = CW_USEDEFAULT, int = CW_USEDEFAULT); // event handling void create(); void command(int); void doubleClick(int, int); void mouseDown(int, int); void paint(); void size(int, int); void timer(); // non-event behavior int okBox(char *, char * = 0); int debugBox(char *); int run(); void quit(); void startTimer(unsigned int = 1000); void stopTimer(); void sendTimerMessage(); BOOL registerApplication(WNDCLASS *); private: int timerInterval; }; // --- Button Class ---------------------------------------------------- // // The button class implements buttons. //---------------------------------------------------------------------- class button { public: // constructors button(); button(window *, char *, int, int, int, int, int); button(const button &); // destructor ~button(); private: window * win; char * titleValue; int idValue; POINT upperLeft; int heightValue; int widthValue; HWND hwnd; }; // --- EditText Class -------------------------------------------------- // // The editText class implements fields of editable text. //---------------------------------------------------------------------- class editText { public: // constructors editText(); editText(window *, int, int, int, int, int, textAlign = leftText, int = 1, char * = 0); editText(const editText &); // destructor ~editText(); // set input focus on void setFocus(); // get the maximum number of characters int size(); // copy text to string char * text(char *); // assignment operator editText & operator = (const editText &); // text output editText & operator << (char *); editText & operator << (char); editText & operator << (int); editText & operator << (editText & (*)(editText &)); private: window * win; int idValue; POINT upperLeft; int heightValue; int widthValue; DWORD style; HWND hwnd; }; // --- Static Text Class ----------------------------------------------- // // The staticText class implements fields of fixed text. //---------------------------------------------------------------------- class staticText { public: // constructors staticText(); staticText(window *, int, int, int, int, textAlign = leftText, int = 0, char * = 0); staticText(const staticText &); // destructor ~staticText(); // assignment operator staticText & operator = (const staticText &); // equality operator int operator == (const staticText &); // text output staticText & operator << (char *); staticText & operator << (char); staticText & operator << (int); staticText & operator << (staticText & (*)(staticText &)); protected: window * win; POINT upperLeft; int heightValue; int widthValue; DWORD style; HWND hwnd; }; // --- List Box Class -------------------------------------------------- // // The listBox class implements a scrollable list of strings. //---------------------------------------------------------------------- class listBox { public: // constructors listBox(); listBox(window *, int, int, int, int, int, int = 1); listBox(const listBox &); // destructor ~listBox(); // list manipulation void clear(); char * add(char *); // assignment operator listBox & operator = (const listBox &); protected: window * win; int idValue; POINT upperLeft; int heightValue; int widthValue; DWORD style; HWND hwnd; }; //--- Templates -------------------------------------------------------- template T max(T x, T y) { return (x > y) ? x : y; }; template T min(T x, T y) { return (x < y) ? x : y; }; template class WO_MANIP { public: WO_MANIP(wostream & (*ff)(wostream &, T), T ii) : f(ff), i(ii) { } friend wostream & operator << (wostream & wos, WO_MANIP & m) { return (*m.f)(wos, m.i); } private: T i; wostream & (*f)(wostream &, T); }; template class WO_MANIP2 { public: WO_MANIP2(wostream & (*ff)(wostream &, T, T), T aa, T bb) : f(ff), a(aa), b(bb) { } friend wostream & operator << (wostream & wos, WO_MANIP2 & m) { return (*m.f)(wos, m.a, m.b); } private: T a; T b; wostream & (*f)(wostream &, T, T); }; //--- Function Prototypes ---------------------------------------------- editText & eraseText(editText &); staticText & eraseText(staticText &); wostream & dec(wostream &); wostream & hex(wostream &); wostream & oct(wostream &); wostream & endl(wostream &); wostream & eraseToEol(wostream &); wostream & operator << (wostream & wos, wostream & (*)(wostream &)); WO_MANIP setbase(streamBase); WO_MANIP2 setpos(int, int); WO_MANIP setprecision(int); WO_MANIP setw(int); void debugBox(); int randomInt(int, int); //--- External Variables ----------------------------------------------- extern ostrstream dout; // debug output stream #endif SHAR_EOF fi if test -f 'laf.cpp' then echo shar: "will not over-write existing file 'laf.cpp'" else cat << \SHAR_EOF > 'laf.cpp' //====================================================================== // File: laf.cpp // Author: Timothy P. Justice // Created: March 24, 1992 // Revised: November 2, 1992 // Description: This file contains the implementation of the // classes for the Little Application Framework. //---------------------------------------------------------------------- // Copyright (c) 1992 by Timothy P. Justice. // // This file is part of LAF, the Little Application Framework. // // Permission is hereby granted to everyone to copy and distribute // without fee verbatim copies of this file. This copyright notice // must be retained on all copies. No warranty expressed or implied // is made concerning this software. // // Author's e-mail address: justict@storm.cs.orst.edu //====================================================================== #include // C++ math library #include // C++ time library #include "laf.h" // Little Application Framework // --- Debug Output String Stream -------------------------------------- // // The "dout" variable is available externally to the user. It allows // the programmer to use familiar stream output to the stream // associated with the debug message box. The character buffer // "debugBuffer" holds the message and is local to this module. //---------------------------------------------------------------------- static char debugBuffer[1024]; ostrstream dout(debugBuffer, sizeof(debugBuffer)+1); // --- Pointer to the application -------------------------------------- // // This variable is set by the constructor for the application class. // It is used only by the "WndProc" function to pass messages on // to the "process" method of the application (see note below). //---------------------------------------------------------------------- static application * theApplication = 0; // --- Application Window Process Function ----------------------------- // // Windows associates a C++ function with each window. This function // is called each time a message is sent to the window and determines // how each message is handled. Windows requires a specific format. // LAF uses the following function along with the global variable // "theApplication" to simply pass the arguments over to the "process" // method in the application for handling messages sent to the main // application window. Note that the "_export" modifier MUST be used // to prevent an application runtime error. //---------------------------------------------------------------------- long FAR PASCAL _export WndProc(HWND win, unsigned msg, unsigned wParam, LONG lParam) { if (theApplication) return theApplication->process(win, msg, wParam, lParam); else return FALSE; } // --- Window Output Stream -------------------------------------------- wostream::wostream(window * w) { fieldWidth = -1; floatPrecision = -1; numberBase = decBase; winptr = w; } streamBase wostream::base() const { return numberBase; } streamBase wostream::base(streamBase b) { return numberBase = b; } static wostream & base(wostream & wos, streamBase b) { wos.base(b); return wos; } WO_MANIP setbase(streamBase b) { return WO_MANIP(&base, b); } wostream & dec(wostream & wos) { return base(wos, decBase); } wostream & hex(wostream & wos) { return base(wos, hexBase); } wostream & oct(wostream & wos) { return base(wos, octBase); } static wostream & setTextPosition(wostream & wos, int x, int y) { wos.outputWindow()->setTextPosition(x, y); return wos; } WO_MANIP2 setpos(int x, int y) { return WO_MANIP2(&setTextPosition, x, y); } int wostream::precision() const { return floatPrecision; } int wostream::precision(int n) { return floatPrecision = n; } static wostream & precision(wostream & wos, int n) { wos.precision(n); return wos; } WO_MANIP setprecision(int n) { return WO_MANIP(&precision, n); } int wostream::width() const { return fieldWidth; } int wostream::width(int n) { return fieldWidth = n; } static wostream & width(wostream & wos, int n) { wos.width(n); return wos; } WO_MANIP setw(int n) { return WO_MANIP(&width, n); } wostream & endl(wostream & wos) { return wos << '\n'; } wostream & eraseToEol(wostream & wos) { int i, x, y; int w = wos.outputWindow()->avgCharWidth(); wos.outputWindow()->textPosition(x, y); for (i = wos.outputWindow()->right() * 2; i > 0; i -= w) wos << ' '; return wos << setpos(x, y); } window * wostream::outputWindow() { return winptr; } wostream & operator << (wostream & wos, wostream & (*fn)(wostream &)) { return fn(wos); } wostream & wostream::operator << (char c) { char str[2]; str[0] = c; str[1] = '\0'; winptr->print(str); return *this; } wostream & wostream::operator << (char * s) { winptr->print(s); return *this; } wostream & wostream::operator << (double d) { char str[80]; if (fieldWidth < 0) { if (floatPrecision < 0) sprintf(str, "%lg", d); else sprintf(str, "%.*lf", floatPrecision, d); } else { if (floatPrecision < 0) sprintf(str, "%*lg", fieldWidth, d); else sprintf(str, "%*.*lf", fieldWidth, floatPrecision, d); } winptr->print(str); return *this; } wostream & wostream::operator << (float f) { char str[80]; if (fieldWidth < 0) { if (floatPrecision < 0) sprintf(str, "%g", f); else sprintf(str, "%.*f", floatPrecision, f); } else { if (floatPrecision < 0) sprintf(str, "%*g", fieldWidth, f); else sprintf(str, "%*.*f", fieldWidth, floatPrecision, f); } winptr->print(str); return *this; } wostream & wostream::operator << (int n) { char str[80]; char fmt[16]; if (fieldWidth < 0) strcpy(fmt, "%"); else sprintf(fmt, "%%%d", fieldWidth); switch (numberBase) { case octBase: strcat(fmt, "o"); break; case decBase: strcat(fmt, "d"); break; case hexBase: strcat(fmt, "x"); break; } sprintf(str, fmt, n); winptr->print(str); return *this; } wostream & wostream::operator << (long n) { char str[80]; char fmt[16]; if (fieldWidth < 0) strcpy(fmt, "%"); else sprintf(fmt, "%%%d", fieldWidth); switch (numberBase) { case octBase: strcat(fmt, "lo"); break; case decBase: strcat(fmt, "ld"); break; case hexBase: strcat(fmt, "lx"); break; } sprintf(str, fmt, n); winptr->print(str); return *this; } // --- Window Class ---------------------------------------------------- window::window() : wout(this) { } int window::avgCharWidth() { return metric.tmAveCharWidth; } int window::bottom() { return lowerRight.y; } void window::cartesianOff() { cartesianColor = black; cartesianStyle = nullLine; cartesianWidth = 0; } void window::cartesianOn(color c, lineStyle s, int w) { cartesianColor = c; cartesianStyle = s; cartesianWidth = w; } void window::checkMenuItem(int item) { if (hmenu) CheckMenuItem(hmenu, item, MF_CHECKED); } void window::circle(const int x, const int y, const int radius) { Ellipse(device, x - radius, y - radius, x + radius, y + radius); } void window::circle(const POINT & center, const int radius) { this->circle(center.x, center.y, radius); } void window::clearAndUpdate() { InvalidateRect(hwnd, NULL, TRUE); } button window::createButton(char * buttonTitle, int buttonId, int buttonWidth, int buttonHeight, int x, int y) { return button(this, buttonTitle, buttonId, buttonWidth, buttonHeight, x, y); } editText window::createEditText(int ctlId, int ctlWidth, int ctlHeight, int x, int y, textAlign align, int border, char * str) { return editText(this, ctlId, ctlWidth, ctlHeight, x, y, align, border, str); } listBox window::createListBox(int ctlId, int ctlWidth, int ctlHeight, int x, int y, int sort) { return listBox(this, ctlId, ctlWidth, ctlHeight, x, y, sort); } staticText window::createStaticText(int ctlWidth, int ctlHeight, int x, int y, textAlign align, int border, char * str) { return staticText(this, ctlWidth, ctlHeight, x, y, align, border, str); } void window::createBrush() { // you must select new brush before deleting current brush HBRUSH previousBrush = currentBrush; currentBrush = CreateSolidBrush(brushColor); SelectObject(device, currentBrush); if (previousBrush) DeleteObject(previousBrush); } void window::deleteBrush() { // first select a stock brush before deleting the current brush SelectObject(device, GetStockObject(WHITE_BRUSH)); if (currentBrush) DeleteObject(currentBrush); currentBrush = 0; } void window::createPen() { // you must select new pen before deleting current pen HPEN previousPen = currentPen; currentPen = CreatePen(penStyle, penWidth, penColor); SelectObject(device, currentPen); if (previousPen) DeleteObject(previousPen); } void window::deletePen() { // first select a stock pen before deleting the current pen SelectObject(device, GetStockObject(BLACK_PEN)); if (currentPen) DeleteObject(currentPen); currentPen = 0; } void window::gridOff() { gridColor = black; gridStyle = nullLine; gridWidth = 0; } void window::gridOn(color c, lineStyle s, int w) { gridColor = c; gridStyle = s; gridWidth = w; } HWND window::handle() { return hwnd; } int window::height() { return clientY; } HANDLE window::instance() { return thisInstance; } int window::left() { return upperLeft.x; } void window::line(const int fromX, const int fromY, const int toX, const int toY) { MoveTo(device, fromX, fromY); LineTo(device, toX, toY); } void window::line(const POINT & from, const POINT & to) { this->line(from.x, from.y, to.x, to.y); } void window::point(const int x, const int y) { this->circle(x, y, 1); } void window::polygon(POINT point[], int count) { Polygon(device, point, count); } void window::print(char * str) { int tabCount = sizeof(tabStop) / sizeof(tabStop[0]); int tabStart = metric.tmAveCharWidth; char * newline; int i; if (!device) return; while ((newline = strchr(str, '\n')) != NULL) { if (newline != str) TabbedTextOut(device, nextPos.x, nextPos.y, str, (int) (newline - str), tabCount, tabStop, tabStart); nextPos.x = metric.tmAveCharWidth; nextPos.y += metric.tmHeight + metric.tmExternalLeading; str = newline + 1; } if (*str) { TabbedTextOut(device, nextPos.x, nextPos.y, str, strlen(str), tabCount, tabStop, tabStart); nextPos.x += GetTabbedTextExtent(device, str, strlen(str), tabCount, tabStop); } } long window::process(HWND win, WORD msg, WORD wParam, LONG lParam) { int atBottom; int i; int chgH, chgV; static int charX = 0; static int charY = 0; static int maxHScroll = 0; static int maxVScroll = 0; PAINTSTRUCT paintStruct; POINT point; RECT rect; COLORREF saveColor; int saveStyle; int saveWidth; hwnd = win; // needed for WM_CREATE message switch (msg) { case WM_COMMAND: device = GetDC(win); this->command((int) wParam); ReleaseDC(win, device); device = 0; return 0; case WM_CREATE: device = GetDC(win); GetClientRect(hwnd, &rect); upperLeft.x = rect.left; upperLeft.y = rect.top; clientX = lowerRight.x = rect.right; clientY = lowerRight.y = rect.bottom; GetTextMetrics(device, &metric); charX = metric.tmAveCharWidth; charY = metric.tmHeight + metric.tmExternalLeading; if (nextPos.x < 0) { nextPos.x = charX; nextPos.y = charY; } for (i = 0; i < sizeof(tabStop)/sizeof(tabStop[0]); i++) tabStop[i] = (i + 1) * 8 * metric.tmAveCharWidth; prevPos = nextPos; this->create(); if (cartesianWidth) { SetMapMode(device, MM_LOENGLISH); SetViewportOrg(device, clientX / 2, clientY / 2); DPtoLP(device, (LPPOINT) &rect, 2); upperLeft.x = rect.left; upperLeft.y = rect.top; lowerRight.x = rect.right; lowerRight.y = rect.bottom; } ReleaseDC(win, device); device = 0; return 0; case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: device = GetDC(win); point = MAKEPOINT(lParam); if (cartesianWidth) DPtoLP(device, &point, 1); this->mouseDown(point.x, point.y); ReleaseDC(win, device); device = 0; return 0; case WM_LBUTTONDBLCLK: case WM_MBUTTONDBLCLK: case WM_RBUTTONDBLCLK: device = GetDC(win); point = MAKEPOINT(lParam); this->doubleClick(point.x, point.y); ReleaseDC(win, device); device = 0; return 0; case WM_PAINT: device = BeginPaint(win, &paintStruct); if (cartesianWidth) { saveColor = penColor; saveStyle = penStyle; saveWidth = penWidth; setPen(cartesianColor, cartesianStyle, 1); GetClientRect(hwnd, &rect); DPtoLP(device, (LPPOINT) &rect, 2); vector(0, rect.bottom, 0, rect.top); vector(rect.left, 0, rect.right, 0); penColor = saveColor; penStyle = saveStyle; penWidth = saveWidth; } if (gridWidth) { saveColor = penColor; saveStyle = penStyle; saveWidth = penWidth; setPen(gridColor, gridStyle, 1); GetClientRect(hwnd, &rect); for (i = rect.left + gridWidth; i < rect.right; i += gridWidth) line(i, rect.top, i, rect.bottom); for (i = rect.top + gridWidth; i < rect.bottom; i += gridWidth) line(rect.left, i, rect.right, i); penColor = saveColor; penStyle = saveStyle; penWidth = saveWidth; } this->createPen(); this->createBrush(); prevPos = nextPos; this->paint(); this->deletePen(); this->deleteBrush(); EndPaint(win, &paintStruct); device = 0; return 0; case WM_SIZE: atBottom = maxY != 0 && vScrollPos + clientY >= maxY; clientX = LOWORD(lParam); clientY = HIWORD(lParam); lowerRight.x = upperLeft.x + clientX - 1; lowerRight.y = upperLeft.y + clientY - 1; if (maxY > clientY && lowerRight.y > maxY) { lowerRight.y = maxY; upperLeft.y = lowerRight.y - clientY + 1; } if (maxX > clientX && lowerRight.x > maxX) { lowerRight.x = maxX; upperLeft.x = lowerRight.x - clientX + 1; } maxVScroll = max(0, maxY - clientY); maxHScroll = max(0, maxX - clientX); vScrollPos = min(vScrollPos, maxVScroll); hScrollPos = min(hScrollPos, maxHScroll); SetScrollRange(hwnd, SB_VERT, 0, maxVScroll, FALSE); SetScrollRange(hwnd, SB_HORZ, 0, maxHScroll, FALSE); SetScrollPos(hwnd, SB_VERT, vScrollPos, TRUE); SetScrollPos(hwnd, SB_HORZ, hScrollPos, TRUE); if (atBottom) scrollToBottom(); nextPos = prevPos; device = GetDC(win); if (cartesianWidth) { SetMapMode(device, MM_LOENGLISH); SetViewportOrg(device, clientX / 2, clientY / 2); GetClientRect(hwnd, &rect); DPtoLP(device, (LPPOINT) &rect, 2); upperLeft.x = rect.left; upperLeft.y = rect.top; lowerRight.x = rect.right; lowerRight.y = rect.bottom; } this->size((int) LOWORD(lParam), (int) HIWORD(lParam)); ReleaseDC(win, device); device = 0; return 0; case WM_VSCROLL: switch (wParam) { case SB_TOP: chgV = -vScrollPos; break; case SB_BOTTOM: chgV = maxVScroll - vScrollPos; break; case SB_LINEUP: chgV = -charY; break; case SB_LINEDOWN: chgV = charY; break; case SB_PAGEUP: chgV = min(-charY, -clientY); break; case SB_PAGEDOWN: chgV = max(charY, clientY); break; case SB_THUMBTRACK: chgV = LOWORD(lParam) - vScrollPos; break; default: chgV = 0; break; } chgV = max(-vScrollPos, min(chgV, maxVScroll - vScrollPos)); if (chgV != 0) { upperLeft.y += chgV; lowerRight.y += chgV; if (upperLeft.y < 0) { upperLeft.y = 0; lowerRight.y = clientY - 1; } else if (maxY > clientY && lowerRight.y > maxY) { lowerRight.y = maxY; upperLeft.y = lowerRight.y - clientY + 1; } vScrollPos += chgV; ScrollWindow(hwnd, 0, -chgV, NULL, NULL); SetScrollPos(hwnd, SB_VERT, vScrollPos, TRUE); UpdateWindow(hwnd); } return 0; case WM_HSCROLL: switch (wParam) { case SB_TOP: chgH = -hScrollPos; break; case SB_BOTTOM: chgH = maxHScroll - hScrollPos; break; case SB_LINEUP: chgH = -charX; break; case SB_LINEDOWN: chgH = charX; break; case SB_PAGEUP: chgH = min(-charX, -clientX); break; case SB_PAGEDOWN: chgH = max(charX, clientX); break; case SB_THUMBTRACK: chgH = LOWORD(lParam) - hScrollPos; break; default: chgH = 0; break; } chgH = max(-hScrollPos, min(chgH, maxHScroll - hScrollPos)); if (chgH != 0) { upperLeft.x += chgH; lowerRight.x += chgH; if (upperLeft.x < 0) { upperLeft.x = 0; lowerRight.x = clientX - 1; } else if (maxX > clientX && lowerRight.x > maxX) { lowerRight.x = maxX; upperLeft.x = lowerRight.x - clientX + 1; } hScrollPos += chgH; ScrollWindow(hwnd, -chgH, 0, NULL, NULL); SetScrollPos(hwnd, SB_HORZ, hScrollPos, TRUE); UpdateWindow(hwnd); } return 0; case WM_TIMER: device = GetDC(win); this->timer(); ReleaseDC(win, device); device = 0; return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(win, msg, wParam, lParam); } void window::rectangle(const int x, const int y, const int wdth, const int hght) { Rectangle(device, x, y, x + wdth, y + hght); } int window::right() { return lowerRight.x; } void window::scrollToBottom() { lowerRight.y = max(maxY, clientY); upperLeft.y = lowerRight.y - clientY + 1; vScrollPos = max(0, maxY - clientY); SetScrollPos(hwnd, SB_VERT, vScrollPos, TRUE); } void window::scrollToLeft() { upperLeft.x = 0; lowerRight.x = clientX - 1; hScrollPos = 0; SetScrollPos(hwnd, SB_HORZ, hScrollPos, TRUE); } void window::scrollToRight() { lowerRight.x = max(maxX, clientX); upperLeft.x = lowerRight.x - clientX + 1; hScrollPos = max(0, maxX - clientX); SetScrollPos(hwnd, SB_HORZ, hScrollPos, TRUE); } void window::scrollToTop() { upperLeft.y = 0; lowerRight.y = clientY - 1; vScrollPos = 0; SetScrollPos(hwnd, SB_VERT, vScrollPos, TRUE); } void window::setBrush(color c) { switch (c) { case black : brushColor = RGB( 0, 0, 0); break; case blue : brushColor = RGB( 0, 0, 128); break; case green : brushColor = RGB( 0, 128, 0); break; case cyan : brushColor = RGB( 0, 128, 128); break; case red : brushColor = RGB(128, 0, 0); break; case magenta : brushColor = RGB(128, 0, 128); break; case brown : brushColor = RGB(128, 128, 0); break; case gray : brushColor = RGB(128, 128, 128); break; case brightBlue : brushColor = RGB( 0, 0, 255); break; case brightGreen : brushColor = RGB( 0, 255, 0); break; case brightCyan : brushColor = RGB( 0, 255, 255); break; case brightRed : brushColor = RGB(255, 0, 0); break; case brightMagenta: brushColor = RGB(255, 0, 255); break; case brightYellow : brushColor = RGB(255, 255, 0); break; case brightGray : brushColor = RGB(192, 192, 192); break; case white : brushColor = RGB(255, 255, 255); break; default : brushColor = RGB( 0, 0, 0); break; } this->createBrush(); } void window::setMaxX(int n) { maxX = n; } void window::setMaxY(int n) { maxY = n; } void window::setMenu(menuItem m[], int n) { HMENU popup[16]; int i, j, items = 0; if (n < 1 || m[0].number != 0) return; if (hmenu != 0) DestroyMenu(hmenu); hmenu = CreateMenu(); for (i = 0; i < n; i++) { if (m[i].number == 0) popup[items++] = CreateMenu(); else AppendMenu(popup[items - 1], MF_STRING, m[i].number, m[i].name); } for (i = 0, j = 0; i < n; i++) { if (m[i].number == 0) AppendMenu(hmenu, MF_POPUP, popup[j++], m[i].name); } SetMenu(hwnd, hmenu); }; void window::setPen(color c, lineStyle s, int w) { switch (c) { case black : penColor = RGB( 0, 0, 0); break; case blue : penColor = RGB( 0, 0, 128); break; case green : penColor = RGB( 0, 128, 0); break; case cyan : penColor = RGB( 0, 128, 128); break; case red : penColor = RGB(128, 0, 0); break; case magenta : penColor = RGB(128, 0, 128); break; case brown : penColor = RGB(128, 128, 0); break; case gray : penColor = RGB(128, 128, 128); break; case brightBlue : penColor = RGB( 0, 0, 255); break; case brightGreen : penColor = RGB( 0, 255, 0); break; case brightCyan : penColor = RGB( 0, 255, 255); break; case brightRed : penColor = RGB(255, 0, 0); break; case brightMagenta: penColor = RGB(255, 0, 255); break; case brightYellow : penColor = RGB(255, 255, 0); break; case brightGray : penColor = RGB(192, 192, 192); break; case white : penColor = RGB(255, 255, 255); break; default : penColor = RGB( 0, 0, 0); break; } switch (s) { case solidLine : penStyle = PS_SOLID; break; case dashedLine: penStyle = PS_DASH; break; case dottedLine: penStyle = PS_DOT; break; case nullLine : penStyle = PS_NULL; break; default : penStyle = PS_SOLID; break; } penWidth = w; this->createPen(); } void window::setTextPosition(int x, int y) { nextPos.x = x; nextPos.y = y; prevPos = nextPos; }; int window::show(int flag) { return ShowWindow(hwnd, flag); } int window::textHeight() { return metric.tmHeight + metric.tmExternalLeading; } int window::textWidth(char * str) { HDC hdc = GetDC(hwnd); int width = 0; if (hdc) { width = LOWORD(GetTextExtent(hdc, str, strlen(str))); ReleaseDC(hwnd, hdc); } return width; } void window::textPosition(int & x, int & y) { x = nextPos.x; y = nextPos.y; } int window::top() { return upperLeft.y; } void window::uncheckMenuItem(int item) { if (hmenu) CheckMenuItem(hmenu, item, MF_UNCHECKED); } void window::update() { InvalidateRect(hwnd, NULL, FALSE); } void window::vector(const int fromX, const int fromY, const int toX, const int toY) { POINT newOrigin; DWORD origin; double theta; int newFromX, newFromY, newToX, newToY; int x, y; MoveTo(device, fromX, fromY); LineTo(device, toX, toY); newOrigin.x = toX; newOrigin.y = toY; LPtoDP(device, &newOrigin, 1); origin = GetViewportOrg(device); SetViewportOrg(device, newOrigin.x, newOrigin.y); newFromX = fromX - toX; newFromY = fromY - toY; newToX = newToY = 0; if (newFromX == 0) { if (newFromY <= newToY) theta = 3.0 * M_PI / 2.0; else theta = M_PI / 2.0; } else if (newToX > newFromX) theta = atan(double(newFromY)/double(newFromX)) + M_PI; else theta = atan(double(newFromY)/double(newFromX)); x = 10.0 * cos(theta + M_PI/6.0) + 0.5; y = 10.0 * sin(theta + M_PI/6.0) + 0.5; MoveTo(device, 0, 0); LineTo(device, x, y); x = 10.0 * cos(theta - M_PI/6.0) + 0.5; y = 10.0 * sin(theta - M_PI/6.0) + 0.5; MoveTo(device, 0, 0); LineTo(device, x, y); SetViewportOrg(device, LOWORD(origin), HIWORD(origin)); MoveTo(device, toX, toY); } int window::width() { return clientX; } // --- Application ----------------------------------------------------- application::application(char * apName, char * apTitle, HANDLE thisInst, HANDLE prevInst, int wdth, int hght) { if (wdth != CW_USEDEFAULT || wdth != CW_USEDEFAULT) { RECT r = { 0, 0, wdth - 1, hght - 1 }; AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, 0); wdth = r.right - r.left + 1; hght = r.bottom - r.top + 1; } theApplication = this; brushColor = RGB(255, 255, 255); cartesianColor = black; cartesianStyle = nullLine; cartesianWidth = 0; currentBrush = 0; currentPen = 0; gridColor = black; gridStyle = nullLine; gridWidth = 0; device = 0; hmenu = 0; hScrollPos = 0; initialWidth = wdth; initialHeight = hght; lowerRight.x = 0; lowerRight.y = 0; maxX = 0; maxY = 0; nextPos.x = -1; nextPos.y = -1; penColor = RGB(0, 0, 0); penStyle = PS_SOLID; penWidth = 1; vScrollPos = 0; prevPos.x = -1; prevPos.y = -1; name = apName; title = apTitle; thisInstance = thisInst; upperLeft.x = 0; upperLeft.y = 0; prevInstance = prevInst; timerInterval = 0; } void application::command(int) { /* do nothing */ } void application::create() { /* do nothing */ } void application::doubleClick(int, int) { /* do nothing */ } void application::mouseDown(int, int) { /* do nothing */ } int application::okBox(char * msg, char * caption) { int saveTimer = timerInterval; int result; if (saveTimer) stopTimer(); result = MessageBox(hwnd, msg, caption, MB_OK | MB_TASKMODAL); if (saveTimer) startTimer(saveTimer); return result; } int application::debugBox(char * msg) { int saveTimer = timerInterval; int result; if (saveTimer) stopTimer(); result = MessageBox(hwnd, msg, "DEBUG", MB_OKCANCEL | MB_TASKMODAL); if (result == IDCANCEL) abort(); if (saveTimer) startTimer(saveTimer); return result; } void application::paint() { /* do nothing */ } void application::quit() { DestroyWindow(hwnd); } BOOL application::registerApplication(WNDCLASS * wc) { wc->cbClsExtra = 0; wc->cbWndExtra = 0; wc->hbrBackground = GetStockObject(WHITE_BRUSH); wc->hCursor = LoadCursor(NULL, IDC_ARROW); wc->hIcon = LoadIcon(NULL, "END"); wc->hInstance = thisInstance; wc->lpfnWndProc = WndProc; wc->lpszClassName = name; wc->lpszMenuName = NULL; wc->style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; return RegisterClass(wc); } int application::run() { int cmdShow = SW_SHOWNORMAL; DWORD style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL; MSG message; WNDCLASS wndClass; if (!prevInstance) if (!this->registerApplication(&wndClass)) return 0; hwnd = CreateWindow(name, // window class name title, // main window title style, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position initialWidth, // initial x size initialHeight, // initial y size NULL, // parent window NULL, // menu thisInstance, // application instance NULL); // creation parameters ShowWindow(hwnd, cmdShow); UpdateWindow(hwnd); while (GetMessage(&message, 0, 0, 0)) { TranslateMessage(&message); DispatchMessage(&message); } return message.wParam; } void application::size(int, int) { /* do nothing */ } void application::startTimer(unsigned int ms) { if (timerInterval != 0) KillTimer(hwnd, 1); timerInterval = ms; if (SetTimer(hwnd, 1, timerInterval, NULL) == 0) { MessageBox(hwnd, "Too many timers. Close some applications", "Little Application Framework", MB_ICONEXCLAMATION | MB_OK); timerInterval = 0; } } void application::stopTimer() { if (timerInterval != 0) { KillTimer(hwnd, 1); timerInterval = 0; } } void application::sendTimerMessage() { PostMessage(hwnd, WM_TIMER, 1, 0); } void application::timer() { /* do nothing */ } // --- Button ---------------------------------------------------------- button::button() { win = 0; titleValue = 0; idValue = 0; widthValue = 0; heightValue = 0; upperLeft.x = 0; upperLeft.y = 0; hwnd = 0; } button::button(window * parentWin, char * buttonTitle, int buttonId, int buttonWidth, int buttonHeight, int x, int y) { DWORD style = WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | BS_PUSHBUTTON; win = parentWin; titleValue = buttonTitle; idValue = buttonId; widthValue = buttonWidth; heightValue = buttonHeight; upperLeft.x = x; upperLeft.y = y; hwnd = CreateWindow("button", // window class name titleValue, // window title style, // window style upperLeft.x, // initial x position upperLeft.y, // initial y position widthValue, // initial x size heightValue, // initial y size win->handle(), // parent window idValue, // menu win->instance(), // application instance NULL); // creation parameters } button::button(const button & initial) { win = initial.win; titleValue = initial.titleValue; idValue = initial.idValue; widthValue = initial.widthValue; heightValue = initial.heightValue; upperLeft = initial.upperLeft; hwnd = initial.hwnd; #if 0 DWORD style = WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | BS_PUSHBUTTON; win = initial.win; titleValue = initial.titleValue; idValue = initial.idValue; widthValue = initial.widthValue; heightValue = initial.heightValue; upperLeft = initial.upperLeft; hwnd = CreateWindow("button", // window class name titleValue, // window title style, // window style upperLeft.x, // initial x position upperLeft.y, // initial y position widthValue, // initial x size heightValue, // initial y size win->handle(), // parent window idValue, // menu win->instance(), // application instance NULL); // creation parameters #endif } button::~button() { #if 0 if (hwnd) DestroyWindow(hwnd); #endif } // --- EditText -------------------------------------------------------- editText::editText() { win = 0; idValue = 0; upperLeft.x = 0; upperLeft.y = 0; heightValue = 0; widthValue = 0; style = 0; hwnd = 0; } editText::editText(window * parentWin, int ctlId, int ctlWidth, int ctlHeight, int x, int y, textAlign align, int border, char * str) { style = WS_CHILD | WS_VISIBLE | ES_MULTILINE; switch (align) { case leftText: style |= ES_LEFT; break; case centerText: style |= ES_CENTER; break; case rightText: style |= ES_RIGHT; break; default: style |= ES_LEFT; break; } if (border) style |= WS_BORDER; win = parentWin; idValue = ctlId; widthValue = ctlWidth; heightValue = ctlHeight; upperLeft.x = x; upperLeft.y = y; hwnd = CreateWindow("edit", // window class name str, // window title style, // window style upperLeft.x, // initial x position upperLeft.y, // initial y position widthValue, // initial x size heightValue, // initial y size win->handle(), // parent window idValue, // menu win->instance(), // application instance NULL); // creation parameters if (hwnd) SendMessage(hwnd, EM_FMTLINES, 0, 0); } editText::editText(const editText & initial) { char str[512]; GetWindowText(initial.hwnd, str, sizeof(str)); win = initial.win; idValue = initial.idValue; upperLeft = initial.upperLeft; heightValue = initial.heightValue; widthValue = initial.widthValue; style = initial.style; hwnd = CreateWindow("edit", // window class name str, // window title style, // window style upperLeft.x, // initial x position upperLeft.y, // initial y position widthValue, // initial x size heightValue, // initial y size win->handle(), // parent window 0, // menu win->instance(), // application instance NULL); // creation parameters if (hwnd) SendMessage(hwnd, EM_FMTLINES, 0, 0); } editText::~editText() { if (hwnd) DestroyWindow(hwnd); } void editText::setFocus() { if (hwnd) SetFocus(hwnd); } int editText::size() { int cols, rows; HDC device; TEXTMETRIC metric; DWORD textExtent; if (hwnd) { device = GetDC(hwnd); GetTextMetrics(device, &metric); textExtent = GetTextExtent(device, "i", 1); ReleaseDC(hwnd, device); cols = widthValue / LOWORD(textExtent); rows = heightValue / (metric.tmHeight + metric.tmExternalLeading); return rows * cols; } else return 0; } char * editText::text(char * str) { int i, n; if (hwnd) { n = GetWindowText(hwnd, str, size()); str[n] = '\0'; // replace newline and carriage return with space for (i = 0; str[i]; i++) if (str[i] == '\n' || str[i] == '\r') str[i] = ' '; // strip trailing spaces for (i = strlen(str) - 1; i >= 0 && str[i] == ' '; i--) str[i] = '\0'; } else *str = '\0'; return str; } editText & editText::operator = (const editText & right) { char str[512] = {0}; GetWindowText(right.hwnd, str, sizeof(str)); win = right.win; idValue = right.idValue; upperLeft = right.upperLeft; heightValue = right.heightValue; widthValue = right.widthValue; style = right.style; hwnd = CreateWindow("edit", // window class name str, // window title style, // window style upperLeft.x, // initial x position upperLeft.y, // initial y position widthValue, // initial x size heightValue, // initial y size win->handle(), // parent window 0, // menu win->instance(), // application instance NULL); // creation parameters if (hwnd) SendMessage(hwnd, EM_FMTLINES, 0, 0); return *this; } editText & editText::operator << (char * str) { int currentLength = GetWindowTextLength(hwnd); char newText[512]; if (!hwnd || currentLength + strlen(str) + 1 > sizeof(newText)) return *this; GetWindowText(hwnd, newText, sizeof(newText)); strcpy(newText + currentLength, str); SetWindowText(hwnd, newText); return *this; } editText & editText::operator << (char ch) { char str[2]; if (ch == '\f' && hwnd) { SetWindowText(hwnd, ""); return *this; } else { sprintf(str, "%c", ch); return (*this) << str; } } editText & editText::operator << (int n) { char str[32]; sprintf(str, "%d", n); return (*this) << str; } editText & editText::operator << (editText & (*fn)(editText &)) { return fn(*this); } editText & eraseText(editText & txt) { return txt << '\f'; } // --- staticText ------------------------------------------------------ staticText::staticText() { win = 0; upperLeft.x = 0; upperLeft.y = 0; heightValue = 0; widthValue = 0; hwnd = 0; } staticText::staticText(window * parentWin, int ctlWidth, int ctlHeight, int x, int y, textAlign align, int border, char * str) { style = WS_CHILD | WS_VISIBLE; switch (align) { case leftText: style |= SS_LEFT; break; case centerText: style |= SS_CENTER; break; case rightText: style |= SS_RIGHT; break; default: style |= SS_LEFT; break; } if (border) style |= WS_BORDER; win = parentWin; widthValue = ctlWidth; heightValue = ctlHeight; upperLeft.x = x; upperLeft.y = y; hwnd = CreateWindow("static", // window class name str, // window title style, // window style upperLeft.x, // initial x position upperLeft.y, // initial y position widthValue, // initial x size heightValue, // initial y size win->handle(), // parent window 0, // menu win->instance(), // application instance NULL); // creation parameters } staticText::staticText(const staticText & initial) { char str[512]; GetWindowText(initial.hwnd, str, sizeof(str)); win = initial.win; upperLeft = initial.upperLeft; heightValue = initial.heightValue; widthValue = initial.widthValue; style = initial.style; hwnd = CreateWindow("static", // window class name str, // window title style, // window style upperLeft.x, // initial x position upperLeft.y, // initial y position widthValue, // initial x size heightValue, // initial y size win->handle(), // parent window 0, // menu win->instance(), // application instance NULL); // creation parameters } staticText::~staticText() { if (hwnd) DestroyWindow(hwnd); } staticText & staticText::operator = (const staticText & right) { char str[512]; GetWindowText(right.hwnd, str, sizeof(str)); win = right.win; upperLeft = right.upperLeft; heightValue = right.heightValue; widthValue = right.widthValue; style = right.style; hwnd = CreateWindow("static", // window class name str, // window title style, // window style upperLeft.x, // initial x position upperLeft.y, // initial y position widthValue, // initial x size heightValue, // initial y size win->handle(), // parent window 0, // menu win->instance(), // application instance NULL); // creation parameters return *this; } int staticText::operator == (const staticText & right) { if (win == right.win && upperLeft.x == right.upperLeft.x && upperLeft.y == right.upperLeft.y && heightValue == right.heightValue && widthValue == right.widthValue) return 1; else return 0; } staticText & staticText::operator << (char * str) { int currentLength = GetWindowTextLength(hwnd); char newText[512]; if (!hwnd || currentLength + strlen(str) + 1 > sizeof(newText)) return *this; GetWindowText(hwnd, newText, sizeof(newText)); strcpy(newText + currentLength, str); SetWindowText(hwnd, newText); return *this; } staticText & staticText::operator << (char ch) { char str[2]; if (ch == '\f' && hwnd) { SetWindowText(hwnd, ""); return *this; } else { sprintf(str, "%c", ch); return (*this) << str; } } staticText & staticText::operator << (int n) { char str[32]; sprintf(str, "%d", n); return (*this) << str; } staticText & staticText::operator << (staticText & (*fn)(staticText &)) { return fn(*this); } staticText & eraseText(staticText & txt) { return txt << '\f'; } // --- listBox --------------------------------------------------------- listBox::listBox() { win = 0; idValue = 0; upperLeft.x = 0; upperLeft.y = 0; heightValue = 0; widthValue = 0; style = 0; hwnd = 0; } listBox::listBox(window * parentWin, int ctlId, int ctlWidth, int ctlHeight, int x, int y, int sort) { style = WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | LBS_NOTIFY; if (sort) style |= LBS_SORT; win = parentWin; idValue = ctlId; widthValue = ctlWidth; heightValue = ctlHeight; upperLeft.x = x; upperLeft.y = y; hwnd = CreateWindow("listbox", // window class name NULL, // window title style, // window style upperLeft.x, // initial x position upperLeft.y, // initial y position widthValue, // initial x size heightValue, // initial y size win->handle(), // parent window idValue, // menu win->instance(), // application instance NULL); // creation parameters } listBox::listBox(const listBox & initial) { win = initial.win; idValue = initial.idValue; upperLeft = initial.upperLeft; heightValue = initial.heightValue; widthValue = initial.widthValue; style = initial.style; hwnd = CreateWindow("listbox", // window class name NULL, // window title style, // window style upperLeft.x, // initial x position upperLeft.y, // initial y position widthValue, // initial x size heightValue, // initial y size win->handle(), // parent window idValue, // menu win->instance(), // application instance NULL); // creation parameters } listBox & listBox::operator = (const listBox & right) { win = right.win; idValue = right.idValue; upperLeft = right.upperLeft; heightValue = right.heightValue; widthValue = right.widthValue; style = right.style; hwnd = CreateWindow("listbox", // window class name NULL, // window title style, // window style upperLeft.x, // initial x position upperLeft.y, // initial y position widthValue, // initial x size heightValue, // initial y size win->handle(), // parent window idValue, // menu win->instance(), // application instance NULL); // creation parameters return *this; } listBox::~listBox() { if (hwnd) DestroyWindow(hwnd); } void listBox::clear() { SendMessage(hwnd, LB_RESETCONTENT, 0, 0); // trick to get scroll bar to disappear SendMessage(hwnd, LB_ADDSTRING, 0, (DWORD) " "); SendMessage(hwnd, LB_RESETCONTENT, 0, 0); } char * listBox::add(char * str) { SendMessage(hwnd, LB_ADDSTRING, 0, (DWORD) str); return str; } // --- Miscellaneous Functions ----------------------------------------- // display a debug box void debugBox() { if (theApplication) { // null terminate the debug message buffer debugBuffer[dout.tellp()] = '\0'; // display message box theApplication->debugBox(debugBuffer); // reset the stream output for dout dout.seekp(0); } } // generate a random number from lo though hi int randomInt(int lo, int hi) { static int firstTime = 1; int tmp; if (firstTime) // seed random generator 1st time only { time_t t; srand(time(&t)); firstTime = 0; } if (hi < lo) // make sure lo <= hi { tmp = hi; hi = lo; lo = tmp; } return (rand() % (hi - lo + 1)) + lo; } SHAR_EOF fi exit 0 # End of shell archive