From: Jimbo on
Hi

I have made my first complex application using Win32 C++ & I am
looking for advice & criticism on my application logic & architecture.

[b]My application is a simple Paint clone where you can create an
object & choose its colour,size & stroke width etc.[/b]

I was looking for criticism & advice in anyway but mostly on:
[i]- the application architecture & design (use of classes, the way
messages r handled & functions designed
- the programs efficiency & "holeproofness", :P, by that I mean its
ability to crash in some circumstance I didn't consider
- suggestions on what should or could be done better or functions I
could use that I didn't consider[/i]

My application will compile easily & I encourage you to open it & try
it out
Here is a link to download the application executable to try out my
program so you dont have to copy all the code to open the program

http://www.mediafire.com/?zmtgvmmumm3

WinMain:
[source]
#include <windows.h>
#include <string>
#include <sstream> // delete ME!!
#include <stdio.h>

#include "drawClass.h"
#include "object.h"

using namespace std;

#define IDC_OBJCOMBO 1
#define IDL_OBJLIST 2
#define IDB_STROKECOL 3
#define IDE_STROKE 4
#define IDC_STROKETYPE 5
#define IDB_FILLCOLOUR 6
#define ID_TEMPLATE 7
#define IDB_NEW 8
#define IDB_DELETE 9

static HINSTANCE gInstance;
drawClass myDraw; // create drawing
(controller) class
object *objFocus = myDraw.getObjectFocus(); // pointer always points
to selected object
bool drawing = false; // are we drawing
something
bool showRect = false;
string drawStat, objStat;
UINT controlMsgs[] =
{IDC_OBJCOMBO,IDL_OBJLIST,IDB_STROKECOL,IDE_STROKE,IDC_STROKETYPE,
IDB_FILLCOLOUR,ID_TEMPLATE,IDB_NEW,IDB_DELETE};

// Functions List //
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam);
void status();

int WINAPI WinMain(HINSTANCE gInstance, HINSTANCE hPrevInstance, LPSTR
lpCmdLine, int nCmdShow)
{

WNDCLASSEX wc;
HWND hwnd;
MSG Msg;

//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = gInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(DKGRAY_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = "Custom Class";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

// if registration of main class fails
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}

// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
"Custom Class",
"Paint Clone",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 600, 500,
NULL, NULL, gInstance, NULL);

if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

// Step 3: The Message Loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}

// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam)
{
switch(msg)
{
case WM_CREATE:
// Get window dimensions
RECT clientRect;
GetClientRect(hwnd,&clientRect);

myDraw.createGUI
(hwnd,gInstance,clientRect.right,clientRect.bottom,controlMsgs); //
create controls
myDraw.displayObjStats(hwnd,IDL_OBJLIST);
break;
case WM_SIZE:
{ // reallign controls when client window is resized
RECT clientRect;
GetClientRect(hwnd,&clientRect);

myDraw.resizeControls
(hwnd,clientRect.right,clientRect.bottom,controlMsgs);
}
break;
case WM_CHAR:
switch(LOWORD(wParam)) {
case 'a': // 'a' key
// delete rectangle
showRect = false;
InvalidateRect(hwnd,NULL,true);
break;
case 'e':
{ // set object shape ellipse
if (objFocus->setShape(4) == false) {
MessageBox(hwnd,"Failed to set Object
Style: Check setObject(bType)",
"Error",MB_OK | MB_ICONERROR);
}
return 0;
}
break;
case 'l':
{ // set object shape to line
if (objFocus->setShape(1) == false) {
MessageBox(hwnd,"Failed to set Object
Style: Check setObject(bType)",
"Error",MB_OK | MB_ICONERROR);
}
return 0;
}
break;
case 'n':
{
// create new object
// NOTE: Potential error, if objFocus does not
have any x,y values
// then we create a new object, the
previous object will be drawn
// at -1,-1 -1,-1

// if selected object has no x,y values then
its unused
// therefore we dont need to make a new
object, just use this one
if (objFocus->isNew()==false) {
myDraw.createObject
(); // declare/add object to
drawClass
objFocus = myDraw.getObjectFocus
(); // select new object
myDraw.displayObjStats
(hwnd,IDL_OBJLIST); // update LB of Object statistics
//InvalidateRect
(hwnd,NULL,true); // redraw client area
}
}
break;
case 'r':
{ // set object shape to rectangle
if (objFocus->setShape(2) == false) {
MessageBox(hwnd,"Failed to set Object
Style: Check setObject(bType)",
"Error",MB_OK | MB_ICONERROR);
}
return 0;
}
break;
case 's':
{ // set object shape to RoundRect
if (objFocus->setShape(3) == false) {
MessageBox(hwnd,"Failed to set Object
Style: Check setObject(bType)",
"Error",MB_OK | MB_ICONERROR);
}
// set RoundRect xCornerEllipse,yCornerEllipse
objFocus->setCornerDim(20,20);
return 0;
}
break;
case 't':
{ // update LB of Object Statistics
myDraw.displayObjStats(hwnd,IDL_OBJLIST);
MessageBox(hwnd,"LB Updated","Notify",MB_OK);
}
break;
default:
break;
}
break;
case WM_RBUTTONDOWN:
// Select an object by right clicking it
POINTS ptTmp = MAKEPOINTS(lParam);
objFocus = myDraw.selectObject(LOWORD(lParam),HIWORD
(lParam));
break;
case WM_LBUTTONDOWN:
drawing = true;
objFocus->setStartCoord(LOWORD(lParam),HIWORD(lParam));
break;
case WM_MOUSEMOVE:
if (drawing) {
// if shift being pressed
if (GetKeyState(VK_SHIFT) & 0x1000) // we 'and'
function w High order bit to check if key is pressed
{
objFocus->setEndCoord(HIWORD(lParam),HIWORD
(lParam)); // make x = y, ie keep object symetrical
}
else objFocus->setEndCoord(LOWORD(lParam),HIWORD
(lParam)); // set xStart, yStart

InvalidateRect(hwnd,NULL,true); // update client
window area
}
break;
case WM_LBUTTONUP:
objFocus->setEndCoord(LOWORD(lParam),HIWORD(lParam)); //
set xStart, yStart
showRect = true; //
display rectangle
drawing = false;
myDraw.displayObjStats(hwnd,IDL_OBJLIST); //
update LB of Object statistics
InvalidateRect(hwnd,NULL,true);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDB_NEW: // create new object
{
// if selected object has no x,y values then its
unused
// therefore we dont need to make a new object,
just use this one
if (objFocus->isNew()==false) {
myDraw.createObject
(); // declare/add object to
drawClass
objFocus = myDraw.getObjectFocus
(); // select new object
myDraw.displayObjStats
(hwnd,IDL_OBJLIST); // update LB of Object statistics
}
}
break;
case IDB_DELETE: // delete object
{
objFocus = myDraw.deleteObject(hwnd,IDL_OBJLIST);
myDraw.displayObjStats(hwnd,IDL_OBJLIST); //
display object stats
InvalidateRect(hwnd,NULL,true); //
redraw client area
}
break;
case IDC_OBJCOMBO:
{
// if new CB cell is selected
if (HIWORD(wParam) == CBN_SELCHANGE) {
myDraw.changeShape(hwnd,IDC_OBJCOMBO); //
change objFocus's shape
InvalidateRect(hwnd,NULL,true); //
redraw client area
return 0;
}
}
break;
case IDB_STROKECOL: // change objects stroke colour
myDraw.changeStrokeColour(hwnd);
InvalidateRect(hwnd,NULL,true); //
redraw client area
break;
case IDE_STROKE: // change objects stroke(width of
outline)
{
// catch if EB altered
if (HIWORD(wParam) == EN_UPDATE) {
myDraw.strokeWidthChg = true;
myDraw.changeStroke
(hwnd,IDE_STROKE,IDC_STROKETYPE);
InvalidateRect(hwnd,NULL,true); //
redraw client area
}
}
break;
case IDC_STROKETYPE: // change stroke type
(solid,dot,dash...)
{
// if new CB cell is selected
if (HIWORD(wParam) == CBN_SELCHANGE) {
myDraw.strokeBrushChg = true; // strokes
brush CB has been altered
myDraw.changeStroke
(hwnd,IDE_STROKE,IDC_STROKETYPE);
InvalidateRect(hwnd,NULL,true); //
redraw client area
}
}
break;
case IDB_FILLCOLOUR: // set objects interior(fill)
colour
myDraw.changeFillColour(hwnd);
InvalidateRect(hwnd,NULL,true); //
redraw client area
break;
case IDL_OBJLIST:
{
// if LB cell is dbl clicked then select that
object relating to the cell
if (HIWORD(wParam)== LBN_DBLCLK) {
int selectedCell = SendDlgItemMessage
(hwnd,IDL_OBJLIST,LB_GETCURSEL,0,0); // get index of selected cell
int size = SendDlgItemMessage
(hwnd,IDL_OBJLIST,LB_GETCOUNT,0,0);
// if cell index exists
if (selectedCell < size) {
objFocus = myDraw.setObjectFocus
(selectedCell);
SendDlgItemMessage
(hwnd,IDL_OBJLIST,LB_SETCURSEL,selectedCell,0); // select cell
}
}
}
break;
default:
break;
}
break;
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;

hdc = BeginPaint(hwnd,&ps);

// if we are drawing or have made an object/shape
if (drawing || showRect) {
myDraw.showObjects(hdc); // draw all objects
}

status();
SetBkMode(hdc, TRANSPARENT);

// are we:
// - drawing right now - displaying object or not
TextOut(hdc,10,10,drawStat.c_str(),drawStat.length()+1);
TextOut(hdc,10,30,objStat.c_str(),objStat.length()+1);

int aa = myDraw.objGroup.size();
int id = objFocus->ID;
stringstream out;
out.str(""); // clears SS;
out << aa;
string m = out.str();
TextOut(hdc,10,50,m.c_str(),m.length()+1);

out.str(""); // clears SS;
out << id;
string h = out.str();
TextOut(hdc,10,70,h.c_str(),h.length()+1);

EndPaint(hwnd,&ps);
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}

void status() {

if (drawing)
drawStat = "We are drawing";
else drawStat = "We are NOT drawing";

if (showRect)
objStat = "Displaying object";
else objStat = "NOT Displaying object";
}


[/source]

controller class header file
[source]
// Part of Dev C++ Proj - Paint Clone

#ifndef DRAWCLASS_H
#define DRAWCLASS_H

#include <windows.h>
#include <vector>
#include <string>
#include <cstdlib>

#include "object.h"

using namespace std;

class drawClass {

public:
// store pointers(data address) to all created objects in
vector
vector <object*> objGroup;
bool strokeWidthChg; // track if stroke width EB is altered
bool strokeBrushChg; // track if stroke brush CB is altered

drawClass();
drawClass(HDC Hdc);
bool createObject();
object* deleteObject(HWND hwnd, UINT listboxMsg);
bool createGUI(HWND hwnd, HINSTANCE gInstance, int clientW,
int clientH, UINT controlMsg[]);
void resizeControls(HWND hwnd,int clentW, int clentH,UINT
controlMsg[]);
bool setObjectFocus(object *obj);
object* setObjectFocus(int ID);
object* getObjectFocus();
object* selectObject(int mouse_x, int mouse_y);
bool showObjects(HDC hdc);
string toString(int xBegin, int yBegin, int xFin, int yFin,
int xCorn,
int yCorn, int strk, bool vis, string shp,
string brsh); // converts all parameters into one single string
bool displayObjStats(HWND hwnd, UINT listboxMsg);
bool changeShape(HWND hwnd,UINT comboboxMsg);
bool changeStroke(HWND hwnd,UINT editboxMsg,UINT
comboboxMsg);
bool changeStrokeColour(HWND hwnd);
bool changeFillColour(HWND hwnd);

private:
HDC hdc; // not essential to call
showObjects();
int instanceCount;
object *objectFocus; // the object we have selected &
are working with
int listboxLen; // used to increase the width of
LB
COLORREF customColours[]; // record custom colours made in
stroke colour dialog
};

#endif

[/source]

object class header file
[source]
// Part of Dev C++ Proj - Paint Clone

#ifndef OBJECT_H
#define OBJECT_H

#include <windows.h>
#include <string>
#include <cstdlib>

using namespace std;

class object {

public:
int ID; // unique identifier
HRGN objectRgn; // the region inside the shape/object

object();
bool isNew(); // makes sure we dont create a new
object until this object has some x,y values
bool isVisible(); // returns true if object is visible
else false
bool setVisibility(bool vis);
bool setShape(int sType);
string getShape();
bool setStroke(int n);
bool setStrokeBrush(int type);
void setStrokeColour(COLORREF colour);
void setFillColour(COLORREF colour);
bool drawSelf(HDC hdc);
void setStartCoord(int x, int y);
void setEndCoord(int x, int y);
bool setCornerDim(int x, int y); // Set RoundRect
xCornerEllipse,yCornerEllipse var's
void getStats(int &xBegin, int &yBegin, int &xFin, int
&yFin, int &xCorn,
int &yCorn, int &strk, bool &vis, string
&shp, string &brsh); // gives objects' variables stats

private:
bool visible; // toggle visibility of object
string shape; // what shape we draw
string brush;
int stroke;
int xStart, yStart, xEnd, yEnd;
int xCornerEllipse, yCornerEllipse; // only used when
shape="RoundRect"
HPEN hPen;
int strokeBrush;
COLORREF strokeCol;
HBRUSH fillColour;
};

#endif

[/source]

controller implementation file:
[source]
// Part of Dev C++ Proj - Paint Clone

#include <windows.h>
#include <iostream>
#include <string>
#include <sstream>
#include <cstdlib>

#include "drawClass.h"
#include "object.h"

using namespace std;

// default constructor
drawClass::drawClass()
{
instanceCount = 0; // initialise instance number to 0
listboxLen = 150; // default LB HSCROLL width
strokeWidthChg = false;
strokeBrushChg = false;
customColours[16];
createObject();
}

drawClass::drawClass(HDC Hdc)
{
instanceCount = 0; // initialise instance number to 0
hdc = Hdc; // required for showObjects();
listboxLen = 150; // default LB HSCROLL width
strokeWidthChg = false;
strokeBrushChg = false;
createObject();
}

bool drawClass::createObject()
{
// Post: Creates a new object to draw with, gives it a unique int
ID
// & stores object in vector
object* newp = new object;
instanceCount += 1; // advance log of instances
created;
objectFocus = newp; // set object focus to new obj
objectFocus->ID = instanceCount; // set object ID

objGroup.push_back(newp); // store object in vector

return true; // function succeeds
}

object* drawClass::deleteObject(HWND hwnd, UINT listboxMsg)
{
// Pre: Only will allow if there is atleast one object left after
deletion
// of parameter object.
// Post: Parameter object is deleted & objFocus is set to the 1st
object

if (objGroup.size() > 1) {

int decision = MessageBox(hwnd,"Do you want to delete the
most recent object?","Notify",
MB_ICONWARNING|MB_YESNO);

if (decision == IDYES) {
int ID = objectFocus->ID;
// find object in vector
for (int i=0; i<objGroup.size(); i++) { // delete object
& delete object data from LB
if (objGroup.at(i)->ID == ID) {
SendDlgItemMessage
(hwnd,listboxMsg,LB_DELETESTRING,ID,0); // delete cell
SendDlgItemMessage
(hwnd,listboxMsg,LB_SETCURSEL,objGroup.size()-2,0); // select
previous object
delete objGroup.at(i); // OR
delete objectFocus;
objGroup.erase(objGroup.begin()+i); // delete
object from object group
}
}
setObjectFocus(objGroup.back()->ID); // MUST SET OBJECT
FOCUS OR WILL GET ERROR!!
return objectFocus;
}
}
MessageBox(hwnd,"Cannot delete last remaining
object","Error",MB_ICONERROR|MB_OK);
return objectFocus; // function has failed because we cannot
delete the last remaining object
}

bool drawClass::createGUI(HWND hwnd, HINSTANCE gInstance, int clientW,
int clientH, UINT controlMsg[])
{
// Post: Create GUI controls
// I think I need a more efficient way to create all my controls
otherwise this will get huge
// MAYBE there is a more efficient way?
// Also can I group them all together

HWND stBorder = CreateWindowEx
(WS_EX_CLIENTEDGE,"Static","",WS_BORDER | WS_CHILD | WS_VISIBLE |
SS_GRAYRECT,
clientW-200,0,200,350,hwnd,(HMENU)
controlMsg[6],gInstance,NULL);

HWND cbShapeSel = CreateWindowEx(0,"ComboBox","",WS_BORDER |
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST,
clientW-190,50,150,100,hwnd,(HMENU)
controlMsg[0],gInstance,NULL);

HWND lbObjStats = CreateWindowEx(0,"ListBox","",WS_BORDER |
WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_HSCROLL,
clientW-180,90,150,150,hwnd,
(HMENU)controlMsg
[1],gInstance,NULL);

HWND btStroke = CreateWindowEx(0,"Button","Colour",WS_BORDER |
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
clientW-161,250,50,20,hwnd,
(HMENU)controlMsg[2],gInstance,NULL);

HWND edStrokeSel = CreateWindowEx(0,"Edit","",WS_BORDER |
WS_CHILD | WS_VISIBLE | ES_NUMBER,
clientW-180,260,25,20,hwnd,
(HMENU)controlMsg
[3],gInstance,NULL);

HWND cbStrokeType = CreateWindowEx(0,"Combobox","",WS_BORDER |
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST,
clientW-109,247,100,140,hwnd,
(HMENU)controlMsg[4],gInstance,NULL);

HWND btFillColour = CreateWindowEx(0,"Button","Fill
Colour",WS_BORDER|WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
clientW-230,280,75,20,hwnd,
(HMENU)controlMsg[5],gInstance,NULL);

HWND btNew = CreateWindowEx(0,"Button","New",WS_BORDER | WS_CHILD
| WS_VISIBLE | BS_PUSHBUTTON,
clientW-190,20,80,20,hwnd,
(HMENU)controlMsg[7],gInstance,NULL);

HWND btDelete = CreateWindowEx(0,"Button","Delete",WS_BORDER |
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
clientW-90,20,80,20,hwnd,(HMENU)
controlMsg[8],gInstance,NULL);
// add cells to shape combobox
int index;
index = SendDlgItemMessage(hwnd,controlMsg[0],CB_ADDSTRING,0,
(LPARAM)"Line");
SendDlgItemMessage(hwnd,controlMsg[0],CB_SETITEMDATA,index,
1); // associate data with specific cell
index = SendDlgItemMessage(hwnd,controlMsg[0],CB_ADDSTRING,0,
(LPARAM)"Rectangle");
SendDlgItemMessage(hwnd,controlMsg[0],CB_SETITEMDATA,index,2);
index = SendDlgItemMessage(hwnd,controlMsg[0],CB_ADDSTRING,0,
(LPARAM)"RoundRect");
SendDlgItemMessage(hwnd,controlMsg[0],CB_SETITEMDATA,index,3);
index = SendDlgItemMessage(hwnd,controlMsg[0],CB_ADDSTRING,0,
(LPARAM)"Ellipse");
SendDlgItemMessage(hwnd,controlMsg[0],CB_SETITEMDATA,index,4);

// add cells to stroke type combobox
index = SendDlgItemMessage(hwnd,controlMsg[4],CB_ADDSTRING,0,
(LPARAM)"Solid");
SendDlgItemMessage(hwnd,controlMsg[4],CB_SETITEMDATA,index,1);
index = SendDlgItemMessage(hwnd,controlMsg[4],CB_ADDSTRING,0,
(LPARAM)"Dash");
SendDlgItemMessage(hwnd,controlMsg[4],CB_SETITEMDATA,index,2);
index = SendDlgItemMessage(hwnd,controlMsg[4],CB_ADDSTRING,0,
(LPARAM)"Dot");
SendDlgItemMessage(hwnd,controlMsg[4],CB_SETITEMDATA,index,3);
index = SendDlgItemMessage(hwnd,controlMsg[4],CB_ADDSTRING,0,
(LPARAM)"Dash Dot");
SendDlgItemMessage(hwnd,controlMsg[4],CB_SETITEMDATA,index,4);
index = SendDlgItemMessage(hwnd,controlMsg[4],CB_ADDSTRING,0,
(LPARAM)"Dash DotDot");
SendDlgItemMessage(hwnd,controlMsg[4],CB_SETITEMDATA,index,5);
}

void drawClass::resizeControls(HWND hwnd,int clientW, int clentH,UINT
controlMsg[])
{
// Post: Realligns controls when application window is resized
// WEIRD STUFF happens when this function is done.
// For example, it redraws the controls in the correct position BUT
some controls
// are not visible, until I run my mouse over them

SetWindowPos(GetDlgItem(hwnd,controlMsg
[6]),NULL,clientW-200,0,200,350,SWP_NOZORDER);
SetWindowPos(GetDlgItem(hwnd,controlMsg
[1]),HWND_TOPMOST,clientW-180,90,150,150,SWP_NOZORDER);
SetWindowPos(GetDlgItem(hwnd,controlMsg
[2]),HWND_TOPMOST,clientW-161,250,50,20,SWP_NOZORDER);
SetWindowPos(GetDlgItem(hwnd,controlMsg
[3]),HWND_TOPMOST,clientW-190,250,25,20,SWP_NOZORDER);
SetWindowPos(GetDlgItem(hwnd,controlMsg
[5]),HWND_TOPMOST,clientW-190,280,75,20,SWP_NOZORDER);
SetWindowPos(GetDlgItem(hwnd,controlMsg
[7]),HWND_TOPMOST,clientW-190,20,80,20,SWP_NOZORDER);
SetWindowPos(GetDlgItem(hwnd,controlMsg
[8]),HWND_TOPMOST,clientW-90,20,80,20,SWP_NOZORDER);
SetWindowPos(GetDlgItem(hwnd,controlMsg
[0]),HWND_TOP,clientW-190,50,150,100,SWP_NOZORDER);
SetWindowPos(GetDlgItem(hwnd,controlMsg
[4]),HWND_TOP,clientW-109,247,100,140,SWP_NOZORDER);
}

bool drawClass::setObjectFocus(object *obj)
{
// Post: sets the object we are working with
objectFocus = obj;
return true;
}

object* drawClass::setObjectFocus(int ID)
{
// Post: sets the object we are working with
for (int i=0; i<objGroup.size(); i++) {
if (objGroup.at(i)->ID == ID) {
objectFocus = objGroup.at(i);
}
}
return objectFocus; // function failed because no object has this
ID
}

object* drawClass::getObjectFocus()
{
// Post: returns pointer(data address) to the object
// we are currently focused on/Selected
return objectFocus;
}

object* drawClass::selectObject(int mouse_x, int mouse_y)
{
// Post: If mouse position collides with highest(depth) object then
objectFocus = that object

// search vector list of objects from back to find closest, highest
object to mouse pos
for(int i=(objGroup.size()-1); i>=0; i--) {
// if mouse position is inside objects region
if (PtInRegion(objGroup.at(i)->objectRgn,mouse_x,mouse_y) > 0)
{
MessageBox(NULL,"New object selected, mouse is within
region","N",MB_OK);
objectFocus = objGroup.at(i); // set object focus
return objectFocus;
}
}
MessageBox(NULL,"mouse is NOT within any objects'
region","Error",MB_OK);
return objectFocus; // no objects collide with mouse pos
}

bool drawClass::showObjects(HDC hdc)
{
// Pre: Function may fail if hdc variable has not been set
// Post: draws all objects that are set to visible
// (if object.isVisible == true)
for(int i=0; i<objGroup.size(); i++) {
// if object is toggled to visible
if (objGroup.at(i)->isVisible() == true) {
objGroup.at(i)->drawSelf(hdc);
}
}
}

string drawClass::toString(int xBegin, int yBegin, int xFin, int yFin,
int xCorn,
int yCorn, int strk, bool vis, string shp,
string brsh)
{
// Post: Builds 1 single string out of all parameters

stringstream out;
string result;

out.str(""); // clear stringstream
out << xBegin << ", " << yBegin << ", " << xFin << ", " << yFin
<< ", " << xCorn;
out << ", " << yCorn << ", " << strk << ", " << vis;
result = out.str() +", "+shp+", "+brsh;

return result;
}

bool drawClass::displayObjStats(HWND hwnd, UINT listboxMsg)
{
// Post: Get each objects stats & displays it in Listbox
// not very efficient just need to delete the cell we are
updating!!!

string objStats;
bool vis;
string shape,brush;
int xB,yB,xE,yE,xCorn,yCorn,stroke;

// if object is new just display stats in new cell & store unique
ID in that new cell
if (objectFocus->isNew()) {

objectFocus->getStats
(xB,yB,xE,yE,xCorn,yCorn,stroke,vis,shape,brush); // Get object
statistics/details
objStats = toString
(xB,yB,xE,yE,xCorn,yCorn,stroke,vis,shape,brush); // create 1
string out of all stats
// if objStats string length is too big for LB HSCROLL then
increase it
if ((objStats.length()*7) > listboxLen-1) {//3.15
listboxLen = int(objStats.length()*7); //5.75
// Set LB horizontal width to listboxLen
SendDlgItemMessage
(hwnd,listboxMsg,LB_SETHORIZONTALEXTENT,listboxLen,0);
}
// - push string onto LB
int index = SendDlgItemMessage(hwnd,listboxMsg,LB_ADDSTRING,0,
(WPARAM)objStats.c_str());
// Associate this objects unique ID with Listbox's Cell at
index
//SendDlgItemMessage(hwnd,listboxMsg,LB_SETITEMDATA,(WPARAM)
index,(LPARAM)objectFocus->ID);
objectFocus->ID = index; // make objects ID the LB cell index/
num
return true; // exit function
}

objectFocus->getStats
(xB,yB,xE,yE,xCorn,yCorn,stroke,vis,shape,brush); // Get object
statistics/details
objStats = toString
(xB,yB,xE,yE,xCorn,yCorn,stroke,vis,shape,brush); // create 1
string out of all stats
// if objStats string length is too big for LB HSCROLL then
increase it
if ((objStats.length()*7) > listboxLen-1) {//3.15
listboxLen = int(objStats.length()*7); //5.75
// Set LB horizontal width to listboxLen
SendDlgItemMessage
(hwnd,listboxMsg,LB_SETHORIZONTALEXTENT,listboxLen,0);
}
// - push string onto LB
SendDlgItemMessage(hwnd,listboxMsg,LB_DELETESTRING,objectFocus-
>ID,0);
int index = SendDlgItemMessage
(hwnd,listboxMsg,LB_INSERTSTRING,objectFocus->ID,(WPARAM)objStats.c_str
());
objectFocus->ID = index; // make objects ID the LB cell index/num
return true; // exit function

/*
// Get num of listbox cells
int lbSize = SendDlgItemMessage(hwnd,listboxMsg,LB_GETCOUNT,0,0);

// find the LB cell that needs updating
for (int i=0; i<lbSize; i++) {

int cell = SendDlgItemMessage
(hwnd,listboxMsg,LB_GETITEMDATA,i,0);
// if condition met, we have found correct LB cell
if (cell == objectFocus->ID) {
objectFocus->getStats
(xB,yB,xE,yE,xCorn,yCorn,stroke,vis,shape,brush); // Get object
statistics/details
objStats = toString
(xB,yB,xE,yE,xCorn,yCorn,stroke,vis,shape,brush); // create 1
string out of all stats
// if objStats string length is too big for LB HSCROLL
then increase it
if ((objStats.length()*7) > listboxLen-1) {//3.15
listboxLen = int(objStats.length()*7); //5.75
// Set LB horizontal width to listboxLen
SendDlgItemMessage
(hwnd,listboxMsg,LB_SETHORIZONTALEXTENT,listboxLen,0);
}
// - push string onto LB
SendDlgItemMessage(hwnd,listboxMsg,LB_DELETESTRING,i,0);
int index = SendDlgItemMessage
(hwnd,listboxMsg,LB_INSERTSTRING,i,(WPARAM)objStats.c_str());
// Associate this objects unique ID with Listbox's Cell
at index
SendDlgItemMessage(hwnd,listboxMsg,LB_SETITEMDATA,(WPARAM)
index,(LPARAM)objectFocus->ID);
//objectFocus->ID = index; // make objects ID the LB cell
index/num
return true; // exit function
}
}
*/
}

bool drawClass::changeShape(HWND hwnd,UINT comboboxMsg)
{
// Post: sets objectFocus's shape to the new shape that is
selected in Combobox

// get cell num of selected cell
int cell = SendDlgItemMessage(hwnd,comboboxMsg,CB_GETCURSEL,0,0);
int selection = SendDlgItemMessage
(hwnd,comboboxMsg,CB_GETITEMDATA,(WPARAM)cell,0); // get selected cell
data

// set object style to selection
if (objectFocus->setShape(selection) == false) {
MessageBox(hwnd,"Failed to set Object Style: Check setObject
(sType)",
"Error",MB_OK | MB_ICONERROR);
}

// if we are selecting RoundRect
if (selection==3) {
// need to remind user to declare xCorn,yCorn values
// set RoundRect xCornerEllipse,yCornerEllipse
objectFocus->setCornerDim(20,20);
}
}

bool drawClass::changeStroke(HWND hwnd,UINT editboxMsg,UINT
comboboxMsg)
{
// Post: sets objectFocus's stroke to number in editbox & stroke
brush to
// combobox selection.
// if stroke width EB has been updated/altered
if (strokeWidthChg) {
int strokeW = GetDlgItemInt
(hwnd,editboxMsg,NULL,false);
// set stroke width & check if fails
if (objectFocus->setStroke(strokeW)==false) {
MessageBox(hwnd,"Stroke number is too large for
object.","Error",MB_OK|MB_ICONERROR);
return false;
}
strokeWidthChg = false;
}
// if stroke brush CB has been updated/altered
if (strokeBrushChg) {
int cell = SendDlgItemMessage(hwnd,comboboxMsg,CB_GETCURSEL,
0,0); // get CB selected cell
int strokeB = SendDlgItemMessage
(hwnd,comboboxMsg,CB_GETITEMDATA,(WPARAM)cell,0);
// set stroke brush & check if fails
if (objectFocus->setStrokeBrush(strokeB)==false) {
MessageBox(hwnd,"Invalid 'Stroke Brush'
type.","Error",MB_OK|MB_ICONERROR);
return false;
}
// if PS_SOLID brush has been selected
if (strokeB!=1) { // set editbox to 1
SetDlgItemInt(hwnd,editboxMsg,1,false);
}
strokeBrushChg = false;
}
return true; // function succeeds
}

bool drawClass::changeStrokeColour(HWND hwnd)
{
// Post: Open colour dialog to allow user to set objects stroke
colour

COLORREF colour = RGB(0,0,0);
CHOOSECOLOR cc = {sizeof(CHOOSECOLOR)};

cc.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ANYCOLOR;
cc.hwndOwner = hwnd;
cc.rgbResult = colour;
cc.lpCustColors = customColours;

// if colour is selected
if(ChooseColor(&cc))
{
colour = cc.rgbResult;
objectFocus->setStrokeColour(colour); // set objects stroke
colour to colour var..
}
return true;
}

bool drawClass::changeFillColour(HWND hwnd)
{
// Post: Open colour dialog to allow user to set objects fill
(interior) colour

COLORREF colour = RGB(0,0,0);
CHOOSECOLOR cc = {sizeof(CHOOSECOLOR)};

cc.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ANYCOLOR;
cc.hwndOwner = hwnd;
cc.rgbResult = colour;
cc.lpCustColors = customColours;

// if colour is selected
if(ChooseColor(&cc))
{
colour = cc.rgbResult;
objectFocus->setFillColour(colour); // set objects fill colour to
colour var..
}
return true;
}

[/source]

object implementation file:
[source]
// Part of Dev C++ Proj - Paint Clone

#include <windows.h>
#include <string>
#include <cstdlib>

#include "object.h"

using namespace std;

object::object()
{
// Constructor:

visible = true;
shape = "Rectangle";
brush = "Solid";
stroke = 2; // 2 = default
stroke
xStart = -1; yStart = -1; xEnd = -1; yEnd = -1; // set coords
to default values
xCornerEllipse = 10; yCornerEllipse = 10; // used where
shape="RoundRect"
strokeCol = RGB(0,0,0); // set stroke
colour to black
strokeBrush = PS_SOLID;
hPen = CreatePen(strokeBrush,stroke,BLACK_PEN); // set stroke
to default black line
objectRgn = CreateRectRgn(xStart,yStart,xEnd,yEnd);
fillColour = CreateSolidBrush(RGB(255,255,255)); // set object
fill colour to default(white)
}

bool object::isNew()
{
// Post: makes sure we dont create a new object until
// this object has some x,y values
// Meaning: If an object has no x,y values its unused.
// So dont make a new object till this is used.

// SHOULD ALSO CHECK IF STROKE HAS BEEN CHANGED OR VISIBILITY OR
BRUSH!!

// if object has not been given x,y values/still has default
values its unused
if (xStart==-1 && yStart==-1 && xEnd==-1 && yEnd==-1 &&
shape == "Rectangle" && brush == "Solid" && stroke == 2) {
return true;
}
else return false; // object has x,y coords/is used
}

bool object::isVisible()
{
// Post: returns true if object is visible else false
return(visible==true);
}

bool object::setVisibility(bool vis)
{
/// Post: if bool vis is true; object is made visible;
if (vis==true)
visible = true;
else visible = false;
}

bool object::setShape(int sType)
{
// Pre: sType is a number from 1-4(1=Line...)
// Post: Sets shape to bType if valid else returns false

switch(sType) {

case 1: shape = "Line"; return true;
break;
case 2: shape = "Rectangle"; return true;
break;
case 3: shape = "RoundRect"; return true;
break;
case 4: shape = "Ellipse"; return true;
break;
default: return false; // sType is invalid so function failed
break;
}
}


string object::getShape()
{
return shape;
}

bool object::setStroke(int n)
{
// Post: set stroke width
if (n < (abs(yEnd-yStart)-5) && n < (abs(xEnd-xStart)-5)) {
stroke = n;
hPen = CreatePen(strokeBrush,stroke,strokeCol); // set
stroke
return true;
}
else return false;
}

bool object::setStrokeBrush(int type)
{
// Post: Set stroke brush type
// NOTE: ALL Pen Styles(other than Solid) require stroke==1 to be
used

switch (type) {
case 1:
brush = "Solid";
strokeBrush = PS_SOLID;
break;
case 2:
brush = "Dash";
stroke = 1;
strokeBrush = PS_DASH;
break;
case 3:
brush = "Dot";
stroke = 1;
strokeBrush = PS_DOT;
break;
case 4:
brush = "Dash Dot";
stroke = 1;
strokeBrush = PS_DASHDOT;
break;
case 5:
brush = "Dash DotDot";
stroke = 1;
strokeBrush = PS_DASHDOTDOT;
break;
default:
return false; // function failed
break;
}
hPen = CreatePen(strokeBrush,stroke,strokeCol);
return true; // function succeeded
}

void object::setStrokeColour(COLORREF colour)
{
// Post: Set objects stroke colour to colour variable
strokeCol = colour;
hPen = CreatePen(strokeBrush,stroke,strokeCol);
}

void object::setFillColour(COLORREF colour)
{
// Post: Set objects fill(interior) colour to colour variable
fillColour = CreateSolidBrush(colour);
}

bool object::drawSelf(HDC hdc)
{
// Post: Draw object

SelectObject(hdc,hPen);

if (shape == "Line") {
MoveToEx(hdc,xStart,yStart,NULL);
LineTo(hdc,xEnd,yEnd);
DeleteObject(hPen);
}
else if (shape == "Rectangle") {
Rectangle(hdc,xStart,yStart,xEnd,yEnd);
objectRgn = CreateRectRgn(xStart,yStart,xEnd,yEnd); //
define object region
FillRgn(hdc,objectRgn,fillColour); //
colour object region
DeleteObject(hPen);
}
else if (shape == "Ellipse") {
Ellipse(hdc,xStart,yStart,xEnd,yEnd);
objectRgn = CreateEllipticRgn(xStart,yStart,xEnd,yEnd);
FillRgn(hdc,objectRgn,fillColour);
DeleteObject(hPen);
}
else if (shape == "RoundRect") {
RoundRect
(hdc,xStart,yStart,xEnd,yEnd,xCornerEllipse,yCornerEllipse);
objectRgn = CreateRoundRectRgn
(xStart,yStart,xEnd,yEnd,xCornerEllipse,yCornerEllipse);
FillRgn(hdc,objectRgn,fillColour);
DeleteObject(hPen);
}
else return false; // function failed because shape variable is
not valid
}

void object::setStartCoord(int x, int y)
{
// Pre: x = mouse x coord, y = mouse y coords
// Post: sets the left corner of shape coords (beginning)
xStart = x;
yStart = y;
}

void object::setEndCoord(int x, int y)
{
// Pre: x = mouse x coord, y = mouse y coords
// Post: sets the right corner of shape coords (end)
xEnd = x;
yEnd = y;
}

bool object::setCornerDim(int x, int y)
{
// Post: sets the RoundRect xCornerEllipse & yCornerEllipse
xCornerEllipse = x;
yCornerEllipse = y;
}

void object::getStats(int &xBegin, int &yBegin, int &xFin, int &yFin,
int &xCorn,
int &yCorn, int &strk, bool &vis, string &shp, string
&brsh)
{
// Post: Set parameters to this objects' variable statistics/
details

xBegin = xStart;
yBegin = yStart;
xFin = xEnd;
yFin = yEnd;
xCorn = xCornerEllipse;
yCorn = yCornerEllipse;
strk = stroke;
vis = visible;
shp = shape;
brsh = brush;
}

[/source]
From: MrBcx on
On Jan 30, 8:13 pm, Jimbo <nill...(a)yahoo.com> wrote:
>
> Hi
>
> I have made my first complex application using Win32 C++ & I am
> looking for advice & criticism on my application logic & architecture.

Wow! ... Which version of Paint is that a clone of?

Let us start with a definition: Dictionary.com

Clone - a person or thing that duplicates, imitates, or closely
resembles another in appearance, function, performance, or style

Using that definition, characterizing your "complex application" as a
"Paint Clone" is like calling a pound of ground beef a cow clone.

From: B.S on
i run your .exe you have done very good. there are some things that
you mast corect at firs you need double buffering. without that it is
fliking. i will see more in your code and will fownd what you need
corect


sory for my english
From: Jimbo on
On Feb 1, 6:39 am, "B.S" <giobs...(a)gmail.com> wrote:
> i run your .exe you have done very good. there are some things that
> you mast corect at firs you need double buffering. without that it is
> fliking. i will see more in your code and will fownd what you need
> corect
>
> sory for my english

Thanks, please give me any advice or criticisms :)