1 // Scintilla source code edit control 2 /** @file LineMarker.cxx 3 ** Defines the look of a line marker in the margin. 4 **/ 5 // Copyright 1998-2011 by Neil Hodgson <[email protected]> 6 // The License.txt file describes the conditions under which this software may be distributed. 7 8 #include <cstring> 9 #include <cmath> 10 11 #include <stdexcept> 12 #include <string> 13 #include <string_view> 14 #include <vector> 15 #include <map> 16 #include <algorithm> 17 #include <memory> 18 19 #include "Platform.h" 20 21 #include "Scintilla.h" 22 23 #include "IntegerRectangle.h" 24 #include "XPM.h" 25 #include "LineMarker.h" 26 27 using namespace Scintilla; 28 29 LineMarker::LineMarker(const LineMarker &other) { 30 // Defined to avoid pxpm and image being blindly copied, not as a complete copy constructor. 31 markType = other.markType; 32 fore = other.fore; 33 back = other.back; 34 backSelected = other.backSelected; 35 alpha = other.alpha; 36 if (other.pxpm) 37 pxpm = std::make_unique<XPM>(*other.pxpm); 38 else 39 pxpm = nullptr; 40 if (other.image) 41 image = std::make_unique<RGBAImage>(*other.image); 42 else 43 image = nullptr; 44 customDraw = other.customDraw; 45 } 46 47 LineMarker &LineMarker::operator=(const LineMarker &other) { 48 // Defined to avoid pxpm and image being blindly copied, not as a complete assignment operator. 49 if (this != &other) { 50 markType = other.markType; 51 fore = other.fore; 52 back = other.back; 53 backSelected = other.backSelected; 54 alpha = other.alpha; 55 if (other.pxpm) 56 pxpm = std::make_unique<XPM>(*other.pxpm); 57 else 58 pxpm = nullptr; 59 if (other.image) 60 image = std::make_unique<RGBAImage>(*other.image); 61 else 62 image = nullptr; 63 customDraw = other.customDraw; 64 } 65 return *this; 66 } 67 68 void LineMarker::SetXPM(const char *textForm) { 69 pxpm = std::make_unique<XPM>(textForm); 70 markType = SC_MARK_PIXMAP; 71 } 72 73 void LineMarker::SetXPM(const char *const *linesForm) { 74 pxpm = std::make_unique<XPM>(linesForm); 75 markType = SC_MARK_PIXMAP; 76 } 77 78 void LineMarker::SetRGBAImage(Point sizeRGBAImage, float scale, const unsigned char *pixelsRGBAImage) { 79 image = std::make_unique<RGBAImage>(static_cast<int>(sizeRGBAImage.x), static_cast<int>(sizeRGBAImage.y), scale, pixelsRGBAImage); 80 markType = SC_MARK_RGBAIMAGE; 81 } 82 83 static void DrawBox(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore, ColourDesired back) { 84 const PRectangle rc = PRectangle::FromInts( 85 centreX - armSize, 86 centreY - armSize, 87 centreX + armSize + 1, 88 centreY + armSize + 1); 89 surface->RectangleDraw(rc, back, fore); 90 } 91 92 static void DrawCircle(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore, ColourDesired back) { 93 const PRectangle rcCircle = PRectangle::FromInts( 94 centreX - armSize, 95 centreY - armSize, 96 centreX + armSize + 1, 97 centreY + armSize + 1); 98 surface->Ellipse(rcCircle, back, fore); 99 } 100 101 static void DrawPlus(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore) { 102 const PRectangle rcV = PRectangle::FromInts(centreX, centreY - armSize + 2, centreX + 1, centreY + armSize - 2 + 1); 103 surface->FillRectangle(rcV, fore); 104 const PRectangle rcH = PRectangle::FromInts(centreX - armSize + 2, centreY, centreX + armSize - 2 + 1, centreY + 1); 105 surface->FillRectangle(rcH, fore); 106 } 107 108 static void DrawMinus(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore) { 109 const PRectangle rcH = PRectangle::FromInts(centreX - armSize + 2, centreY, centreX + armSize - 2 + 1, centreY + 1); 110 surface->FillRectangle(rcH, fore); 111 } 112 113 void LineMarker::Draw(Surface *surface, PRectangle &rcWhole, Font &fontForCharacter, FoldPart part, int marginStyle) const { 114 if (customDraw) { 115 customDraw(surface, rcWhole, fontForCharacter, static_cast<int>(part), marginStyle, this); 116 return; 117 } 118 119 ColourDesired colourHead = back; 120 ColourDesired colourBody = back; 121 ColourDesired colourTail = back; 122 123 switch (part) { 124 case FoldPart::head: 125 case FoldPart::headWithTail: 126 colourHead = backSelected; 127 colourTail = backSelected; 128 break; 129 case FoldPart::body: 130 colourHead = backSelected; 131 colourBody = backSelected; 132 break; 133 case FoldPart::tail: 134 colourBody = backSelected; 135 colourTail = backSelected; 136 break; 137 default: 138 // FoldPart::undefined 139 break; 140 } 141 142 if ((markType == SC_MARK_PIXMAP) && (pxpm)) { 143 pxpm->Draw(surface, rcWhole); 144 return; 145 } 146 if ((markType == SC_MARK_RGBAIMAGE) && (image)) { 147 // Make rectangle just large enough to fit image centred on centre of rcWhole 148 PRectangle rcImage; 149 rcImage.top = ((rcWhole.top + rcWhole.bottom) - image->GetScaledHeight()) / 2; 150 rcImage.bottom = rcImage.top + image->GetScaledHeight(); 151 rcImage.left = ((rcWhole.left + rcWhole.right) - image->GetScaledWidth()) / 2; 152 rcImage.right = rcImage.left + image->GetScaledWidth(); 153 surface->DrawRGBAImage(rcImage, image->GetWidth(), image->GetHeight(), image->Pixels()); 154 return; 155 } 156 157 const IntegerRectangle ircWhole(rcWhole); 158 // Restrict most shapes a bit 159 const PRectangle rc(rcWhole.left, rcWhole.top + 1, rcWhole.right, rcWhole.bottom - 1); 160 // Ensure does not go beyond edge 161 const int minDim = std::min(ircWhole.Width(), ircWhole.Height() - 2) - 1; 162 int centreX = (ircWhole.right + ircWhole.left) / 2; 163 const int centreY = (ircWhole.bottom + ircWhole.top) / 2; 164 const int dimOn2 = minDim / 2; 165 const int dimOn4 = minDim / 4; 166 const int blobSize = dimOn2 - 1; 167 const int armSize = dimOn2 - 2; 168 if (marginStyle == SC_MARGIN_NUMBER || marginStyle == SC_MARGIN_TEXT || marginStyle == SC_MARGIN_RTEXT) { 169 // On textual margins move marker to the left to try to avoid overlapping the text 170 centreX = ircWhole.left + dimOn2 + 1; 171 } 172 173 switch (markType) { 174 case SC_MARK_ROUNDRECT: { 175 PRectangle rcRounded = rc; 176 rcRounded.left = rc.left + 1; 177 rcRounded.right = rc.right - 1; 178 surface->RoundedRectangle(rcRounded, fore, back); 179 } 180 break; 181 182 case SC_MARK_CIRCLE: { 183 const PRectangle rcCircle = PRectangle::FromInts( 184 centreX - dimOn2, 185 centreY - dimOn2, 186 centreX + dimOn2, 187 centreY + dimOn2); 188 surface->Ellipse(rcCircle, fore, back); 189 } 190 break; 191 192 case SC_MARK_ARROW: { 193 Point pts[] = { 194 Point::FromInts(centreX - dimOn4, centreY - dimOn2), 195 Point::FromInts(centreX - dimOn4, centreY + dimOn2), 196 Point::FromInts(centreX + dimOn2 - dimOn4, centreY), 197 }; 198 surface->Polygon(pts, std::size(pts), fore, back); 199 } 200 break; 201 202 case SC_MARK_ARROWDOWN: { 203 Point pts[] = { 204 Point::FromInts(centreX - dimOn2, centreY - dimOn4), 205 Point::FromInts(centreX + dimOn2, centreY - dimOn4), 206 Point::FromInts(centreX, centreY + dimOn2 - dimOn4), 207 }; 208 surface->Polygon(pts, std::size(pts), fore, back); 209 } 210 break; 211 212 case SC_MARK_PLUS: { 213 Point pts[] = { 214 Point::FromInts(centreX - armSize, centreY - 1), 215 Point::FromInts(centreX - 1, centreY - 1), 216 Point::FromInts(centreX - 1, centreY - armSize), 217 Point::FromInts(centreX + 1, centreY - armSize), 218 Point::FromInts(centreX + 1, centreY - 1), 219 Point::FromInts(centreX + armSize, centreY - 1), 220 Point::FromInts(centreX + armSize, centreY + 1), 221 Point::FromInts(centreX + 1, centreY + 1), 222 Point::FromInts(centreX + 1, centreY + armSize), 223 Point::FromInts(centreX - 1, centreY + armSize), 224 Point::FromInts(centreX - 1, centreY + 1), 225 Point::FromInts(centreX - armSize, centreY + 1), 226 }; 227 surface->Polygon(pts, std::size(pts), fore, back); 228 } 229 break; 230 231 case SC_MARK_MINUS: { 232 Point pts[] = { 233 Point::FromInts(centreX - armSize, centreY - 1), 234 Point::FromInts(centreX + armSize, centreY - 1), 235 Point::FromInts(centreX + armSize, centreY + 1), 236 Point::FromInts(centreX - armSize, centreY + 1), 237 }; 238 surface->Polygon(pts, std::size(pts), fore, back); 239 } 240 break; 241 242 case SC_MARK_SMALLRECT: { 243 PRectangle rcSmall; 244 rcSmall.left = rc.left + 1; 245 rcSmall.top = rc.top + 2; 246 rcSmall.right = rc.right - 1; 247 rcSmall.bottom = rc.bottom - 2; 248 surface->RectangleDraw(rcSmall, fore, back); 249 } 250 break; 251 252 case SC_MARK_EMPTY: 253 case SC_MARK_BACKGROUND: 254 case SC_MARK_UNDERLINE: 255 case SC_MARK_AVAILABLE: 256 // An invisible marker so don't draw anything 257 break; 258 259 case SC_MARK_VLINE: { 260 surface->PenColour(colourBody); 261 surface->MoveTo(centreX, ircWhole.top); 262 surface->LineTo(centreX, ircWhole.bottom); 263 } 264 break; 265 266 case SC_MARK_LCORNER: { 267 surface->PenColour(colourTail); 268 surface->MoveTo(centreX, ircWhole.top); 269 surface->LineTo(centreX, centreY); 270 surface->LineTo(ircWhole.right - 1, centreY); 271 } 272 break; 273 274 case SC_MARK_TCORNER: { 275 surface->PenColour(colourTail); 276 surface->MoveTo(centreX, centreY); 277 surface->LineTo(ircWhole.right - 1, centreY); 278 279 surface->PenColour(colourBody); 280 surface->MoveTo(centreX, ircWhole.top); 281 surface->LineTo(centreX, centreY + 1); 282 283 surface->PenColour(colourHead); 284 surface->LineTo(centreX, ircWhole.bottom); 285 } 286 break; 287 288 case SC_MARK_LCORNERCURVE: { 289 surface->PenColour(colourTail); 290 surface->MoveTo(centreX, ircWhole.top); 291 surface->LineTo(centreX, centreY - 3); 292 surface->LineTo(centreX + 3, centreY); 293 surface->LineTo(ircWhole.right - 1, centreY); 294 } 295 break; 296 297 case SC_MARK_TCORNERCURVE: { 298 surface->PenColour(colourTail); 299 surface->MoveTo(centreX, centreY - 3); 300 surface->LineTo(centreX + 3, centreY); 301 surface->LineTo(ircWhole.right - 1, centreY); 302 303 surface->PenColour(colourBody); 304 surface->MoveTo(centreX, ircWhole.top); 305 surface->LineTo(centreX, centreY - 2); 306 307 surface->PenColour(colourHead); 308 surface->LineTo(centreX, ircWhole.bottom); 309 } 310 break; 311 312 case SC_MARK_BOXPLUS: { 313 DrawBox(surface, centreX, centreY, blobSize, fore, colourHead); 314 DrawPlus(surface, centreX, centreY, blobSize, fore); 315 } 316 break; 317 318 case SC_MARK_BOXPLUSCONNECTED: { 319 if (part == FoldPart::headWithTail) 320 surface->PenColour(colourTail); 321 else 322 surface->PenColour(colourBody); 323 surface->MoveTo(centreX, centreY + blobSize); 324 surface->LineTo(centreX, ircWhole.bottom); 325 326 surface->PenColour(colourBody); 327 surface->MoveTo(centreX, ircWhole.top); 328 surface->LineTo(centreX, centreY - blobSize); 329 330 DrawBox(surface, centreX, centreY, blobSize, fore, colourHead); 331 DrawPlus(surface, centreX, centreY, blobSize, fore); 332 333 if (part == FoldPart::body) { 334 surface->PenColour(colourTail); 335 surface->MoveTo(centreX + 1, centreY + blobSize); 336 surface->LineTo(centreX + blobSize + 1, centreY + blobSize); 337 338 surface->MoveTo(centreX + blobSize, centreY + blobSize); 339 surface->LineTo(centreX + blobSize, centreY - blobSize); 340 341 surface->MoveTo(centreX + 1, centreY - blobSize); 342 surface->LineTo(centreX + blobSize + 1, centreY - blobSize); 343 } 344 } 345 break; 346 347 case SC_MARK_BOXMINUS: { 348 DrawBox(surface, centreX, centreY, blobSize, fore, colourHead); 349 DrawMinus(surface, centreX, centreY, blobSize, fore); 350 351 surface->PenColour(fore); 352 surface->MoveTo(centreX, centreY + blobSize); 353 surface->LineTo(centreX, ircWhole.bottom); 354 } 355 break; 356 357 case SC_MARK_BOXMINUSCONNECTED: { 358 DrawBox(surface, centreX, centreY, blobSize, fore, colourHead); 359 DrawMinus(surface, centreX, centreY, blobSize, fore); 360 361 surface->PenColour(fore); 362 surface->MoveTo(centreX, centreY + blobSize); 363 surface->LineTo(centreX, ircWhole.bottom); 364 365 surface->PenColour(colourBody); 366 surface->MoveTo(centreX, ircWhole.top); 367 surface->LineTo(centreX, centreY - blobSize); 368 369 if (part == FoldPart::body) { 370 surface->PenColour(colourTail); 371 surface->MoveTo(centreX + 1, centreY + blobSize); 372 surface->LineTo(centreX + blobSize + 1, centreY + blobSize); 373 374 surface->MoveTo(centreX + blobSize, centreY + blobSize); 375 surface->LineTo(centreX + blobSize, centreY - blobSize); 376 377 surface->MoveTo(centreX + 1, centreY - blobSize); 378 surface->LineTo(centreX + blobSize + 1, centreY - blobSize); 379 } 380 } 381 break; 382 383 case SC_MARK_CIRCLEPLUS: { 384 DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead); 385 DrawPlus(surface, centreX, centreY, blobSize, colourTail); 386 } 387 break; 388 389 case SC_MARK_CIRCLEPLUSCONNECTED: { 390 if (part == FoldPart::headWithTail) 391 surface->PenColour(colourTail); 392 else 393 surface->PenColour(colourBody); 394 surface->MoveTo(centreX, centreY + blobSize); 395 surface->LineTo(centreX, ircWhole.bottom); 396 397 surface->PenColour(colourBody); 398 surface->MoveTo(centreX, ircWhole.top); 399 surface->LineTo(centreX, centreY - blobSize); 400 401 DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead); 402 DrawPlus(surface, centreX, centreY, blobSize, colourTail); 403 } 404 break; 405 406 case SC_MARK_CIRCLEMINUS: { 407 surface->PenColour(colourHead); 408 surface->MoveTo(centreX, centreY + blobSize); 409 surface->LineTo(centreX, ircWhole.bottom); 410 411 DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead); 412 DrawMinus(surface, centreX, centreY, blobSize, colourTail); 413 } 414 break; 415 416 case SC_MARK_CIRCLEMINUSCONNECTED: { 417 surface->PenColour(colourHead); 418 surface->MoveTo(centreX, centreY + blobSize); 419 surface->LineTo(centreX, ircWhole.bottom); 420 421 surface->PenColour(colourBody); 422 surface->MoveTo(centreX, ircWhole.top); 423 surface->LineTo(centreX, centreY - blobSize); 424 425 DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead); 426 DrawMinus(surface, centreX, centreY, blobSize, colourTail); 427 } 428 break; 429 430 case SC_MARK_DOTDOTDOT: { 431 XYPOSITION right = static_cast<XYPOSITION>(centreX - 6); 432 for (int b = 0; b < 3; b++) { 433 const PRectangle rcBlob(right, rc.bottom - 4, right + 2, rc.bottom - 2); 434 surface->FillRectangle(rcBlob, fore); 435 right += 5.0f; 436 } 437 } 438 break; 439 440 case SC_MARK_ARROWS: { 441 surface->PenColour(fore); 442 int right = centreX - 2; 443 const int armLength = dimOn2 - 1; 444 for (int b = 0; b < 3; b++) { 445 surface->MoveTo(right, centreY); 446 surface->LineTo(right - armLength, centreY - armLength); 447 surface->MoveTo(right, centreY); 448 surface->LineTo(right - armLength, centreY + armLength); 449 right += 4; 450 } 451 } 452 break; 453 454 case SC_MARK_SHORTARROW: { 455 Point pts[] = { 456 Point::FromInts(centreX, centreY + dimOn2), 457 Point::FromInts(centreX + dimOn2, centreY), 458 Point::FromInts(centreX, centreY - dimOn2), 459 Point::FromInts(centreX, centreY - dimOn4), 460 Point::FromInts(centreX - dimOn4, centreY - dimOn4), 461 Point::FromInts(centreX - dimOn4, centreY + dimOn4), 462 Point::FromInts(centreX, centreY + dimOn4), 463 Point::FromInts(centreX, centreY + dimOn2), 464 }; 465 surface->Polygon(pts, std::size(pts), fore, back); 466 } 467 break; 468 469 case SC_MARK_FULLRECT: 470 surface->FillRectangle(rcWhole, back); 471 break; 472 473 case SC_MARK_LEFTRECT: { 474 PRectangle rcLeft = rcWhole; 475 rcLeft.right = rcLeft.left + 4; 476 surface->FillRectangle(rcLeft, back); 477 } 478 break; 479 480 case SC_MARK_BOOKMARK: { 481 const int halfHeight = minDim / 3; 482 Point pts[] = { 483 Point::FromInts(ircWhole.left, centreY - halfHeight), 484 Point::FromInts(ircWhole.right - 3, centreY - halfHeight), 485 Point::FromInts(ircWhole.right - 3 - halfHeight, centreY), 486 Point::FromInts(ircWhole.right - 3, centreY + halfHeight), 487 Point::FromInts(ircWhole.left, centreY + halfHeight), 488 }; 489 surface->Polygon(pts, std::size(pts), fore, back); 490 } 491 break; 492 493 case SC_MARK_VERTICALBOOKMARK: { 494 const int halfWidth = minDim / 3; 495 Point pts[] = { 496 Point::FromInts(centreX - halfWidth, centreY - dimOn2), 497 Point::FromInts(centreX + halfWidth, centreY - dimOn2), 498 Point::FromInts(centreX + halfWidth, centreY + dimOn2), 499 Point::FromInts(centreX, centreY + dimOn2 - halfWidth), 500 Point::FromInts(centreX - halfWidth, centreY + dimOn2), 501 }; 502 surface->Polygon(pts, std::size(pts), fore, back); 503 } 504 break; 505 506 default: 507 if (markType >= SC_MARK_CHARACTER) { 508 std::string character(1, static_cast<char>(markType - SC_MARK_CHARACTER)); 509 const XYPOSITION width = surface->WidthText(fontForCharacter, character); 510 PRectangle rcText = rc; 511 rcText.left += (rc.Width() - width) / 2; 512 rcText.right = rc.left + width; 513 surface->DrawTextClipped(rcText, fontForCharacter, rcText.bottom - 2, 514 character, fore, back); 515 } else { 516 // treat as SC_MARK_FULLRECT 517 surface->FillRectangle(rcWhole, back); 518 } 519 break; 520 } 521 } 522