Implementacja zaznaczania dynamicznie dodawanych obiektów

Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 2167 razy

Wstęp

Pierwsze co należy zrobić zanim przystąpi się do obsługi zadania związanego z zaznaczaniem naszych obiektów w programie Rysowanie, konieczne jest zastanowienie się, jak powinno wyglądać takie zaznaczanie obiektów? Oto kilka zasad, jakie ja postawiłem sobie aby mój program spełniał:

Do realizacji tego zadania zostanie utworzona specjalna klasa o nazwie select_obj, która będzie odpowiedzialna za realizację zadań związanych z zaznaczaniem i edycją obiektów. Poniżej zamieszczam mały diagram UML zależności klas w programie.

Schemat UML
Rys. 1
Diagram UML relacji klas w programie Rysowanie

Klasa select_obj jest powiązana z klasą i_dr_obj za pośrednictwem pola tSelObj oraz konstruktora i metod związanych z obsługą zaznaczania obiektów.

Mechanizm wyrywania obiektu pod kursorem myszki

Podstawowe pytanie, jakie należy sobie zadać, to: jak wykrywać obiekt znajdujący się pod kursorem myszki? Odpowiedź na to pytanie nie jest prosta, ponieważ ten mechanizm jest zależy od rodzaju obiektu. Dla linii trzeba wykryć, czy obiekt znajduje się nad tą linią z pewną dopuszczalną tolerancją błędu, dla prostokąta czy punkt kursora znajduje się w jego wnętrzu, podobnie rzecz się ma z okręgiem, ale oba te obiekty muszą realizować swoje zadania w nieco inny sposób.

Wykrywanie kursora nad linią

Jak wykryć kursor myszy nad linią a ściślej rzecz ujmując nad odcinkiem? Pierwszym etapem jest przeprowadzenie pewnych obliczeń wstępnych, polegających na rzutowaniu punktu kursora myszki mousepos na linię przechodzącą przez punkty begin i end. I tu pojawia się pierwszy wyjątek. Wyjątek ten polega na tym, że punkty begin oraz end nie mogą się pokrywać, ponieważ gdy się pokrywają nie da się rzutować punktu. Z tego względu trzeba najpierw sprawdzić czy współrzędne punktu begin są równe współrzędnym punktu end i jeżeli tak, to należy sprawdzić czy punkt kursora myszy mousepos nie jest w pewnej odległości mniejszej od zadanej wartości. Do obliczeń należy wykorzystać stary dobry wzór Pitagorasa:

Dobra - ktoś powie, o co tutaj chodzi? Czyżby twierdzenie Pitagorasa uległo zmianie od czasów, gdy ukończyłem podstawówkę? Co to w ogóle za wzór jest?! Spokojnie to jest twierdzenie Pitagorasa zapisane w nieco innej bo wektorowej postaci. Trzeba mieć świadomość, że iloczyn skalarny dwóch wektorów jest równy sumie iloczynu poszczególnych składowych. A więc powyższy wzór można rozpisać do bardziej normalnej postaci:

Skoro obsłużony został ten paskudny wyjątek, teraz można zakasać rękawy i obliczyć prostopadły punktu kursora myszki mousepos na linię. Do tego celu należy wykorzystać wzory z strony Programowanie → Algorytmy obliczeniowe → Rzutowanie punktu na prostą. Wzorów tych nie będę tutaj przytaczał napiszę tylko, że po rzutowaniu należy obliczyć odległość tegoż punktu od kursora myszy i jeżeli jest on mniejszy od pewnej zadanej wartości to należy jeszcze sprawdzić czy punkt zrzutowany znajduje się pomiędzy końcami linii.

Poniżej zamieszczam fragment kodu klasy line, który dotyczy wykrywania obiektu tego typu znajdującego się pod kursorem myszy:

virtual bool CursorOnObject(POINT &ps) const { if(begin.x == end.x && begin.y == end.y){ // gdy punkty się pokrywają if((begin.x - ps.x) * (begin.x - ps.x) > ZN_SIZE * ZN_SIZE){ // oraz kwadrat odległości od kursora od punktu jest większa od zadanej tolerancji return false; // to zwracaj fałsz bo obiket jest poza zasięgiem zaznaczenia } return true; // w przeciwnym razie zwracaj prawdę } float u = float((ps.x - begin.x)*(end.x -begin.x)+(ps.y-begin.y)*(end.y-begin.y)) / (pow(begin.x - end.x, 2.f) + pow(begin.y - end.y, 2.f)); // współrzynnik umożliwiający znalezienie punktu zrzutowanego na linię POINT pt; // zmienna punktu zrzutowanego na linię pt.x = begin.x + (end.x - begin.x) * u; // współrzędna x zrzutowanego punktu pt.y = begin.y + (end.y - begin.y) * u; // współrzędna y zrzutowanego punktu if(pt.x >= min(begin.x, end.x) && pt.x <= max(begin.x, end.x) && pt.y >= min(begin.y, end.y) && pt.y <= max(begin.y, end.y)){ // jak punkt zawiera się w zadanym zakresie wartości to if((pt.x - ps.x) * (pt.x - ps.x) + (pt.y - ps.y) * (pt.y - ps.y) <= ZN_SIZE * ZN_SIZE){ // jak kwadrat odległości jest mniejszy lub równy kwadratowi maksymalnego odchylenia to return true; // zwracaj prawdę } } return false; // w przeciwnym przypadku zwracaj fałsz }

Wykrywanie kursora nad kołem

W przypadku koła, czyli klasy circle znów trzeba się posilić twierdzeniem Pitagorasa, jednakże do tego celu będzie potrzebny punkt centralny, od którego kwadrat odległości dzielącej go od punktu kursora myszki go dzieli. Kod realizujący tę część zadania będzie wyglądał następująco:

virtual bool CursorOnObject(POINT &ps) const { POINT cp = GetCenterPoint(); // metoda zwracająca współrzędne punktu centralnego UINT ray = abs(begin.x - end.x) * 0.5; // promień okręgu if((cp.x - ps.x) * (cp.x - ps.x) + (cp.y - ps.y) * (cp.y - ps.y) > ray * ray){ // jak kwadrat odległości kursora od punktu centralnego jest większy od kwadratu promienia okręgu to return false; // zwracaj fałsz } return true; // w przeciwnym przypadku zwracaj prawdę }

Wykrywanie kursora nad prostokątem

W przypadku prostokąta sprawa jest jeszcze prostsza, ponieważ istnieje pewna funkcja PtInRect, która sama odwali za nas brudną robotę. Kod realizujący to zadanie będzie miał następującą postać:

virtual bool CursorOnObject(POINT &ps) const { RECT r; // zmienna pomocnicza SetRect(&r, min(begin.x, end.x), min(begin.y, end.y), max(begin.x, end.x), max(begin.y, end.y)); // ustawienie prostokąta return PtInRect(&r, ps); // zwraca prawdę, gdy punkt jest w prostokącie a fałsz gdy nie jest }

Zmiany w kodzie programu

Zmiany w kodzie klasy i_dr_obj oraz klas dziedziczących po niej

Oto zmieniony nieco kod klasy i_dr_obj oraz klas po niej dziedziczących:

// ############### DAFINICJA INTERFEJSU KLAS DZIEDZICZĄCYCH ############### class i_dr_obj{ // i - interface; dr - draving; obj - object (interfejs rysowanego obiektu) protected: bool selected; int pen_index; // index pędzla rysowania int brush_index; // index wypełnienia rysowania public: i_dr_obj(std::vector<hpen> &tPen, std::vector<hbrush> &tBrush, pen &p, brush &b):selected(false), pen_index(-1), brush_index(-1){ for(int i = 0; i < tPen.size(); i++){ // dla każdego elementu kontenera tPen if((pen)tPen[i] == p){ // sprawdzam, czy nie ma już utworzonego pióra o takich samych parametrach pen_index = i; // jeżeli jest takie pióro to zapamiętuję jego index break; // i kończę zabawę z iterowaniem } } if(pen_index == -1){ // jak pen_index jest równe -1 to znaczy, że nie ma w tPen takiego pióra więc pen_index = tPen.size(); // przypisuję index ostatniego elementu hpen hp(p); // tworzę nową definicję pióra tPen.push_back(hp); // dodaję definicję pióra } for(int i = 0; i < tBrush.size(); i++){ // dla każdego elementu kontenera tBrush if((brush)tBrush[i] == b){ // sprawdzam, czy nie ma już utworzonego pędzla o takich samych parametrach brush_index = i; // jeżeli taki pędzel już istnieje to zapamiętuję jego index break; // i kończę zabawę z iterowaniem } } if(brush_index == -1){ // jak brush_index jest równy -1 to znaczy, że w tBrush nie ma takiego pędzla brush_index = tBrush.size(); // więc przypisuję index ostatniego elementu kontenera (który zaraz dodam) hbrush hbr(b); // tworzę ten element tBrush.push_back(hbr); // i dodaję go do kontenera } }; virtual std::wstring toString(std::vector<hpen> &tPen, std::vector<hbrush> &tBrush) const = 0; // każda definicja klasy musi obsługiwać tę metodę virtual void Draw(HDC &hdc, std::vector<hpen> &tPen, std::vector<hbrush> &tbrush) const = 0; // każda definicja klasy musi obsługiwać tę metodę virtual void DrawSelect(HDC &hdc) const = 0; // rysowanie zaznaczenia virtual void WmLButtonDown(POINT &mp) = 0; virtual void WmLButtonUp(POINT &mp) = 0; virtual RECT GetRect() const = 0; bool Selected() const {return selected;}; // określa, czy obiekt został zaznaczony void Unselect() { selected = false;}; virtual bool Select(POINT &ps) = 0; // każda definicja klasy musi obługiwać tę metodą virtual bool Select(RECT &r) = 0; virtual bool CursorOnObject(POINT &mp) const = 0; // sprawdza, czy kursor jest nad obiektem virtual ~i_dr_obj(){}; }; // ######################### DEFINICJA KLASY LINE (LINIA) ######################## class line : public i_dr_obj{ POINT begin; POINT end; public: line(std::vector<hpen> &tPen, std::vector<hbrush> &tBrush, pen &p, brush &b):i_dr_obj(tPen, tBrush, p, b){begin.x = begin.y = end.x = end.y = 0;}; virtual std::wstring toString(std::vector<hpen> &tPen, std::vector<hbrush> &tBrush) const { std::wstring str; wchar_t buffor[100]; wsprintf(buffor, L"line %i %i; %i %i", begin.x, begin.y, end.x, end.y); str = buffor; str += L" "; str += tPen[this->pen_index].toString(); str += L" "; str += tBrush[this->brush_index].toString(); return str; } virtual void Draw(HDC &hdc, std::vector<hpen> &tPen, std::vector<hbrush> &tBrush) const { tPen[pen_index].SelectPen(hdc); tBrush[brush_index].SelectBrush(hdc); MoveToEx(hdc, begin.x, begin.y, NULL); LineTo(hdc, end.x, end.y); } virtual void DrawSelect(HDC &hdc) const { Rectangle(hdc, begin.x - ZN_SIZE, begin.y - ZN_SIZE, begin.x + ZN_SIZE, begin.y + ZN_SIZE); Rectangle(hdc, end.x - ZN_SIZE, end.y - ZN_SIZE, end.x + ZN_SIZE, end.y + ZN_SIZE); } virtual void WmLButtonDown(POINT &mp){ begin = mp; end = mp; } virtual void WmLButtonUp(POINT &mp){ end = mp; } virtual bool Select(POINT &ps){ if(CursorOnObject(ps)){ selected = true; return true; } return false; } virtual bool Select(RECT &r){ if(PtInRect(&r, begin) && PtInRect(&r, end)){ selected = true; return true; } return false; } virtual RECT GetRect() const { RECT r; SetRect(&r, min(begin.x, end.x), min(begin.y, end.y), max(begin.x, end.x), max(begin.y, end.y)); return r; } virtual bool CursorOnObject(POINT &ps) const { if(begin.x == end.x && begin.y == end.y){ // gdy punkty się pokrywają if((begin.x - ps.x) * (begin.x - ps.x) > ZN_SIZE * ZN_SIZE){ // oraz kwadrat odległości od kursora od punktu jest większa od zadanej tolerancji return false; // to zwracaj fałsz bo obiket jest poza zasięgiem zaznaczenia } return true; // w przeciwnym razie zwracaj prawdę } float u = float((ps.x - begin.x)*(end.x -begin.x)+(ps.y-begin.y)*(end.y-begin.y)) / (pow(begin.x - end.x, 2.f) + pow(begin.y - end.y, 2.f)); // współrzynnik umożliwiający znalezienie punktu zrzutowanego na linię POINT pt; // zmienna punktu zrzutowanego na linię pt.x = begin.x + (end.x - begin.x) * u; // współrzędna x zrzutowanego punktu pt.y = begin.y + (end.y - begin.y) * u; // współrzędna y zrzutowanego punktu if(pt.x >= min(begin.x, end.x) && pt.x <= max(begin.x, end.x) && pt.y >= min(begin.y, end.y) && pt.y <= max(begin.y, end.y)){ // jak punkt zawiera się w zadanym zakresie wartości to if((pt.x - ps.x) * (pt.x - ps.x) + (pt.y - ps.y) * (pt.y - ps.y) <= ZN_SIZE * ZN_SIZE){ // jak kwadrat odległości jest mniejszy lub równy kwadratowi maksymalnego odchylenia to return true; // zwracaj prawdę } } return false; // w przeciwnym przypadku zwracaj fałsz } }; // ######################### DEFINICJA KLASY CIRCLE (OKRĄG) ######################## class circle : public i_dr_obj{ POINT begin; POINT end; public: circle(std::vector<hpen> &tPen, std::vector<hbrush> &tBrush, pen &p, brush &b):i_dr_obj(tPen, tBrush, p, b){begin.x = begin.y = end.x = end.y = 0;}; POINT GetCenterPoint() const { POINT pt; pt.x = (begin.x + end.x) * 0.5; pt.y = (begin.y + end.y) * 0.5; return pt; } virtual std::wstring toString(std::vector<hpen> &tPen, std::vector<hbrush> &tBrush) const { std::wstring str; wchar_t buffor[100]; wsprintf(buffor, L"circle %i %i; %i %i", begin.x, begin.y, end.x, end.y); str = buffor; str += L" "; str += tPen[this->pen_index].toString(); str += L" "; str += tBrush[this->brush_index].toString(); return str; } virtual void Draw(HDC &hdc, std::vector<hpen> &tPen, std::vector<hbrush> &tBrush) const { tPen[pen_index].SelectPen(hdc); tBrush[brush_index].SelectBrush(hdc); Ellipse(hdc, begin.x, begin.y, end.x, end.y); } virtual void DrawSelect(HDC &hdc) const { Rectangle(hdc, begin.x - ZN_SIZE, begin.y - ZN_SIZE, begin.x + ZN_SIZE, begin.y + ZN_SIZE); Rectangle(hdc, end.x - ZN_SIZE, end.y - ZN_SIZE, end.x + ZN_SIZE, end.y + ZN_SIZE); } virtual void WmLButtonDown(POINT &mp){ begin = mp; end = mp; } virtual void WmLButtonUp(POINT &mp){ end = mp; if(abs(begin.x - end.x) < abs(begin.y - end.y)){ end.y = begin.y + abs(begin.x - end.x) * ( begin.y < end.y ? 1 : -1); }else{ end.x = begin.x + abs(begin.y - end.y) * ( begin.x < end.x ? 1 : -1); } } virtual bool Select(POINT &ps){ if(CursorOnObject(ps)){ selected = true; return true; } return false; } virtual bool Select(RECT &r){ if(PtInRect(&r, begin) && PtInRect(&r, end)){ selected = true; return true; } return false; } virtual bool CursorOnObject(POINT &ps) const { POINT cp = GetCenterPoint(); // metoda zwracająca współrzędne punktu centralnego UINT ray = abs(begin.x - end.x) * 0.5; // promień okręgu if((cp.x - ps.x) * (cp.x - ps.x) + (cp.y - ps.y) * (cp.y - ps.y) > ray * ray){ // jak kwadrat odległości kursora od punktu centralnego jest większy od kwadratu promienia okręgu to return false; // zwracaj fałsz } return true; // w przeciwnym przypadku zwracaj prawdę } virtual RECT GetRect() const { RECT r; SetRect(&r, min(begin.x, end.x), min(begin.y, end.y), max(begin.x, end.x), max(begin.y, end.y)); return r; } }; // ######################### DEFINICJA KLASY RECTANGLE (PROSTOKĄT) ######################## class rectangle : public i_dr_obj{ POINT begin; POINT end; public: rectangle(std::vector<hpen> &tPen, std::vector<hbrush> &tBrush, pen &p, brush &b):i_dr_obj(tPen, tBrush, p, b){begin.x = begin.y = end.x = end.y = 0;}; virtual std::wstring toString(std::vector<hpen> &tPen, std::vector<hbrush> &tBrush) const { std::wstring str; wchar_t buffor[100]; wsprintf(buffor, L"rectangle %i %i; %i %i", begin.x, begin.y, end.x, end.y); str = buffor; str += L" "; str += tPen[this->pen_index].toString(); str += L" "; str += tBrush[this->brush_index].toString(); return str; } virtual void Draw(HDC &hdc, std::vector<hpen> &tPen, std::vector<hbrush> &tBrush) const { tPen[pen_index].SelectPen(hdc); tBrush[brush_index].SelectBrush(hdc); Rectangle(hdc, begin.x, begin.y, end.x, end.y); } virtual void DrawSelect(HDC &hdc) const { Rectangle(hdc, begin.x - ZN_SIZE, begin.y - ZN_SIZE, begin.x + ZN_SIZE, begin.y + ZN_SIZE); Rectangle(hdc, end.x - ZN_SIZE, end.y - ZN_SIZE, end.x + ZN_SIZE, end.y + ZN_SIZE); } virtual void WmLButtonDown(POINT &mp){ begin = mp; end = mp; } virtual void WmLButtonUp(POINT &mp){ end = mp; } virtual bool Select(POINT &ps){ if(CursorOnObject(ps)){ selected = true; return true; } return false; } virtual bool Select(RECT &r){ if(PtInRect(&r, begin) && PtInRect(&r, end)){ selected = true; return true; } return false; } virtual bool CursorOnObject(POINT &ps) const { RECT r; // zmienna pomocnicza SetRect(&r, min(begin.x, end.x), min(begin.y, end.y), max(begin.x, end.x), max(begin.y, end.y)); // ustawienie prostokąta return PtInRect(&r, ps); // zwraca prawdę, gdy punkt jest w prostokącie a fałsz gdy nie jest } virtual RECT GetRect() const { RECT r; SetRect(&r, min(begin.x, end.x), min(begin.y, end.y), max(begin.x, end.x), max(begin.y, end.y)); return r; } };

Dodanie nowego kodu klasy select_obj

Do projektu dodać należy plik nagłówkowy o nazwie select.h a w nim znajdzie się kod klasy obsługującej zaznaczanie obiektów:

#ifndef SELECT_H #define SELECT_H #include "drawing_class_def.h" class select_obj{ protected: i_dr_obj* objundercursor; // wskaźnik na obiekt znajdujący się pod kursorem w trybie zaznaczania i edycji hpen selpen; // pióro dla znaczników obiektów zaznaczonych hbrush selbrush; // kolor wypełnienia znaczników obiektu zaznaczonego hpen selframe; // pióro ramki zaznaczenia hpen selbycursor; // pióro rysowania znaczników obiektu, gdy ten jest pod kursorem myszki hpen selrangepen; // pióro używane do rysowania prostokąta zakresu zaznaczanych elementów std::vector<i_dr_obj*> tSelObj; // tablica zaznaczonych obiektów RECT selrect; // zaznaczenie lub obszar zaznaczany public: select_obj():objundercursor(NULL), selpen(255,0,0,1,pen::ps::solid), selbrush(brush(255,255,255)), selframe(150,150,150, 1, pen::ps::dot), selbycursor(0,0,255, 2, pen::ps::solid), selrangepen(0,150,255,1,pen::ps::dot){ SetRectEmpty(&selrect); } void WmLButtonDown(HWND hWndDraw, std::vector<i_dr_obj*> &tObj, WPARAM wParam, POINT mousepos){ // obsługa komunikatu wciśnięcia lewego przycisku myszki if(!(wParam & MK_SHIFT) && !tSelObj.empty() && (!objundercursor || !objundercursor->Selected())){ for(std::vector<i_dr_obj*>::iterator i = tSelObj.begin(); i < tSelObj.end(); i ++){ (*i)->Unselect(); } tSelObj.clear(); } if(objundercursor){ if(objundercursor->Selected()){ if(wParam & MK_SHIFT){ objundercursor->Unselect(); for(std::vector<i_dr_obj*>::iterator j = tSelObj.begin(); j < tSelObj.end(); j++){ if(objundercursor == *j){ tSelObj.erase(j); break; } } } }else{ tSelObj.push_back(objundercursor); objundercursor->Select(mousepos); } }else{ tSelObj.clear(); } if(!tSelObj.empty()){ selrect = tSelObj[0]->GetRect(); for(std::vector<i_dr_obj*>::iterator i = tSelObj.begin() + 1; i < tSelObj.end(); i++){ UnionRect(&selrect, &selrect, &(*i)->GetRect()); } }else{ SetRect(&selrect, mousepos.x, mousepos.y, mousepos.x, mousepos.y); } InvalidateRect(hWndDraw, NULL, true); } void WmLButtonUp(HWND hWndDraw, std::vector<i_dr_obj*> &tObj){ // obsługa komunikatu zwolnienia przycisku myszki if(tSelObj.empty()){ SetRect(&selrect, min(selrect.right, selrect.left), min(selrect.top, selrect.bottom), max(selrect.right, selrect.left), max(selrect.top, selrect.bottom)); bool sel; for(int i = tObj.size() - 1; i > -1; i--){ sel = tObj[i]->Selected(); if(tObj[i]->Select(selrect)){ if(sel){ tObj[i]->Unselect(); for(std::vector<i_dr_obj*>::iterator j = tSelObj.begin(); j < tSelObj.end(); j++){ if(tObj[i] == *j){ tSelObj.erase(j); break; } } }else{ tSelObj.push_back(tObj[i]); } } } if(!tSelObj.empty()){ selrect = tSelObj[0]->GetRect(); for(std::vector<i_dr_obj*>::iterator i = tSelObj.begin() + 1; i < tSelObj.end(); i++){ UnionRect(&selrect, &selrect, &(*i)->GetRect()); } }else{ SetRectEmpty(&selrect); } InvalidateRect(hWndDraw, NULL, true); } } void WmMouseMove(HWND hWndDraw, std::vector<i_dr_obj*> &tObj, WPARAM wParam, POINT &mousepos, state &st){ // obsługa ruchu myszki if(wParam & MK_LBUTTON){ if(st == state::sel && tSelObj.empty()){ selrect.right = mousepos.x; selrect.bottom = mousepos.y; } InvalidateRect(hWndDraw, NULL, true); // odświerzanie okna w celu odrysowania nowych ustawień obiektu }else if(st == state::sel){ objundercursor = NULL; for(int i = tObj.size() - 1; i > -1; i--){ if(tObj[i]->CursorOnObject(mousepos)){ objundercursor = tObj[i]; break; } } InvalidateRect(hWndDraw, false, true); } } void WmPaint(HDC &hdc){ // obsługa rysowania if(!tSelObj.empty()){ SelectObject(hdc, GetStockObject(NULL_BRUSH)); selframe.SelectPen(hdc); Rectangle(hdc, selrect.left, selrect.top, selrect.right, selrect.bottom); } selpen.SelectPen(hdc); selbrush.SelectBrush(hdc); for(std::vector<i_dr_obj*>::iterator i = tSelObj.begin(); i < tSelObj.end(); i++){ (*i)->DrawSelect(hdc); // rysowanie dodanych obiektów } if(tSelObj.empty() && selrect.right && selrect.bottom && selrect.top && selrect.left){ selrangepen.SelectPen(hdc); SelectObject(hdc, GetStockObject(NULL_BRUSH)); RECT r; SetRect(&r, min(selrect.right, selrect.left), min(selrect.top, selrect.bottom), max(selrect.right, selrect.left), max(selrect.top, selrect.bottom)); Rectangle(hdc, selrect.left, selrect.top, selrect.right, selrect.bottom); } if(objundercursor){ selbycursor.SelectPen(hdc); objundercursor->DrawSelect(hdc); } } void Clear(){ for(std::vector<i_dr_obj*>::iterator i = tSelObj.begin(); i < tSelObj.end(); i++){ (*i)->Unselect(); } tSelObj.clear(); objundercursor = NULL; SetRectEmpty(&selrect); } }; #endif

Plik select.h należy załączyć do pliku winmain.cpp.

Zmiany w funkcji procedury okna rysowania

Oto kod procedury okna rysowania, który również musiał ulegnąć zmianie:

// ################################### Procedura okna dokumentu ##################################### LRESULT CALLBACK WndDrawingProc(HWND hWndDraw, UINT msg, WPARAM wParam, LPARAM lParam){ static POINT mousepos; // współrzędne kursora w oknie static i_dr_obj* obj; // wskaźnik na interfejs dodawanego obiektu static std::vector<i_dr_obj*> tObj; // tablica interfejsów obiektów rysowanych static enum state st; // tryb rysowania lub edycji static std::vector<hpen> tPen; // tablica wykorzystywanych pędzli static std::vector<hbrush> tBrush; // tablica wykorzystywanych wypełnień static select_obj SelObjAct; switch(msg){ case WM_CREATE: { obj = NULL; } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWndDraw, &ps); SetBkMode(hdc, TRANSPARENT); for(std::vector<i_dr_obj*>::iterator i = tObj.begin(); i < tObj.end(); i++){ (*i)->Draw(hdc, tPen, tBrush); // rysowanie dodanych obiektów } if(st == state::sel){ SelObjAct.WmPaint(hdc); } if(obj){ obj->Draw(hdc, tPen, tBrush); // rysowanie dodawanego obiektu } EndPaint(hWndDraw, &ps); } break; case WM_LBUTTONDOWN: { SetCapture(hWndDraw); switch(st){ case dr_line: // tworzę obiekt typu line { obj = new line(tPen, tBrush, pen(255,0,0,1,pen::ps::solid), brush(0,0,150)); obj->WmLButtonDown(mousepos); } break; case dr_circle: // tworzę obiekt typu circle { obj = new circle(tPen, tBrush, pen(0,255,0,1,pen::ps::solid), brush(0,150,0)); obj->WmLButtonDown(mousepos); } break; case dr_rect: // tworzę obiekt typu rectangle { obj = new rectangle(tPen, tBrush, pen(0, 150, 255, 1, pen::ps::solid), brush(150,0,200)); obj->WmLButtonDown(mousepos); } break; case sel: // trzyb zaznaczania (nie obsłużony jeszcze) { SelObjAct.WmLButtonDown(hWndDraw, tObj, wParam, mousepos); } break; } } break; case WM_LBUTTONUP: { switch(st){ case dr_line: // dodawanie obiektu do kontenera { if(obj){ obj->WmLButtonUp(mousepos); tObj.push_back(obj); } } break; case dr_circle: // dodawanie obiektu do kontenera { if(obj){ obj->WmLButtonUp(mousepos); tObj.push_back(obj); } } break; case dr_rect: // dodawanie obiektu do kontenera { if(obj){ obj->WmLButtonUp(mousepos); tObj.push_back(obj); } } break; case sel: // tryb edycji { SelObjAct.WmLButtonUp(hWndDraw, tObj); } break; } obj = NULL; ReleaseCapture(); } break; case WM_MOUSEMOVE: { mousepos.x = LOWORD(lParam); // pobieranie współrzędnej x mousepos.y = HIWORD(lParam); // pobieranie współrzędnej y wchar_t buffor[100]; // bufor do konwersji na tekst wsprintf(buffor, L"X = %i",mousepos.x); // konwersja na tekst współrzędnej x SendMessage(statusbar, SB_SETTEXT, 0, (LPARAM)buffor); // ustawienie tekstu pierwszej pozycji paska kontrolki statusbar wsprintf(buffor, L"Y = %i",mousepos.y); // konwersja na tekst współrzędnej y SendMessage(statusbar, SB_SETTEXT, 1, (LPARAM)buffor); // ustawienie tekstu drugiej pozycji paska kontrolki statusbar if((wParam & MK_LBUTTON) && st != state::sel){ if(obj){ obj->WmLButtonUp(mousepos); // zmiana obiektu, podczas ruchu mychy } InvalidateRect(hWndDraw, NULL, true); // odświerzanie okna w celu odrysowania nowych ustawień obiektu } SelObjAct.WmMouseMove(hWndDraw, tObj, wParam, mousepos, st); } break; case WM_COMMAND: { if((HWND)lParam == toolbar){ UINT n = LOWORD(wParam); if(n != ID_SELECT && st == state::sel){ SelObjAct.Clear(); InvalidateRect(hWndDraw, NULL, true); } switch(n){ case ID_LINE: // komunikat przychodzący od przycisku toolbar-a o identyfikatorze ID_LINE { st = dr_line; SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie linii"); // ustawienie tekstu w ostatnim polu statusbar-u } break; case ID_CIRCLE: // to samo co poprzednio, tylko dla ID_CIRCLE { st = dr_circle; SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie okręgu"); // ustawienie tekstu w ostatnim polu statusbar-u } break; case ID_RECTANGLE: // to samo co poprzednio, tylko dla ID_RECTANGLE { st = dr_rect; SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie prostokąta"); // ustawienie tekstu w ostatnim polu statusbar-u } break; case ID_SELECT: // to samo co poprzednio, tylko dla ID_SELECT { st = sel; SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie edycja"); // ustawienie tekstu w ostatnim polu statusbar-u } break; } } } break; case WM_DESTROY: // tutaj sprzątanko się rozpoczyna { for(std::vector<i_dr_obj*>::iterator i = tObj.begin(); i < tObj.end(); i++){ delete *i; // usuwanie obiektów } // czyszczenie kontenerów tObj.clear(); tPen.clear(); tBrush.clear(); } break; } return DefWindowProc(hWndDraw, msg, wParam, lParam); }

Wygląd programu

Oto wygląd naszego programu, w którym można już zaznaczać poszczególne obiekty.

Program rysowanie z obsługą zaznaczania obiektów
Rys. 2
Widok programu Rysowanie obsługującego już jak widać zaznaczanie elementów.
Propozycje książek