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 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 selectionWindow.ShowWindow(SW_HIDE); 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 selectionWindow.ShowWindow(SW_HIDE); 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); 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::DrawSelectionBackground(COLORREF rgbBG) 286 { 287 if (toolsModel.GetActiveTool() == TOOL_FREESEL) 288 selectionModel.DrawBackgroundPoly(hDrawingDC, rgbBG); 289 else 290 selectionModel.DrawBackgroundRect(hDrawingDC, rgbBG); 291 } 292 293 void ImageModel::DeleteSelection() 294 { 295 if (selectionWindow.IsWindowVisible()) 296 ResetToPrevious(); 297 CopyPrevious(); 298 if (selectionWindow.IsWindowVisible()) 299 Undo(TRUE); 300 DrawSelectionBackground(paletteModel.GetBgColor()); 301 selectionWindow.ShowWindow(SW_HIDE); 302 NotifyImageChanged(); 303 } 304 305 void ImageModel::Bound(POINT& pt) 306 { 307 pt.x = max(0, min(pt.x, GetWidth())); 308 pt.y = max(0, min(pt.y, GetHeight())); 309 } 310