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 /* INCLUDES *********************************************************/
10 
11 #include "precomp.h"
12 
13 /* FUNCTIONS ********************************************************/
14 
15 void ImageModel::NotifyDimensionsChanged()
16 {
17     imageArea.SendMessage(WM_IMAGEMODELDIMENSIONSCHANGED);
18 }
19 
20 void ImageModel::NotifyImageChanged()
21 {
22     imageArea.SendMessage(WM_IMAGEMODELIMAGECHANGED);
23 }
24 
25 ImageModel::ImageModel()
26 {
27     currInd = 0;
28     undoSteps = 0;
29     redoSteps = 0;
30     imageSaved = TRUE;
31 
32     // prepare a minimal usable bitmap
33     int imgXRes = 1;
34     int imgYRes = 1;
35 
36     hDrawingDC = CreateCompatibleDC(NULL);
37     SelectObject(hDrawingDC, CreatePen(PS_SOLID, 0, paletteModel.GetFgColor()));
38     SelectObject(hDrawingDC, CreateSolidBrush(paletteModel.GetBgColor()));
39 
40     hBms[0] = CreateDIBWithProperties(imgXRes, imgYRes);
41     SelectObject(hDrawingDC, hBms[0]);
42     Rectangle(hDrawingDC, 0 - 1, 0 - 1, imgXRes + 1, imgYRes + 1);
43 }
44 
45 void ImageModel::CopyPrevious()
46 {
47     DeleteObject(hBms[(currInd + 1) % HISTORYSIZE]);
48     hBms[(currInd + 1) % HISTORYSIZE] = (HBITMAP) CopyImage(hBms[currInd], IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG);
49     currInd = (currInd + 1) % HISTORYSIZE;
50     if (undoSteps < HISTORYSIZE - 1)
51         undoSteps++;
52     redoSteps = 0;
53     SelectObject(hDrawingDC, hBms[currInd]);
54     imageSaved = FALSE;
55 }
56 
57 void ImageModel::Undo()
58 {
59     if (undoSteps > 0)
60     {
61         int oldWidth = GetWidth();
62         int oldHeight = GetHeight();
63         selectionWindow.ShowWindow(SW_HIDE);
64         currInd = (currInd + HISTORYSIZE - 1) % HISTORYSIZE;
65         SelectObject(hDrawingDC, hBms[currInd]);
66         undoSteps--;
67         if (redoSteps < HISTORYSIZE - 1)
68             redoSteps++;
69         if (GetWidth() != oldWidth || GetHeight() != oldHeight)
70             NotifyDimensionsChanged();
71         NotifyImageChanged();
72     }
73 }
74 
75 void ImageModel::Redo()
76 {
77     if (redoSteps > 0)
78     {
79         int oldWidth = GetWidth();
80         int oldHeight = GetHeight();
81         selectionWindow.ShowWindow(SW_HIDE);
82         currInd = (currInd + 1) % HISTORYSIZE;
83         SelectObject(hDrawingDC, hBms[currInd]);
84         redoSteps--;
85         if (undoSteps < HISTORYSIZE - 1)
86             undoSteps++;
87         if (GetWidth() != oldWidth || GetHeight() != oldHeight)
88             NotifyDimensionsChanged();
89         NotifyImageChanged();
90     }
91 }
92 
93 void ImageModel::ResetToPrevious()
94 {
95     DeleteObject(hBms[currInd]);
96     hBms[currInd] =
97         (HBITMAP) CopyImage(hBms[(currInd + HISTORYSIZE - 1) % HISTORYSIZE], IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG);
98     SelectObject(hDrawingDC, hBms[currInd]);
99     NotifyImageChanged();
100 }
101 
102 void ImageModel::ClearHistory()
103 {
104     undoSteps = 0;
105     redoSteps = 0;
106 }
107 
108 void ImageModel::Insert(HBITMAP hbm)
109 {
110     int oldWidth = GetWidth();
111     int oldHeight = GetHeight();
112     DeleteObject(hBms[(currInd + 1) % HISTORYSIZE]);
113     hBms[(currInd + 1) % HISTORYSIZE] = hbm;
114     currInd = (currInd + 1) % HISTORYSIZE;
115     if (undoSteps < HISTORYSIZE - 1)
116         undoSteps++;
117     redoSteps = 0;
118     SelectObject(hDrawingDC, hBms[currInd]);
119     if (GetWidth() != oldWidth || GetHeight() != oldHeight)
120         NotifyDimensionsChanged();
121     NotifyImageChanged();
122 }
123 
124 void ImageModel::Crop(int nWidth, int nHeight, int nOffsetX, int nOffsetY)
125 {
126     HDC hdc;
127     HPEN oldPen;
128     HBRUSH oldBrush;
129     int oldWidth = GetWidth();
130     int oldHeight = GetHeight();
131 
132     if (nWidth <= 0)
133         nWidth = 1;
134     if (nHeight <= 0)
135         nHeight = 1;
136 
137     SelectObject(hDrawingDC, hBms[currInd]);
138     DeleteObject(hBms[(currInd + 1) % HISTORYSIZE]);
139     hBms[(currInd + 1) % HISTORYSIZE] = CreateDIBWithProperties(nWidth, nHeight);
140     currInd = (currInd + 1) % HISTORYSIZE;
141     if (undoSteps < HISTORYSIZE - 1)
142         undoSteps++;
143     redoSteps = 0;
144 
145     hdc = CreateCompatibleDC(hDrawingDC);
146     SelectObject(hdc, hBms[currInd]);
147 
148     oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, 1, paletteModel.GetBgColor()));
149     oldBrush = (HBRUSH) SelectObject(hdc, CreateSolidBrush(paletteModel.GetBgColor()));
150     Rectangle(hdc, 0, 0, nWidth, nHeight);
151     BitBlt(hdc, -nOffsetX, -nOffsetY, GetWidth(), GetHeight(), hDrawingDC, 0, 0, SRCCOPY);
152     DeleteObject(SelectObject(hdc, oldBrush));
153     DeleteObject(SelectObject(hdc, oldPen));
154     DeleteDC(hdc);
155     SelectObject(hDrawingDC, hBms[currInd]);
156 
157     if (GetWidth() != oldWidth || GetHeight() != oldHeight)
158         NotifyDimensionsChanged();
159     NotifyImageChanged();
160 }
161 
162 void ImageModel::SaveImage(LPTSTR lpFileName)
163 {
164     SaveDIBToFile(hBms[currInd], lpFileName, hDrawingDC);
165 }
166 
167 BOOL ImageModel::IsImageSaved()
168 {
169     return imageSaved;
170 }
171 
172 BOOL ImageModel::HasUndoSteps()
173 {
174     return undoSteps > 0;
175 }
176 
177 BOOL ImageModel::HasRedoSteps()
178 {
179     return redoSteps > 0;
180 }
181 
182 void ImageModel::StretchSkew(int nStretchPercentX, int nStretchPercentY, int nSkewDegX, int nSkewDegY)
183 {
184     int oldWidth = GetWidth();
185     int oldHeight = GetHeight();
186     Insert((HBITMAP) CopyImage(hBms[currInd], IMAGE_BITMAP,
187            GetWidth() * nStretchPercentX / 100,
188            GetHeight() * nStretchPercentY / 100, 0));
189     if (GetWidth() != oldWidth || GetHeight() != oldHeight)
190         NotifyDimensionsChanged();
191     NotifyImageChanged();
192 }
193 
194 int ImageModel::GetWidth()
195 {
196     return GetDIBWidth(hBms[currInd]);
197 }
198 
199 int ImageModel::GetHeight()
200 {
201     return GetDIBHeight(hBms[currInd]);
202 }
203 
204 void ImageModel::InvertColors()
205 {
206     RECT rect = {0, 0, GetWidth(), GetHeight()};
207     CopyPrevious();
208     InvertRect(hDrawingDC, &rect);
209     NotifyImageChanged();
210 }
211 
212 void ImageModel::Clear(COLORREF color)
213 {
214     Rectangle(hDrawingDC, 0 - 1, 0 - 1, GetWidth() + 1, GetHeight() + 1);
215     NotifyImageChanged();
216 }
217 
218 HDC ImageModel::GetDC()
219 {
220     return hDrawingDC;
221 }
222 
223 void ImageModel::FlipHorizontally()
224 {
225     CopyPrevious();
226     StretchBlt(hDrawingDC, GetWidth() - 1, 0, -GetWidth(), GetHeight(), GetDC(), 0, 0,
227                GetWidth(), GetHeight(), SRCCOPY);
228     NotifyImageChanged();
229 }
230 
231 void ImageModel::FlipVertically()
232 {
233     CopyPrevious();
234     StretchBlt(hDrawingDC, 0, GetHeight() - 1, GetWidth(), -GetHeight(), GetDC(), 0, 0,
235                GetWidth(), GetHeight(), SRCCOPY);
236     NotifyImageChanged();
237 }
238 
239 void ImageModel::RotateNTimes90Degrees(int iN)
240 {
241     if (iN == 2)
242     {
243         CopyPrevious();
244         StretchBlt(hDrawingDC, GetWidth() - 1, GetHeight() - 1, -GetWidth(), -GetHeight(), GetDC(),
245                    0, 0, GetWidth(), GetHeight(), SRCCOPY);
246     }
247     NotifyImageChanged();
248 }
249