[MSPAINT] Make selection window-less (#5208)
[reactos.git] / base / applications / mspaint / history.cpp
1 /*
2 * PROJECT: PAINT for ReactOS
3 * LICENSE: LGPL
4 * FILE: base/applications/mspaint/history.cpp
5 * PURPOSE: Undo and redo functionality
6 * PROGRAMMERS: Benedikt Freisen
7 */
8
9 #include "precomp.h"
10
11 ImageModel imageModel;
12
13 /* FUNCTIONS ********************************************************/
14
15 void ImageModel::NotifyDimensionsChanged()
16 {
17 if (imageArea.IsWindow())
18 imageArea.SendMessage(WM_IMAGEMODELDIMENSIONSCHANGED);
19 }
20
21 void ImageModel::NotifyImageChanged()
22 {
23 if (imageArea.IsWindow())
24 imageArea.SendMessage(WM_IMAGEMODELIMAGECHANGED);
25 }
26
27 ImageModel::ImageModel()
28 {
29 currInd = 0;
30 undoSteps = 0;
31 redoSteps = 0;
32 imageSaved = TRUE;
33
34 // prepare a minimal usable bitmap
35 int imgXRes = 1;
36 int imgYRes = 1;
37
38 hDrawingDC = CreateCompatibleDC(NULL);
39 SelectObject(hDrawingDC, CreatePen(PS_SOLID, 0, paletteModel.GetFgColor()));
40 SelectObject(hDrawingDC, CreateSolidBrush(paletteModel.GetBgColor()));
41
42 hBms[0] = CreateDIBWithProperties(imgXRes, imgYRes);
43 SelectObject(hDrawingDC, hBms[0]);
44 Rectangle(hDrawingDC, 0 - 1, 0 - 1, imgXRes + 1, imgYRes + 1);
45 }
46
47 void ImageModel::CopyPrevious()
48 {
49 ATLTRACE("%s: %d\n", __FUNCTION__, currInd);
50 DeleteObject(hBms[(currInd + 1) % HISTORYSIZE]);
51 hBms[(currInd + 1) % HISTORYSIZE] = CopyDIBImage(hBms[currInd]);
52 currInd = (currInd + 1) % HISTORYSIZE;
53 if (undoSteps < HISTORYSIZE - 1)
54 undoSteps++;
55 redoSteps = 0;
56 SelectObject(hDrawingDC, hBms[currInd]);
57 imageSaved = FALSE;
58 }
59
60 void ImageModel::Undo(BOOL bClearRedo)
61 {
62 ATLTRACE("%s: %d\n", __FUNCTION__, undoSteps);
63 if (undoSteps > 0)
64 {
65 int oldWidth = GetWidth();
66 int oldHeight = GetHeight();
67 selectionModel.m_bShow = FALSE;
68 currInd = (currInd + HISTORYSIZE - 1) % HISTORYSIZE;
69 SelectObject(hDrawingDC, hBms[currInd]);
70 undoSteps--;
71 if (bClearRedo)
72 redoSteps = 0;
73 else if (redoSteps < HISTORYSIZE - 1)
74 redoSteps++;
75 if (GetWidth() != oldWidth || GetHeight() != oldHeight)
76 NotifyDimensionsChanged();
77 NotifyImageChanged();
78 }
79 }
80
81 void ImageModel::Redo()
82 {
83 ATLTRACE("%s: %d\n", __FUNCTION__, redoSteps);
84 if (redoSteps > 0)
85 {
86 int oldWidth = GetWidth();
87 int oldHeight = GetHeight();
88 selectionModel.m_bShow = FALSE;
89 currInd = (currInd + 1) % HISTORYSIZE;
90 SelectObject(hDrawingDC, hBms[currInd]);
91 redoSteps--;
92 if (undoSteps < HISTORYSIZE - 1)
93 undoSteps++;
94 if (GetWidth() != oldWidth || GetHeight() != oldHeight)
95 NotifyDimensionsChanged();
96 NotifyImageChanged();
97 }
98 }
99
100 void ImageModel::ResetToPrevious()
101 {
102 ATLTRACE("%s: %d\n", __FUNCTION__, currInd);
103 DeleteObject(hBms[currInd]);
104 hBms[currInd] = CopyDIBImage(hBms[(currInd + HISTORYSIZE - 1) % HISTORYSIZE]);
105 SelectObject(hDrawingDC, hBms[currInd]);
106 NotifyImageChanged();
107 }
108
109 void ImageModel::ClearHistory()
110 {
111 undoSteps = 0;
112 redoSteps = 0;
113 }
114
115 void ImageModel::Insert(HBITMAP hbm)
116 {
117 int oldWidth = GetWidth();
118 int oldHeight = GetHeight();
119 DeleteObject(hBms[(currInd + 1) % HISTORYSIZE]);
120 hBms[(currInd + 1) % HISTORYSIZE] = hbm;
121 currInd = (currInd + 1) % HISTORYSIZE;
122 if (undoSteps < HISTORYSIZE - 1)
123 undoSteps++;
124 redoSteps = 0;
125 SelectObject(hDrawingDC, hBms[currInd]);
126 if (GetWidth() != oldWidth || GetHeight() != oldHeight)
127 NotifyDimensionsChanged();
128 NotifyImageChanged();
129 }
130
131 void ImageModel::Crop(int nWidth, int nHeight, int nOffsetX, int nOffsetY)
132 {
133 HDC hdc;
134 HPEN oldPen;
135 HBRUSH oldBrush;
136 int oldWidth = GetWidth();
137 int oldHeight = GetHeight();
138
139 if (nWidth <= 0)
140 nWidth = 1;
141 if (nHeight <= 0)
142 nHeight = 1;
143
144 SelectObject(hDrawingDC, hBms[currInd]);
145 DeleteObject(hBms[(currInd + 1) % HISTORYSIZE]);
146 hBms[(currInd + 1) % HISTORYSIZE] = CreateDIBWithProperties(nWidth, nHeight);
147 currInd = (currInd + 1) % HISTORYSIZE;
148 if (undoSteps < HISTORYSIZE - 1)
149 undoSteps++;
150 redoSteps = 0;
151
152 hdc = CreateCompatibleDC(hDrawingDC);
153 SelectObject(hdc, hBms[currInd]);
154
155 oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, 1, paletteModel.GetBgColor()));
156 oldBrush = (HBRUSH) SelectObject(hdc, CreateSolidBrush(paletteModel.GetBgColor()));
157 Rectangle(hdc, 0, 0, nWidth, nHeight);
158 BitBlt(hdc, -nOffsetX, -nOffsetY, GetWidth(), GetHeight(), hDrawingDC, 0, 0, SRCCOPY);
159 DeleteObject(SelectObject(hdc, oldBrush));
160 DeleteObject(SelectObject(hdc, oldPen));
161 DeleteDC(hdc);
162 SelectObject(hDrawingDC, hBms[currInd]);
163
164 if (GetWidth() != oldWidth || GetHeight() != oldHeight)
165 NotifyDimensionsChanged();
166 NotifyImageChanged();
167 }
168
169 void ImageModel::SaveImage(LPTSTR lpFileName)
170 {
171 SaveDIBToFile(hBms[currInd], lpFileName, hDrawingDC);
172 }
173
174 BOOL ImageModel::IsImageSaved() const
175 {
176 return imageSaved;
177 }
178
179 BOOL ImageModel::HasUndoSteps() const
180 {
181 return undoSteps > 0;
182 }
183
184 BOOL ImageModel::HasRedoSteps() const
185 {
186 return redoSteps > 0;
187 }
188
189 void ImageModel::StretchSkew(int nStretchPercentX, int nStretchPercentY, int nSkewDegX, int nSkewDegY)
190 {
191 int oldWidth = GetWidth();
192 int oldHeight = GetHeight();
193 INT newWidth = oldWidth * nStretchPercentX / 100;
194 INT newHeight = oldHeight * nStretchPercentY / 100;
195 if (oldWidth != newWidth || oldHeight != newHeight)
196 {
197 HBITMAP hbm0 = CopyDIBImage(hBms[currInd], newWidth, newHeight);
198 Insert(hbm0);
199 }
200 if (nSkewDegX)
201 {
202 HBITMAP hbm1 = SkewDIB(hDrawingDC, hBms[currInd], nSkewDegX, FALSE);
203 Insert(hbm1);
204 }
205 if (nSkewDegY)
206 {
207 HBITMAP hbm2 = SkewDIB(hDrawingDC, hBms[currInd], nSkewDegY, TRUE);
208 Insert(hbm2);
209 }
210 if (GetWidth() != oldWidth || GetHeight() != oldHeight)
211 NotifyDimensionsChanged();
212 NotifyImageChanged();
213 }
214
215 int ImageModel::GetWidth() const
216 {
217 return GetDIBWidth(hBms[currInd]);
218 }
219
220 int ImageModel::GetHeight() const
221 {
222 return GetDIBHeight(hBms[currInd]);
223 }
224
225 void ImageModel::InvertColors()
226 {
227 RECT rect = {0, 0, GetWidth(), GetHeight()};
228 CopyPrevious();
229 InvertRect(hDrawingDC, &rect);
230 NotifyImageChanged();
231 }
232
233 void ImageModel::Clear(COLORREF color)
234 {
235 Rectangle(hDrawingDC, 0 - 1, 0 - 1, GetWidth() + 1, GetHeight() + 1);
236 NotifyImageChanged();
237 }
238
239 HDC ImageModel::GetDC()
240 {
241 return hDrawingDC;
242 }
243
244 void ImageModel::FlipHorizontally()
245 {
246 CopyPrevious();
247 StretchBlt(hDrawingDC, GetWidth() - 1, 0, -GetWidth(), GetHeight(), GetDC(), 0, 0,
248 GetWidth(), GetHeight(), SRCCOPY);
249 NotifyImageChanged();
250 }
251
252 void ImageModel::FlipVertically()
253 {
254 CopyPrevious();
255 StretchBlt(hDrawingDC, 0, GetHeight() - 1, GetWidth(), -GetHeight(), GetDC(), 0, 0,
256 GetWidth(), GetHeight(), SRCCOPY);
257 NotifyImageChanged();
258 }
259
260 void ImageModel::RotateNTimes90Degrees(int iN)
261 {
262 switch (iN)
263 {
264 case 1:
265 case 3:
266 DeleteObject(hBms[(currInd + 1) % HISTORYSIZE]);
267 hBms[(currInd + 1) % HISTORYSIZE] = Rotate90DegreeBlt(hDrawingDC, GetWidth(), GetHeight(), iN == 1, FALSE);
268 currInd = (currInd + 1) % HISTORYSIZE;
269 if (undoSteps < HISTORYSIZE - 1)
270 undoSteps++;
271 redoSteps = 0;
272 SelectObject(hDrawingDC, hBms[currInd]);
273 imageSaved = FALSE;
274 NotifyDimensionsChanged();
275 break;
276 case 2:
277 CopyPrevious();
278 StretchBlt(hDrawingDC, GetWidth() - 1, GetHeight() - 1, -GetWidth(), -GetHeight(), GetDC(),
279 0, 0, GetWidth(), GetHeight(), SRCCOPY);
280 break;
281 }
282 NotifyImageChanged();
283 }
284
285 void ImageModel::DeleteSelection()
286 {
287 if (!selectionModel.m_bShow)
288 return;
289
290 selectionModel.TakeOff();
291 selectionModel.m_bShow = FALSE;
292 selectionModel.ClearColor();
293 selectionModel.ClearMask();
294 NotifyImageChanged();
295 }
296
297 void ImageModel::Bound(POINT& pt)
298 {
299 pt.x = max(0, min(pt.x, GetWidth()));
300 pt.y = max(0, min(pt.y, GetHeight()));
301 }