From: Jimbo on 30 Jan 2010 22:13 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 31 Jan 2010 10:17 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 31 Jan 2010 14:39 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 2 Feb 2010 00:39 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 :)
|
Pages: 1 Prev: How to use GLib in Windows? Next: Code and Creation 89475 |