// --------------------------------------------------------------------------------------- //
// AppWindow example implementation for a blank window, to show the fundaentals of //
// programing a blank DWinLib window. //
// --------------------------------------------------------------------------------------- //
#include "PrecompiledHeaders.h"
#pragma hdrstop
#include "AppWindow.h"
#include "Application.h"
#include "DwlMdiManager.h"
#include "DwlScrollbar.h"
#include "DwlIniFile.h"
#include "HelpFileConstants.h"
#include "WinMainO.h"
extern WinMainO * gWinMain;
#include "UIManager.h"
#include "Globals.h"
extern Globals gGlobals;
namespace {
wString winCaption = _T("Main Application Window");
wString winClassName = _T("DockerWindow");
int timeBoxHeight = 80;
}
AppWindow::AppWindow(DwlControl * parent, int x, int y, int width, int height, bool openingFile) :
DwlMdiBaseWin(parent, x, y, width, height),
verScrollAmtC(0),
horScrollAmtC(0),
scrollbarWidthC(15),
mouseSquareC(NULL) {
appC.reset(new Application(this, openingFile));
horScrollC.reset(new DwlScrollbar(this, DwlScrollbar::Horizontal, 0,
clientHeight()-gGlobals.uiMan().scrollbarWidth(), clientWidth(),
gGlobals.uiMan().scrollbarWidth()));
verScrollC.reset(new DwlScrollbar(this, DwlScrollbar::Vertical,
clientWidth()-gGlobals.uiMan().scrollbarWidth(), 0,
gGlobals.uiMan().scrollbarWidth(), clientHeight()));
horScrollC->showWindow(true);
verScrollC->showWindow(true);
int ch = clientHeight();
int cw = clientWidth();
int sbw = gGlobals.uiMan().scrollbarWidth();
horScrollC->moveWindow(0, ch-sbw, cw-sbw, sbw);
verScrollC->moveWindow(cw-sbw, 0, sbw, ch-sbw);
if (!openingFile) {
//Do whatever is needed:
}
else {
//Do whatever else is needed:
}
wHelpTopicC = HFC::AppWindow;
updateScrolls();
}
AppWindow::~AppWindow() {
gWinMain->winMdiManager()->removeWindow(this);
}
wString AppWindow::shortName() {
return appC->shortName();
}
wString AppWindow::fullName() {
return appC->fullName();
}
bool AppWindow::canClose() {
return appC->canClose();
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// BEGINNING OF ROUTINES COMMON TO DWINLIB APPWINDOWS
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
LRESULT AppWindow::wClose() {
if (!appC->canClose()) return 0;
LRESULT ret = DefMDIChildProc(hwndC, WM_CLOSE, 0, 0);
delete this;
gWinMain->updateUI();
return ret;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// END OF ROUTINES COMMON TO DWINLIB APPWINDOWS
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
LRESULT AppWindow::wPosChanged(WINDOWPOS * p) {
updateScrolls();
RECT r;
r.top = 0;
r.left = 0;
r.right = clientWidth() - (verScrollC->visible() ? verScrollC->width() : 0);
r.bottom = clientHeight() - (horScrollC->visible() ? horScrollC->height() : 0);
InvalidateRect(hwndC, &r, TRUE);
UpdateWindow(hwndC);
return DefMDIChildProc(hwndC, WM_WINDOWPOSCHANGED, 0, (LPARAM) p);
}
LRESULT AppWindow::wPosChanging(WINDOWPOS * wp) {
if (wp->cy < 100) wp->cy = 100;
return DefMDIChildProc(hwndC, WM_WINDOWPOSCHANGING, 0, (LPARAM) wp);
}
LRESULT AppWindow::wPaint(DwlDC & dc) {
//Double-buffer for cleanliness. Could be improved by holding a bitmap in memory at all
//times, but as this is a non-critical sample application, we will save ourselves the
//time.
DwlDC mDC(dc); //Memory DC
//Figure out how big our bitmap needs to be...
int bmpWidth = horScrollAmtC+clientWidth();
if (bmpWidth<SLW) bmpWidth = SLW;
int bmpHeight = clientHeight() + verScrollAmtC;
if (bmpHeight<SLW) bmpHeight = SLW;
HBITMAP buffBitmap = CreateCompatibleBitmap(dc(), bmpWidth, bmpHeight);
//The previous bitmap cannot be created by 'CreateCompatibleBitmap(mDC()...)'
//The output becomes dithered in a bad way...
mDC.bitmap(buffBitmap, true);
RECT r1 = { 0, 0, bmpWidth, bmpHeight };
HBRUSH brush = (HBRUSH)GetStockObject(GRAY_BRUSH);
FillRect(mDC(), &r1, brush);
RECT r2 = { 0, 0, SLW, SLW };
brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
FillRect(mDC(), &r2, brush);
std::vector<StupidSquare> & squares = appC->squares();
size_t num = squares.size();
for (size_t i=0; i<num; ++i) {
if (!squares[i].selected() && !squares[i].hidden()) squares[i].blit(mDC);
}
if (mouseSquareC) blitSelectedSquare(mDC);
if (needToBlitCornerC) {
r1.left = clientWidth()-scrollbarWidthC+horScrollAmtC;
r1.top = clientHeight()-scrollbarWidthC+verScrollAmtC;
r1.bottom = clientHeight()+verScrollAmtC;
r1.right = clientWidth()+horScrollAmtC;
brush = (HBRUSH)GetStockObject(GRAY_BRUSH);
FillRect(mDC(), &r1, brush);
}
BitBlt(dc(), -horScrollAmtC, -verScrollAmtC, bmpWidth, bmpHeight, mDC(), 0, 0, SRCCOPY);
return 0;
}
void AppWindow::blitSelectedSquare(DwlDC & dc) {
if (!mouseSquareC) return;
mouseSquareC->blitSelected(dc, mousePointC.x-pointInSquareC.x,
mousePointC.y-pointInSquareC.y);
}
void AppWindow::updateDataAndViews() {
//Do whatever is needed to get the views to update themselves
updateScrolls();
RECT r;
r.top = 0;
r.left = 0;
r.right = clientWidth() - (verScrollC->visible() ? verScrollC->width() : 0);
r.bottom = clientHeight() - (horScrollC->visible() ? horScrollC->height() : 0);
InvalidateRect(hwndC, &r, TRUE);
}
void AppWindow::updateScrolls() {
needToBlitCornerC = false;
verScrollC->showWindow(false);
horScrollC->showWindow(false);
int w = clientWidth();
int h = clientHeight();
//Determine which scrollbars need to be visible
bool showBottomScroll(w<SLW+horScrollAmtC ? true : false);
bool showRightScroll(h<SLW+verScrollAmtC ? true : false);
if (showBottomScroll && !showRightScroll && h<SLW+scrollbarWidthC)
showRightScroll = true;
if (showRightScroll && !showBottomScroll && w<SLW+scrollbarWidthC)
showBottomScroll = true;
//Now, change the 'horScrollAmtC' and 'verScrollAmtC' variables, if the user's action is
//attempting to uncover real estate below or beside the output 'square', and there is
//real estate above or to the left of the window that is 'covered up'.
int scrollbarWidth(showRightScroll ? scrollbarWidthC : 0);
if (showBottomScroll && clientWidth()+horScrollAmtC-scrollbarWidth > SLW)
horScrollAmtC = SLW-clientWidth()+scrollbarWidth;
scrollbarWidth = showBottomScroll ? scrollbarWidthC : 0;
if (showRightScroll && clientHeight() + verScrollAmtC-scrollbarWidth > SLW)
verScrollAmtC = SLW-clientHeight()+scrollbarWidth;
//Finally, show the appropriate scroll bars.
if (showRightScroll && showBottomScroll) {
horScrollC->showWindow(true);
verScrollC->showWindow(true);
verScrollC->moveWindow(w-scrollbarWidthC, 0, scrollbarWidthC, h-scrollbarWidthC);
verScrollC->scrollTicksTotal(SLW);
verScrollC->pageSize(clientHeight()-scrollbarWidthC);
verScrollC->pos(verScrollAmtC);
horScrollC->moveWindow(0, h-scrollbarWidthC, w-scrollbarWidthC, scrollbarWidthC);
horScrollC->scrollTicksTotal(SLW);
horScrollC->pageSize(clientWidth()-scrollbarWidthC);
horScrollC->pos(horScrollAmtC);
needToBlitCornerC = true;
}
else if (showRightScroll) {
verScrollC->showWindow(true);
verScrollC->moveWindow(w-scrollbarWidthC, 0, scrollbarWidthC, h);
verScrollC->scrollTicksTotal(SLW);
verScrollC->pageSize(clientHeight());
verScrollC->pos(verScrollAmtC);
horScrollAmtC = 0;
}
else if (showBottomScroll) {
horScrollC->showWindow(true);
horScrollC->moveWindow(0, h-scrollbarWidthC, w, scrollbarWidthC);
horScrollC->scrollTicksTotal(SLW);
horScrollC->pageSize(clientWidth());
horScrollC->pos(horScrollAmtC);
verScrollAmtC = 0;
}
else {
horScrollAmtC = verScrollAmtC = 0;
}
}
LRESULT AppWindow::wScrollHor(WORD scrollCode, int pos, HWND ) { // scrollHwnd
//This is our response to Windows calling us, and simply calls INTO the scrollbar code.
horScrollC->scroll(scrollCode, pos);
return 0;
}
LRESULT AppWindow::wScrollVer(WORD scrollCode, int pos, HWND ) { // scrollHwnd
//This is our response to Windows calling us, and simply calls INTO the scrollbar code.
verScrollC->scroll(scrollCode, pos);
return 0;
}
void AppWindow::scrollPosChanged(DwlScrollbar * bar, int pos) {
//this is called FROM the scrollbar code, and is used to modify stuff on the window...
if (bar == verScrollC.get()) {
verScrollAmtC = pos;
}
else if (bar == horScrollC.get()) {
horScrollAmtC = pos;
}
RECT r;
r.top = 0;
r.left = 0;
r.right = clientWidth() - (verScrollC->visible() ? verScrollC->width() : 0);
r.bottom = clientHeight() - (horScrollC->visible() ? horScrollC->height() : 0);
InvalidateRect(hwndC, &r, TRUE);
}
LRESULT AppWindow::wMouseDown(Button , WPARAM , SHORT x, SHORT y) { //button flags
//First, determine if the mouse is over a square:
mouseSquareC = NULL;
int numSquares = appC->numSquares();
for (int i=0; i<numSquares; ++i) {
mouseSquareC = appC->squares()[i].touch(x+horScrollAmtC, y+verScrollAmtC);
if (mouseSquareC) break;
}
if (!mouseSquareC) return 0;
SetCapture(hwndC);
mouseSquareC->selected(true);
appC->lastSquare(mouseSquareC);
pointInSquareC.x = x+horScrollAmtC-mouseSquareC->x();
pointInSquareC.y = y+verScrollAmtC-mouseSquareC->y();
mousePointC.x = x+horScrollAmtC;
mousePointC.y = y+verScrollAmtC;
return 0;
}
LRESULT AppWindow::wMouseMove(WPARAM , SHORT x, SHORT y) { // keys
if (!mouseSquareC) return 0;
mousePointC.x = x+horScrollAmtC;
mousePointC.y = y+verScrollAmtC;
constrainSquare(mousePointC.x, mousePointC.y);
//Check for touching other squares, and take appropriate action
int numSquares = appC->numSquares();
for (int i=0; i<numSquares; ++i) {
StupidSquare * square = &appC->squares()[i];
if (square == mouseSquareC) continue;
if (square->hidden()) continue;
if (square->touch(mousePointC.x-pointInSquareC.x, mousePointC.y-pointInSquareC.y)) {
square->hidden(true);
appC->hideUndoer()->hideSquares(mouseSquareC, square);
ReleaseCapture();
mouseSquareC->selected(false);
appC->lastSquare(NULL);
mouseSquareC = NULL;
if (appC->gameOver()) MessageBox(gWinMain->hwnd(), TEXT("Game Over"),
TEXT("Stupid Squares"), MB_OK | MB_APPLMODAL);
return 0;
}
}
RECT r;
r.top = 0;
r.left = 0;
r.right = clientWidth() - (verScrollC->visible() ? verScrollC->width() : 0);
r.bottom = clientHeight() - (horScrollC->visible() ? horScrollC->height() : 0);
InvalidateRect(hwndC, &r, TRUE);
return 0;
}
LRESULT AppWindow::wMouseUp(Button , WPARAM , SHORT x, SHORT y) { //button flags
if (!mouseSquareC) return 0;
mousePointC.x = x+horScrollAmtC;
mousePointC.y = y+verScrollAmtC;
constrainSquare(mousePointC.x, mousePointC.y);
ReleaseCapture();
appC->moveUndoer()->moveSquare(mouseSquareC, mousePointC.x-pointInSquareC.x,
mousePointC.y-pointInSquareC.y);
mouseSquareC = NULL;
return 0;
}
void AppWindow::constrainSquare(LONG & mousePointX, LONG & mousePointY) {
//mousePointX & Y are sent to this routine in ABSOLUTE coordinates, from top-left of
//bitmap. They are NOT screen coordinates at this point. (IE - if the window has been
//scrolled to the right 50 pixels, the 0,0 screen point will correspond to 50,0.)
//The following will constrain the square to being within the visible screen, and not
//going outside of the 700x700 or whatever max screen size (SLW) has been set to.
if (mousePointX-pointInSquareC.x<horScrollAmtC)
mousePointX = horScrollAmtC + pointInSquareC.x;
else if
(mousePointX > horScrollAmtC + clientWidth() - mouseSquareC->width() -
(verScrollC->visible() ? scrollbarWidthC : 0) + pointInSquareC.x)
mousePointX = horScrollAmtC + clientWidth() - mouseSquareC->width() -
(verScrollC->visible() ? scrollbarWidthC : 0) + pointInSquareC.x;
if (mousePointY-pointInSquareC.y<verScrollAmtC)
mousePointY = verScrollAmtC + pointInSquareC.y;
else if
(mousePointY > verScrollAmtC + clientHeight() - mouseSquareC->width() -
(horScrollC->visible() ? scrollbarWidthC : 0) + pointInSquareC.y)
mousePointY = verScrollAmtC + clientHeight() - mouseSquareC->width() -
(horScrollC->visible() ? scrollbarWidthC : 0) + pointInSquareC.y;
if (mousePointX + mouseSquareC->width() > SLW)
mousePointX = SLW - mouseSquareC->width()+pointInSquareC.x;
if (mousePointY + mouseSquareC->width() > SLW)
mousePointY = SLW - mouseSquareC->width()+pointInSquareC.y;
}