• Không có kết quả nào được tìm thấy

chế độ vẽ, ta chỉ quan tâm đến hai chế độ vẽ là R2_XORPEN và R2_NOTXORPEN

Trong tài liệu LẬP TRÌNH WINDOWS (Trang 107-119)

GetCapture

Có 16 chế độ vẽ, ta chỉ quan tâm đến hai chế độ vẽ là R2_XORPEN và R2_NOTXORPEN

Cả hai chế độ vẽ này đều có chung đặc điểm là nếu một hình được vẽ hai lần, nó sẽ khôi phục lại nền về trạng thái trước khi vẽ. Với một trong hai chế độ vẽ kể trên, khi một đối tượng tạm được kéo lê. Ta vẽ lại nó một lần nữa ở hình dáng và vị trí cũ, thao tác này sẽ khôi phục lại phần nền như khi chưa vẽ, sau đó ta vẽ lại đối tượng ở hình dáng, vị trí mới. Chế độ R2_XORPEN phù hợp với cửa sổ có nền đen và R2_NOTXORPEN phù hợp với cửa sổ có nền trắng.

Ví dụ ứng dụng – Chương trình ObjectDrag V2

Chương trình ObjectDrag V2 sau minh hoạ sử dụng các hàm SetCapture, GetCapture, ReleaseCapture kết hợp với chế độ vẽ R2_NOTXORPEN kể trên để thao tác trên đối tượng đồ hoạ. Chương trình ban đầu vẽ một hình ellipse, người sử dụng sau đó có thể thay đổi vị trí của hình ellipse này bằng cách đưa con trỏ chuột vào bên trong hình, bấm mắt trái và kéo lê.

Khi người sử dụng bấm mắt trái chuột bên trong hình ellipse, cursor đổi sang hình dạng khác cho biết người sử dụng đang trong quá trình kéo lê. Đồng thời một hình ellipse tạm với nét vẽ PS_DOT, chổi vẽ rỗng được vẽ chồng lên hình ellipse đang có trong chế độ vẽ R2_NOTXORPEN. Khi con trỏ chuột được kéo lê đến đâu, hình ellipse tạm này di chuyển theo đến đó, điều này được thực hiện nhờ xử lý thông điệp WM_MOUSEMOVE, hàm GetCapture được gọi khi xử lý thông điệp này để xác định con trỏ chuột có đang bị bắt giữ không, nói cách khác, chuột đang được kéo lên không. Ta di chuyển hình elippse tạm bằng cách vẽ lại để khôi phục phần nền bị hình này vẽ lên, cập nhật vị trí, sau đó vẽ lại hình ellipse tạm này, cũng với chế độ vẽ R2_NOTXORPEN để chuẩn bị cho lần xử lý sau. Khi người sử dụng buông mắt chuột để kết thúc quá trình kéo lê, mới vẽ lại đối tượng thực ở vị trí mới.

Chương trình gồm một số tập tin tài nguyên

small.ico ObjectDrag.ico

resource.h

Và một số tập tin chương trình nguồn

ObjectDrag.rc StdAfx.h StdAfx.cpp

ObjectDrag.h ObjectDrag.cpp

Hình 7-3 Hình ellipse ban đầu

Hình 7-5 Người sử dụng kéo lê, chỉ có đối tượng tạm di chuyển theo

//////////////////////////////////////////////////////////////////

bool IsInsideEllipse(int x1, int y1, int x2, int y2, int x, int y) {

double a = (double(x2)-x1)/2, b = (double(y2)-y1)/2;

if (!a || !b) return false;

double xx = x - (double(x1)+x2)/2, yy = y - (double(y1)+y2)/2;

double xr = xx/a, yr = yy/b;

return xr*xr+yr*yr <= 1;

}

//////////////////////////////////////////////////////////////////////////

// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

int wmId, wmEvent;

PAINTSTRUCT ps;

HDC hdc;

TCHAR szHello[MAX_LOADSTRING];

static int x1,y1,x2,y2,xc,yc,px,py;

static HBRUSH hbr;

static HCURSOR hCursorMove;

static HPEN hPenDot;

int dx, dy;

RECT rt;

LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

switch (message) {

case WM_CREATE:

GetClientRect(hWnd, &rt);

x1 = rt.right/4; x2 = 3*rt.right/4;

y1 = rt.bottom/4; y2 = 3*rt.bottom/4;

hbr = CreateHatchBrush(HS_DIAGCROSS, RGB(255,0,0));

hCursorMove = LoadCursor(NULL, IDC_SIZEALL);

hPenDot = CreatePen(PS_DOT, 1, RGB(0,0,255));

break;

case WM_COMMAND:

wmId = LOWORD(wParam);

case WM_LBUTTONDOWN:

xc = LOWORD(lParam); yc = HIWORD(lParam);

if (IsInsideEllipse(x1,y1,x2,y2,xc,yc)) {

hdc = GetDC(hWnd);

SelectObject(hdc,hPenDot);

SetROP2(hdc, R2_NOTXORPEN);

Ellipse(hdc,x1,y1,x2,y2);

SetCursor(hCursorMove);

SetCapture(hWnd);

} break;

case WM_MOUSEMOVE:

if (GetCapture() == hWnd) {

hdc = GetDC(hWnd);

SelectObject(hdc,hPenDot);

SetROP2(hdc, R2_NOTXORPEN);

Ellipse(hdc,x1,y1,x2,y2);

px = xc; py = yc;

xc = LOWORD(lParam); yc = HIWORD(lParam);

dx = xc-px, dy = yc - py;

x1 += dx, x2 += dx, y1 += dy, y2 += dy;

Ellipse(hdc,x1,y1,x2,y2);

} break;

case WM_LBUTTONUP:

if (GetCapture() == hWnd) {

ReleaseCapture();

InvalidateRect(hWnd, NULL, TRUE);

} break;

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

GetClientRect(hWnd, &rt);

DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);

SelectObject(hdc,hbr);

Ví dụ ứng dụng – Chương trình ObjectDrag V6

Chương trình ObjectDrag V6 cho phép vẽ và di chuyển hình ellipse. Người sử dụng bấm chuột và kéo lê. Nếu điểm bấm không thuộc một hình ellipse nào đã vẽ trước đó thì vẽ hình ellipse mới. Nếu vị trí chuột được bấm ở trong một trong các hình ellipse đã vẽ thì di chuyển hình ellipse đó theo chiều di chuyển của chuột. Người sử dụng có thể thay đổi thuộc tính tô và màu tô của hình ellipse sẽ vẽ tiếp theo.

Hình 7-7 Cửa số chương trình sau khi vẽ 3 hình ellipse. Hình thứ ba có chổi vẽ khác.

Hình 7-8 Cửa sổ chương trình sau khi di chuyển ba hình đầu và vẽ thêm 4 hình khác.

// ObjectDrag.cpp : Defines the entry point for the application.

//...

class CEllipse {

int x1,y1,x2,y2;

COLORREF crColor;

int hatch;

public:

CEllipse(){Set(0,0,0,0); SetColor(RGB(0,0,255)); SetHatch(HS_DIAGCROSS);}

CEllipse(int _x1, int _y1, int _x2, int _y2) { Set(_x1,_y1,_x2,_y2);

SetColor(RGB(0,0,255));

SetHatch(HS_DIAGCROSS);

}

};

void CEllipse::Draw(HDC hdc) {

HBRUSH hbr;

hbr = CreateHatchBrush(hatch, crColor);

SelectObject(hdc, hbr);

Ellipse(hdc,x1,y1,x2,y2);

DeleteObject(hbr);

}

bool CEllipse::IsInside(int x, int y) {

double a = (double(x2)-x1)/2, b = (double(y2)-y1)/2;

if (!a || !b) return false;

double xx = x - (double(x1)+x2)/2, yy = y - (double(y1)+y2)/2;

double xr = xx/a, yr = yy/b;

return xr*xr+yr*yr <= 1;

}

void CEllipse::Translate(int dx, int dy) {

x1 += dx; y1 += dy;

x2 += dx; y2 += dy;

}

int FindEllipse(CEllipse ae[], int n, int x, int y) {

for (int i = n-1; i >= 0; i--) if (ae[i].IsInside(x,y))

return i;

return -1;

}

//////////////////////////////////////////////////////////

// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //

// PURPOSE: Processes messages for the main window.

//

enum {DRAW, MOVE};

static COLORREF aCrTab[] = {

RGB(0,255,0), RGB(0, 255,255),RGB(0,0,0), RGB(255,0,0), RGB(0,0,255), RGB(255,255,0),

};

static int aHatch[] = {

HS_HORIZONTAL, HS_VERTICAL, HS_FDIAGONAL, HS_BDIAGONAL, HS_CROSS, HS_DIAGCROSS };

int dx, dy, i;

static int curColor, curHatch = 5;

static INT flag = DRAW, nEllipse,nCur = -1;

RECT rt;

LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

switch (message) {

case WM_CREATE:

GetClientRect(hWnd, &rt);

hCursorMove = LoadCursor(NULL, IDC_SIZEALL);

hCursorCross = LoadCursor(NULL, IDC_CROSS);

hPenDot = CreatePen(PS_DOT, 1, RGB(0,0,255));

break;

case WM_COMMAND:

wmId = LOWORD(wParam);

wmEvent = HIWORD(wParam);

switch (wmId) {

case IDM_ABOUT:

DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);

break;

case IDM_EXIT:

DestroyWindow(hWnd);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

} break;

break;

case WM_LBUTTONDOWN:

xc = LOWORD(lParam); yc = HIWORD(lParam);

if ((nCur = FindEllipse(ae, nEllipse, xc, yc)) >= 0) {

flag = MOVE;

hdc = GetDC(hWnd);

SelectObject(hdc,hPenDot);

SetROP2(hdc, R2_NOTXORPEN);

ae[nCur].Draw(hdc);

SetCursor(hCursorMove);

SetCapture(hWnd);

}

else if (nEllipse < MAX_ELLIPSE)// DRAW {

flag = DRAW;

ae[nEllipse].Set(xc,yc,xc,yc,aCrTab[curColor],aHatch[curHatch]);

hdc = GetDC(hWnd);

SetROP2(hdc, R2_NOTXORPEN);

ae[nEllipse].Draw(hdc);

SetCursor(hCursorCross);

SetCapture(hWnd);

nEllipse++;

} else

flag = -1;

break;

case WM_MOUSEMOVE:

if (GetCapture() == hWnd) {

if (flag == MOVE) {

hdc = GetDC(hWnd);

SelectObject(hdc,hPenDot);

SetROP2(hdc, R2_NOTXORPEN);

ae[nCur].Draw(hdc);

px = xc; py = yc;

xc = LOWORD(lParam); yc = HIWORD(lParam);

dx = xc-px, dy = yc - py;

ae[nCur].Translate(dx,dy);

ae[nCur].Draw(hdc);

{

ReleaseCapture();

InvalidateRect(hWnd, NULL, TRUE);

} break;

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

GetClientRect(hWnd, &rt);

for (i = 0; i < nEllipse; i++) ae[i].Draw(hdc);

EndPaint(hWnd, &ps);

char buf[128];

wsprintf(buf, "Number of Ellipses: %d", nEllipse);

DrawText(hdc, buf, lstrlen(buf), &rt, DT_CENTER);

break;

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

8. Sử dụng tài nguyên

Window cho phép người sử dụng đưa vào chương trình các tài nguyên thuộc các loại biểu tượng (icons), con trỏ (cursors), bitmap, bảng các chuỗi hằng (string tables), bảng phím tắt (Accelerator), hộp hội thoại (Dialog), thực đơn (menu), thanh công cụ (toolbar) và một siêu văn bản (văn bản loại HTML). Một số tài nguyên thuộc các loại này có thể được Windows cung cấp sẵn gọi là tài nguyên chuẩn. Người sử dụng có thể tự tạo các tài nguyên khác thông qua một tập tin tài nguyên có phần đuôi .RC (Resource Script).

Tập tin tài nguyên là một văn bản viết theo cú pháp của một ngôn ngữ lập trình để mô tả các tài nguyên sẽ cấp cho chương trình.

Hình 8-1 Tạo tài nguyên trong VC++ 6.0

• Biểu tượng chuẩn (standard icons) là những biểu tượng do Windows cung cấp sẵn,

những biểu tượng này được xác định bằng các hằng có phần tiếp đầu ngữ IDI_.

Trong tài liệu LẬP TRÌNH WINDOWS (Trang 107-119)