-2

I have a problem with create drawing rectangles functions in C++. I have prepared c++ file, when I must finished drawing functions.

I need to add functionality where on mouse click it will creating rectangle, and when I finished moving mouse and released it should be rectangle. next it should be created again and changes position.

I have completely no idea how to do it. Any ideas ? Code looks like this:

MsgHandlers.cpp (here all things happen):

#include "MsgHandlers.h"
#include "Utils.h"
#include <time.h>
#include <math.h>



int ClientWidth, ClientHeight;
BOOL EraseBkgnd = TRUE;
bool IgnoreTimer = false;
RECT Rect;



void OnCreate(HWND hwnd)
{
    SetTimer(hwnd, 1, 25, NULL);
}



void OnSize(HWND hwnd, int width, int height, WPARAM wParam)
{
    ClientWidth = width;
    ClientHeight = height;

Rect.left = width/4;
Rect.right=Rect.left + width/2;
Rect.top = height/4;
Rect.bottom= Rect.top + height/2;
}





void OnTimer(HWND hwnd, WPARAM timerID)
{


if(IgnoreTimer) return;
Rect.left+=RandRange(-10, 10);
Rect.right +=RandRange(-10,10);
Rect.top +=RandRange(-10,10);
Rect.bottom +=RandRange(-10,10);

InvalidateRect(hwnd, NULL, EraseBkgnd);


}



void OnPaint(HWND hwnd, HDC hdc)
{


  Rectangle(hdc,Rect.left,Rect.top,Rect.right,Rect.bottom);


}



void OnKeyDown(HWND hwnd, WPARAM keyCode)
{
    switch (keyCode)
    {
    case VK_LEFT:
        break;
    case VK_UP:
        break;
    case VK_RIGHT:
        break;
    case VK_DOWN:
        break;
    case 0x43: // C
        break;
    case 0x45: // E
        EraseBkgnd ^= 0x00000001;
        break;
    case 0x52: // R
        break;
    case 0x49: //  I
    IgnoreTimer = !IgnoreTimer;
    case 0x53: // S
        break;
    }
    //InvalidateRect(hwnd, NULL, EraseBkgnd);
}



void OnMouseMove(HWND hwnd, int x, int y, WPARAM wParam)
{

}



void OnLButtonDown(HWND hwnd, int x, int y, WPARAM wParam)
{

}



void OnLButtonUp(HWND hwnd, int x, int y, WPARAM wParam)
{

}



void OnDestroy(HWND hwnd)
{
    KillTimer(hwnd, 1);
}

I put also others files from this C++ project:

Main:

#include <windows.h>
#include "MsgHandlers.h"


/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
char szClassName[ ] = "CodeBlocksWindowsApp";

int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG message;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
               0,                   /* Extended possibilites for variation */
               szClassName,         /* Classname */
               "Progr.",       /* Title Text */
               WS_OVERLAPPEDWINDOW, /* default window */
               CW_USEDEFAULT,       /* Windows decides the position */
               CW_USEDEFAULT,       /* where the window ends up on the screen */
               1024,                 /* The programs width */
               768,                 /* and height in pixels */
               HWND_DESKTOP,        /* The window is a child-window to desktop */
               NULL,                /* No menu */
               hThisInstance,       /* Program Instance handler */
               NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage(&message, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&message);
        /* Send message to WindowProcedure */
        DispatchMessage(&message);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return message.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;


    switch (message)                  /* handle the messages */
    {
    case WM_CREATE:
        OnCreate(hwnd);
        break;
    case WM_SIZE:
        OnSize(hwnd, short(lParam & 0x0000FFFF), short(lParam >> 16), wParam);
        break;
    case WM_TIMER:
        OnTimer(hwnd, wParam);
        break;
    case WM_KEYDOWN:
        OnKeyDown(hwnd, wParam);
        break;
    case WM_LBUTTONDOWN:
        OnLButtonDown(hwnd, short(lParam & 0x0000FFFF), short(lParam >> 16), wParam);
        break;
    case WM_MOUSEMOVE:
        OnMouseMove(hwnd, short(lParam & 0x0000FFFF), short(lParam >> 16), wParam);
        break;
    case WM_LBUTTONUP:
        OnLButtonUp(hwnd, short(lParam & 0x0000FFFF), short(lParam >> 16), wParam);
        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        OnPaint(hwnd, hdc);
        EndPaint(hwnd, &ps);
        break;
    case WM_DESTROY:
        OnDestroy(hwnd);
        PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
        break;
    default:                      /* for messages that we don't deal with */
        return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

Utils.cpp:

#include "Utils.h"
#include <math.h>

//random <min, max>
int RandRange(int min, int max)
{
    return min + rand() % (1 + max - min);
}

//Return -1 for negative argument; 
//return +1 for positive argument or 0

int Sign(int arg)
{
    return arg < 0 ? -1 : 1;
}

double Deg2Rad(double aDegrees)
{
    return aDegrees * PI / 180.0;
}

double Rad2Deg(double aRadians)
{
    return aRadians * 180 / PI;
}

float DirectionFromSpeedXY(int aSpeedX, int aSpeedY)
{
    if(aSpeedX==0 && aSpeedY==0)
        return 0;
    else
        return atan2(aSpeedX, -aSpeedY);
}

float SpeedFromSpeedXY(int aSpeedX, int aSpeedY)
{
    return sqrt(aSpeedX*aSpeedX + aSpeedY*aSpeedY);// * Sign(aSpeedX*aSpeedY);
}

void SpeedXYFromSpeedDirection(float aSpeed, float aDirection, int *aSpeedX, int *aSpeedY)
{
    aSpeed = abs(aSpeed);
    *aSpeedX = aSpeed * sin(aDirection);
    *aSpeedY = -aSpeed * cos(aDirection);
}

void CorrectRect(RECT *aRect)
{
    int temp;

    if(aRect->right < aRect->left)
    {
        temp = aRect->right;
        aRect->right = aRect->left;
        aRect->left = temp;
    }

    if(aRect->bottom < aRect->top)
    {
        temp = aRect->bottom;
        aRect->bottom = aRect->top;
        aRect->top = temp;
    }
}
Pigius
  • 31
  • 1
  • 7

1 Answers1

1

As mentioned, the code you provided is not standard. To accomplish the task you described there are some steps that must be handled.

How to draw a (simple) rectangle:

// x and y are the x- and y-locations of the mouse cursor upon release
void drawRectangle(HWND hwnd, const int x, const int y)
{
    // obtain a handle to the device context
    HDC hdc = GetDC(hwnd);

    // RECT_WIDTH and RECT_HEIGHT are elsewhere defined
    // draw rectangle
    Rectangle(hdc, x - RECT_WIDTH / 2, y - RECT_HEIGHT / 2, x + RECT_WIDTH / 2, y + RECT_HEIGHT / 2);

    // release the DC
    ReleaseDC(hwnd, hdc);
}

To capture the mouse button release you process the WM_LBUTTONUP message. In the window's window procedure:

LRESULT __stdcall wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static int x;
    static int y;

    HDC hdc;
    PAINTSTRUCT ps;

    case WM_LBUTTONUP:
    {
        x = LOWORD(lParam); // grab the x-position
        y = HIWORD(lParam); // grab the y-position

        // invalidate the entire client area
        // this will cause the window's client area to be "cleared"
        // using the window class background brush upon BeginPaint call
        // in WM_PAINT
        InvalidateRect(hwnd, NULL, true);
        return 0;
    }

    case WM_PAINT:
    {
        hdc = BeginPaint(hwnd, &ps);

        // draw the rectangle
        drawRectangle(hwnd, x, y);

        EndPaint(hwnd, &ps);
        return 0;
    }

    // other case handlers like WM_DESTROY
}

Obviously, there are other things to take into consideration, but this should get you started.

jensa
  • 2,792
  • 2
  • 21
  • 36
  • The first parameter to `drawRectangle` should ideally be a device context. This allows to reuse the rending code for printing, or if you wish to render into an enhanced meta file (see [CreateEnhMetaFile](https://msdn.microsoft.com/en-us/library/dd183498.aspx)). – IInspectable Mar 08 '15 at 11:55
  • Yeah, that would work better in this case. However, I just wanted to show a "complete" drawing function. – jensa Mar 08 '15 at 12:23
  • Passing a device context would work better in **every** case. I don't understand what's less *complete* about doing it right. You already have a device context - with appropriate clipping in place - from your call to `BeginPaint`. – IInspectable Mar 08 '15 at 13:09
  • You don't have to draw from WM_PAINT, I just did it in this example. – jensa Mar 08 '15 at 13:39
  • If you decide to side-step the system, and perform your rendering outside of `WM_PAINT`, you will have to set up a clipping region. This is missing from your code. And even if you do find valid reasons to render from a random point in your application, what's keeping you from retrieving a device context there, and pass that into `drawRectangle`? And how are you going to deal with a future requirement to add a `drawWombat` method? – IInspectable Mar 08 '15 at 13:53
  • I see your point, and I agree. My answer was focused most on some simple basic required. At least, my code shows two different ways of obtaining a handle to a device context (BeginPaint and GetDC). – jensa Mar 08 '15 at 14:15
  • I added more files from projects. For now when you run a program (with my files) it shows rectangle which is refreshing and shows in random position. I need to create this rectangle on mouse click (declaration of functions are in code) – Pigius Mar 08 '15 at 16:48
  • Why do you pass both a HWND and HDC to your OnPaint function? You only need a HDC (or a HWND to obtain a HDC from). What exactly are you trying to accomplish? Do you want a rectangle to be drawn in the place where you click with your mouse button? If so, you need to handle the WM_LBUTTONDOWN message. – jensa Mar 08 '15 at 18:16
  • I want a functionality,when I click mouse button it will start to create rectangle, and when I release mouse button, the rectangle will be created, and make a "things" which does a default rectangle which was created in my code before (I mean changing position). – Pigius Mar 08 '15 at 20:47
  • @Pigius that's your problem. You asked a question. We are here to answer them. Please don't add loads of extra requirements in comments. – David Heffernan Mar 09 '15 at 08:23
  • I wrote the same in main problem. And in comments I repeated it. – Pigius Mar 09 '15 at 10:50