1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 module dfl.drawing; 5 6 import core.sys.windows.windows; 7 import core.sys.windows.com; 8 import core.sys.windows.ocidl; 9 import core.sys.windows.objfwd; 10 import core.sys.windows.objidl; // IStream 11 import core.sys.windows.olectl; // OleLoadPicture 12 import core.sys.windows.uuid; // IID_IPicture 13 14 import dfl.internal.dlib; 15 import dfl.internal.d2; 16 17 //import dfl.internal.winapi; 18 import dfl.exception; // new class! 19 import dfl.internal.utf; 20 import dfl.internal.com; 21 22 //import dfl.internal.wincom; 23 24 version (D_Version2) { 25 version = DFL_D2; 26 version = DFL_D2_AND_ABOVE; 27 } else version (D_Version3) { 28 version = DFL_D3; 29 version = DFL_D3_AND_ABOVE; 30 version = DFL_D2_AND_ABOVE; 31 } else version (D_Version4) { 32 version = DFL_D4; 33 version = DFL_D4_AND_ABOVE; 34 version = DFL_D3_AND_ABOVE; 35 version = DFL_D2_AND_ABOVE; 36 } else { 37 version = DFL_D1; 38 } 39 //version = DFL_D1_AND_ABOVE; 40 41 /// X and Y coordinate. 42 struct Point { 43 union { 44 struct { 45 LONG x; 46 LONG y; 47 } 48 49 POINT point; // package 50 } 51 52 /// Construct a new Point. 53 this(int x, int y) pure nothrow { 54 this.x = x; 55 this.y = y; 56 } 57 58 this(in POINT* pt) pure nothrow { 59 this.x = pt.x; 60 this.y = pt.y; 61 } 62 63 version (DFL_D2_AND_ABOVE) { 64 65 Dequ opEquals(ref ConstType!(Point) pt) const { 66 return x == pt.x && y == pt.y; 67 } 68 69 Dequ opEquals(Point pt) const { 70 return x == pt.x && y == pt.y; 71 } 72 } else { 73 74 Dequ opEquals(Point pt) { 75 return x == pt.x && y == pt.y; 76 } 77 } 78 79 Point opAdd(Size sz) const pure nothrow { 80 Point result; 81 result.x = x + sz.width; 82 result.y = y + sz.height; 83 return result; 84 } 85 86 Point opSub(Size sz) const pure nothrow { 87 Point result; 88 result.x = x - sz.width; 89 result.y = y - sz.height; 90 return result; 91 } 92 93 void opAddAssign(Size sz) pure nothrow { 94 x += sz.width; 95 y += sz.height; 96 } 97 98 void opSubAssign(Size sz) pure nothrow { 99 x -= sz.width; 100 y -= sz.height; 101 } 102 103 Point opNeg() const pure nothrow { 104 return Point(-x, -y); 105 } 106 } 107 108 /// Width and height. 109 struct Size { 110 int width; 111 int height; 112 113 SIZE size() const pure nothrow { 114 SIZE sz; 115 sz.cx = width; 116 sz.cy = height; 117 return sz; 118 } 119 120 void size(SIZE sz) pure nothrow { 121 width = sz.cx; 122 height = sz.cy; 123 } 124 125 /// Construct a new Size. 126 this(int width, int height) pure nothrow { 127 this.width = width; 128 this.height = height; 129 } 130 131 /// Construct a new Size. 132 this(in SIZE* size) pure nothrow { 133 this.width = size.cx; 134 this.height = size.cy; 135 } 136 137 version (DFL_D2_AND_ABOVE) { 138 139 Dequ opEquals(ref ConstType!(Size) sz) const { 140 return width == sz.width && height == sz.height; 141 } 142 143 Dequ opEquals(Size sz) const { 144 return width == sz.width && height == sz.height; 145 } 146 } else { 147 148 Dequ opEquals(Size sz) { 149 return width == sz.width && height == sz.height; 150 } 151 } 152 153 Size opAdd(Size sz) const pure nothrow { 154 Size result; 155 result.width = width + sz.width; 156 result.height = height + sz.height; 157 return result; 158 } 159 160 Size opSub(Size sz) const pure nothrow { 161 Size result; 162 result.width = width - sz.width; 163 result.height = height - sz.height; 164 return result; 165 } 166 167 void opAddAssign(Size sz) pure nothrow { 168 width += sz.width; 169 height += sz.height; 170 } 171 172 void opSubAssign(Size sz) pure nothrow { 173 width -= sz.width; 174 height -= sz.height; 175 } 176 } 177 178 /// X, Y, width and height rectangle dimensions. 179 struct Rect { 180 int x, y, width, height; 181 182 // Used internally. 183 void getRect(RECT* r) pure nothrow { // package 184 r.left = x; 185 r.right = x + width; 186 r.top = y; 187 r.bottom = y + height; 188 } 189 190 Point location() const pure nothrow @property { 191 return Point(x, y); 192 } 193 194 void location(Point pt) pure nothrow @property { 195 x = pt.x; 196 y = pt.y; 197 } 198 199 Size size() const pure nothrow @property { 200 return Size(width, height); 201 } 202 203 void size(Size sz) pure nothrow @property { 204 width = sz.width; 205 height = sz.height; 206 } 207 208 int right() const pure nothrow @property { 209 return x + width; 210 } 211 212 int bottom() const pure nothrow @property { 213 return y + height; 214 } 215 216 /// Construct a new Rect. 217 this(int x, int y, int width, int height) pure nothrow { 218 this.x = x; 219 this.y = y; 220 this.width = width; 221 this.height = height; 222 } 223 224 this(Point location, Size size) pure nothrow { 225 x = location.x; 226 y = location.y; 227 width = size.width; 228 height = size.height; 229 } 230 231 // Used internally. 232 this(in RECT* rect) pure nothrow { // package 233 x = rect.left; 234 y = rect.top; 235 width = rect.right - rect.left; 236 height = rect.bottom - rect.top; 237 } 238 239 /// Construct a new Rect from left, top, right and bottom values. 240 static Rect fromLTRB(int left, int top, int right, int bottom) pure nothrow { 241 Rect r; 242 r.x = left; 243 r.y = top; 244 r.width = right - left; 245 r.height = bottom - top; 246 return r; 247 } 248 249 version (DFL_D2_AND_ABOVE) { 250 251 Dequ opEquals(ref ConstType!(Rect) r) const { 252 return x == r.x && y == r.y && width == r.width && height == r.height; 253 } 254 255 Dequ opEquals(Rect r) const { 256 return x == r.x && y == r.y && width == r.width && height == r.height; 257 } 258 } else { 259 260 Dequ opEquals(Rect r) { 261 return x == r.x && y == r.y && width == r.width && height == r.height; 262 } 263 } 264 265 bool contains(int c_x, int c_y) const pure nothrow { 266 if (c_x >= x && c_y >= y) { 267 if (c_x <= right && c_y <= bottom) { 268 return true; 269 } 270 } 271 return false; 272 } 273 274 bool contains(Point pos) const pure nothrow { 275 return contains(pos.x, pos.y); 276 } 277 278 // Contained entirely within -this-. 279 bool contains(Rect r) const pure nothrow { 280 if (r.x >= x && r.y >= y) { 281 if (r.right <= right && r.bottom <= bottom) { 282 return true; 283 } 284 } 285 return false; 286 } 287 288 void inflate(int i_width, int i_height) pure nothrow { 289 x -= i_width; 290 width += i_width * 2; 291 y -= i_height; 292 height += i_height * 2; 293 } 294 295 void inflate(Size insz) pure nothrow { 296 inflate(insz.width, insz.height); 297 } 298 299 // Just tests if there's an intersection. 300 bool intersectsWith(Rect r) const pure nothrow { 301 if (r.right >= x && r.bottom >= y) { 302 if (r.y <= bottom && r.x <= right) { 303 return true; 304 } 305 } 306 return false; 307 } 308 309 void offset(int x, int y) pure nothrow { 310 this.x += x; 311 this.y += y; 312 } 313 314 void offset(Point pt) pure nothrow { 315 //return offset(pt.x, pt.y); 316 this.x += pt.x; 317 this.y += pt.y; 318 } 319 320 /+ 321 // Modify -this- to include only the intersection 322 // of -this- and -r-. 323 void intersect(Rect r) { 324 } 325 +/ 326 327 // void offset(Point), void offset(int, int) 328 // static Rect union(Rect, Rect) 329 } 330 331 unittest { 332 Rect r = Rect(3, 3, 3, 3); 333 334 assert(r.contains(3, 3)); 335 assert(!r.contains(3, 2)); 336 assert(r.contains(6, 6)); 337 assert(!r.contains(6, 7)); 338 assert(r.contains(r)); 339 assert(r.contains(Rect(4, 4, 2, 2))); 340 assert(!r.contains(Rect(2, 4, 4, 2))); 341 assert(!r.contains(Rect(4, 3, 2, 4))); 342 343 r.inflate(2, 1); 344 assert(r.x == 1); 345 assert(r.right == 8); 346 assert(r.y == 2); 347 assert(r.bottom == 7); 348 r.inflate(-2, -1); 349 assert(r == Rect(3, 3, 3, 3)); 350 351 assert(r.intersectsWith(Rect(4, 4, 2, 9))); 352 assert(r.intersectsWith(Rect(3, 3, 1, 1))); 353 assert(r.intersectsWith(Rect(0, 3, 3, 0))); 354 assert(r.intersectsWith(Rect(3, 2, 0, 1))); 355 assert(!r.intersectsWith(Rect(3, 1, 0, 1))); 356 assert(r.intersectsWith(Rect(5, 6, 1, 1))); 357 assert(!r.intersectsWith(Rect(7, 6, 1, 1))); 358 assert(!r.intersectsWith(Rect(6, 7, 1, 1))); 359 } 360 361 /// Color value representation 362 struct Color { 363 /// Red, green, blue and alpha channel color values. 364 @property ubyte r() nothrow { 365 validateColor(); 366 return color.red; 367 } 368 369 @property ubyte g() nothrow { 370 validateColor(); 371 return color.green; 372 } 373 374 @property ubyte b() nothrow { 375 validateColor(); 376 return color.blue; 377 } 378 379 @property ubyte a() nothrow { /+ validateColor(); +/ return color.alpha; 380 } 381 382 /// Return the numeric color value. 383 COLORREF toArgb() nothrow { 384 validateColor(); 385 return color.cref; 386 } 387 388 /// Return the numeric red, green and blue color value. 389 COLORREF toRgb() nothrow { 390 validateColor(); 391 return color.cref & 0x00FFFFFF; 392 } 393 394 // Used internally. 395 HBRUSH createBrush() nothrow { // package 396 HBRUSH hbr; 397 if (_systemColorIndex == Color.INVAILD_SYSTEM_COLOR_INDEX) { 398 hbr = CreateSolidBrush(toRgb()); 399 } else { 400 hbr = GetSysColorBrush(_systemColorIndex); 401 } 402 return hbr; 403 } 404 405 Color* Dthisptr(Color* t) pure nothrow { 406 return t; 407 } 408 409 Color* Dthisptr(ref Color t) pure nothrow { 410 return &t; 411 } 412 413 Color Dthisval(Color* t) pure nothrow { 414 return *t; 415 } 416 417 Color Dthisval(Color t) pure nothrow { 418 return t; 419 } 420 421 deprecated static Color opCall(COLORREF argb) { 422 Color nc; 423 nc.color.cref = argb; 424 return nc; 425 } 426 427 /// Construct a new color. 428 private this(_color c) pure nothrow { 429 color = c; 430 } 431 432 /// Construct a new color. 433 this(ubyte alpha, Color c) pure nothrow { 434 this = fromRgb(alpha, c.color.cref); 435 } 436 437 this(ubyte red, ubyte green, ubyte blue) pure nothrow { 438 this = fromArgb(0xff, red, green, blue); 439 } 440 441 this(ubyte alpha, ubyte red, ubyte green, ubyte blue) pure nothrow { 442 this = fromArgb(alpha, red, green, blue); 443 } 444 445 //alias opCall fromArgb; 446 static Color fromArgb(ubyte alpha, ubyte red, ubyte green, ubyte blue) pure nothrow { 447 return Color(_color((alpha << 24) | (blue << 16) | (green << 8) | red)); 448 } 449 450 static Color fromRgb(COLORREF rgb) pure nothrow { 451 if (CLR_NONE == rgb) { 452 return empty; 453 } 454 return Color(_color(cast(COLORREF)(rgb | 0xff000000))); 455 } 456 457 static Color fromRgb(ubyte alpha, COLORREF rgb) pure nothrow { 458 return Color(_color(rgb | ((cast(COLORREF) alpha) << 24))); 459 } 460 461 static @property Color empty() pure nothrow { 462 return Color(0, 0, 0, 0); 463 } 464 465 /// Return a completely transparent color value. 466 static @property Color transparent() nothrow { 467 return Color.fromArgb(0, 0xFF, 0xFF, 0xFF); 468 } 469 470 deprecated alias blend = blendColor; 471 472 /// Blend colors; alpha channels are ignored. 473 // Blends the color channels half way. 474 // Does not consider alpha channels and discards them. 475 // The new blended color is returned; -this- Color is not modified. 476 Color blendColor(Color wc) nothrow { 477 if (Dthisval(this) == Color.empty) { 478 return wc; 479 } 480 if (wc == Color.empty) { 481 return Dthisval(this); 482 } 483 484 validateColor(); 485 wc.validateColor(); 486 487 return Color(cast(ubyte)((cast(uint) color.red + cast(uint) wc.color.red) >> 1), 488 cast(ubyte)((cast(uint) color.green + cast(uint) wc.color.green) >> 1), 489 cast(ubyte)((cast(uint) color.blue + cast(uint) wc.color.blue) >> 1)); 490 } 491 492 /// Alpha blend this color with a background color to return a solid color (100% opaque). 493 // Blends with backColor if this color has opacity to produce a solid color. 494 // Returns the new solid color, or the original color if no opacity. 495 // If backColor has opacity, it is ignored. 496 // The new blended color is returned; -this- Color is not modified. 497 Color solidColor(Color backColor) nothrow { 498 //if(0x7F == this.color.alpha) 499 // return blendColor(backColor); 500 //if(Dthisval(this) == Color.empty) // Checked if(0 == this.color.alpha) 501 // return backColor; 502 if (0 == this.color.alpha) { 503 return backColor; 504 } 505 if (backColor == Color.empty) { 506 return Dthisval(this); 507 } 508 if (0xFF == this.color.alpha) { 509 return Dthisval(this); 510 } 511 512 validateColor(); 513 backColor.validateColor(); 514 515 float fa, ba; 516 fa = cast(float) color.alpha / 255.0; 517 ba = 1.0 - fa; 518 519 Color result; 520 result.color.alpha = 0xFF; 521 result.color.red = cast(ubyte)(this.color.red * fa + backColor.color.red * ba); 522 result.color.green = cast(ubyte)(this.color.green * fa + backColor.color.green * ba); 523 result.color.blue = cast(ubyte)(this.color.blue * fa + backColor.color.blue * ba); 524 return result; 525 } 526 527 package static Color systemColor(int colorIndex) pure nothrow { 528 Color c; 529 c.sysIndex = cast(ubyte) colorIndex; 530 c.color.alpha = 0xFF; 531 return c; 532 } 533 534 // Gets color index or INVAILD_SYSTEM_COLOR_INDEX. 535 package @property int _systemColorIndex() pure nothrow { 536 return sysIndex; 537 } 538 539 package enum ubyte INVAILD_SYSTEM_COLOR_INDEX = ubyte.max; 540 541 private: 542 union _color { 543 COLORREF cref; 544 struct { 545 align(1): 546 ubyte red; 547 ubyte green; 548 ubyte blue; 549 ubyte alpha; 550 } 551 } 552 553 static assert(_color.sizeof == uint.sizeof); 554 _color color; 555 556 ubyte sysIndex = INVAILD_SYSTEM_COLOR_INDEX; 557 558 void validateColor() nothrow { 559 if (sysIndex != INVAILD_SYSTEM_COLOR_INDEX) { 560 color.cref = GetSysColor(sysIndex); 561 //color.alpha = 0xFF; // Should already be set. 562 } 563 } 564 } 565 566 unittest { 567 enum red = Color.fromArgb(0xff, 0xff, 0x00, 0x00); 568 } 569 570 class SystemColors { 571 private this() { 572 } 573 574 static: 575 576 @property Color activeBorder() { 577 return Color.systemColor(COLOR_ACTIVEBORDER); 578 } 579 580 @property Color activeCaption() { 581 return Color.systemColor(COLOR_ACTIVECAPTION); 582 } 583 584 @property Color activeCaptionText() { 585 return Color.systemColor(COLOR_CAPTIONTEXT); 586 } 587 588 @property Color appWorkspace() { 589 return Color.systemColor(COLOR_APPWORKSPACE); 590 } 591 592 @property Color control() { 593 return Color.systemColor(COLOR_BTNFACE); 594 } 595 596 @property Color controlDark() { 597 return Color.systemColor(COLOR_BTNSHADOW); 598 } 599 600 @property Color controlDarkDark() { 601 return Color.systemColor(COLOR_3DDKSHADOW); // ? 602 } 603 604 @property Color controlLight() { 605 return Color.systemColor(COLOR_3DLIGHT); 606 } 607 608 @property Color controlLightLight() { 609 return Color.systemColor(COLOR_BTNHIGHLIGHT); // ? 610 } 611 612 @property Color controlText() { 613 return Color.systemColor(COLOR_BTNTEXT); 614 } 615 616 @property Color desktop() { 617 return Color.systemColor(COLOR_DESKTOP); 618 } 619 620 @property Color grayText() { 621 return Color.systemColor(COLOR_GRAYTEXT); 622 } 623 624 @property Color highlight() { 625 return Color.systemColor(COLOR_HIGHLIGHT); 626 } 627 628 @property Color highlightText() { 629 return Color.systemColor(COLOR_HIGHLIGHTTEXT); 630 } 631 632 @property Color hotTrack() { 633 return Color(0, 0, 0xFF); // ? 634 } 635 636 @property Color inactiveBorder() { 637 return Color.systemColor(COLOR_INACTIVEBORDER); 638 } 639 640 @property Color inactiveCaption() { 641 return Color.systemColor(COLOR_INACTIVECAPTION); 642 } 643 644 @property Color inactiveCaptionText() { 645 return Color.systemColor(COLOR_INACTIVECAPTIONTEXT); 646 } 647 648 @property Color info() { 649 return Color.systemColor(COLOR_INFOBK); 650 } 651 652 @property Color infoText() { 653 return Color.systemColor(COLOR_INFOTEXT); 654 } 655 656 @property Color menu() { 657 return Color.systemColor(COLOR_MENU); 658 } 659 660 @property Color menuText() { 661 return Color.systemColor(COLOR_MENUTEXT); 662 } 663 664 @property Color scrollBar() { 665 return Color.systemColor(CTLCOLOR_SCROLLBAR); 666 } 667 668 @property Color window() { 669 return Color.systemColor(COLOR_WINDOW); 670 } 671 672 @property Color windowFrame() { 673 return Color.systemColor(COLOR_WINDOWFRAME); 674 } 675 676 @property Color windowText() { 677 return Color.systemColor(COLOR_WINDOWTEXT); 678 } 679 } 680 681 class SystemIcons { 682 private this() { 683 } 684 685 static: 686 687 @property Icon application() { 688 return new Icon(LoadIcon(null, IDI_APPLICATION), false); 689 } 690 691 @property Icon error() { 692 return new Icon(LoadIcon(null, IDI_HAND), false); 693 } 694 695 @property Icon question() { 696 return new Icon(LoadIcon(null, IDI_QUESTION), false); 697 } 698 699 @property Icon warning() { 700 return new Icon(LoadIcon(null, IDI_EXCLAMATION), false); 701 } 702 703 @property Icon information() { 704 return new Icon(LoadIcon(null, IDI_INFORMATION), false); 705 } 706 } 707 708 /+ 709 class ImageFormat { 710 /+ 711 this(guid) { 712 713 } 714 715 716 final @property guid() { 717 return guid; 718 } 719 +/ 720 721 722 static: 723 724 @property ImageFormat bmp() { 725 return null; 726 } 727 728 729 @property ImageFormat icon() { 730 return null; 731 } 732 } 733 +/ 734 735 abstract class Image { 736 //flags(); // getter ??? 737 738 /+ 739 final @property ImageFormat rawFormat(); 740 +/ 741 742 static Bitmap fromHBitmap(HBITMAP hbm) { // package 743 return new Bitmap(hbm, false); // Not owned. Up to caller to manage or call dispose(). 744 } 745 746 /+ 747 static Image fromFile(Dstring file) { 748 return new Image(LoadImageA()); 749 } 750 +/ 751 752 void draw(Graphics g, Point pt); 753 754 void drawStretched(Graphics g, Rect r); 755 756 @property Size size(); 757 758 @property int width() { 759 return size.width; 760 } 761 762 @property int height() { 763 return size.height; 764 } 765 766 int _imgtype(HGDIOBJ* ph) { // internal 767 if (ph) { 768 *ph = HGDIOBJ.init; 769 } 770 return 0; // 1 = bitmap; 2 = icon. 771 } 772 } 773 774 class Bitmap : Image { 775 776 // Load from a bmp file. 777 this(Dstring fileName) { 778 this.hbm = cast(HBITMAP) loadImage(null, fileName, 779 IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); 780 if (!this.hbm) { 781 throw new DflException("Unable to load bitmap from file '" ~ fileName ~ "'"); 782 } 783 } 784 785 // Used internally. 786 this(HBITMAP hbm, bool owned = true) { 787 this.hbm = hbm; 788 this.owned = owned; 789 } 790 791 final @property HBITMAP handle() { 792 return hbm; 793 } 794 795 private void _getInfo(BITMAP* bm) { 796 if (GetObjectA(hbm, BITMAP.sizeof, bm) != BITMAP.sizeof) { 797 throw new DflException("Unable to get image information"); 798 } 799 } 800 801 final override @property Size size() { 802 BITMAP bm; 803 _getInfo(&bm); 804 return Size(bm.bmWidth, bm.bmHeight); 805 } 806 807 final override @property int width() { 808 return size.width; 809 } 810 811 final override @property int height() { 812 return size.height; 813 } 814 815 private void _draw(Graphics g, Point pt, HDC memdc) { 816 HGDIOBJ hgo; 817 Size sz; 818 819 sz = size; 820 hgo = SelectObject(memdc, hbm); 821 BitBlt(g.handle, pt.x, pt.y, sz.width, sz.height, memdc, 0, 0, SRCCOPY); 822 SelectObject(memdc, hgo); // Old bitmap. 823 } 824 825 final override void draw(Graphics g, Point pt) { 826 HDC memdc; 827 memdc = CreateCompatibleDC(g.handle); 828 try { 829 _draw(g, pt, memdc); 830 } 831 finally { 832 DeleteDC(memdc); 833 } 834 } 835 836 // -tempMemGraphics- is used as a temporary Graphics instead of 837 // creating and destroying a temporary one for each call. 838 final void draw(Graphics g, Point pt, Graphics tempMemGraphics) { 839 _draw(g, pt, tempMemGraphics.handle); 840 } 841 842 private void _drawStretched(Graphics g, Rect r, HDC memdc) { 843 HGDIOBJ hgo; 844 Size sz; 845 int lstretch; 846 847 sz = size; 848 hgo = SelectObject(memdc, hbm); 849 lstretch = SetStretchBltMode(g.handle, COLORONCOLOR); 850 StretchBlt(g.handle, r.x, r.y, r.width, r.height, memdc, 0, 0, sz.width, sz.height, 851 SRCCOPY); 852 SetStretchBltMode(g.handle, lstretch); 853 SelectObject(memdc, hgo); // Old bitmap. 854 } 855 856 final override void drawStretched(Graphics g, Rect r) { 857 HDC memdc; 858 memdc = CreateCompatibleDC(g.handle); 859 try { 860 _drawStretched(g, r, memdc); 861 } 862 finally { 863 DeleteDC(memdc); 864 } 865 } 866 867 // -tempMemGraphics- is used as a temporary Graphics instead of 868 // creating and destroying a temporary one for each call. 869 final void drawStretched(Graphics g, Rect r, Graphics tempMemGraphics) { 870 _drawStretched(g, r, tempMemGraphics.handle); 871 } 872 873 void dispose() { 874 assert(owned); 875 DeleteObject(hbm); 876 hbm = null; 877 } 878 879 ~this() { 880 if (owned) { 881 dispose(); 882 } 883 } 884 885 override int _imgtype(HGDIOBJ* ph) { // internal 886 if (ph) { 887 *ph = cast(HGDIOBJ) hbm; 888 } 889 return 1; 890 } 891 892 private: 893 HBITMAP hbm; 894 bool owned = true; 895 } 896 897 final class EnhancedMetaFile : Image { 898 private: 899 HENHMETAFILE hemf; 900 ENHMETAHEADER emfh; 901 HDC hdcref; 902 bool owned; 903 public: 904 // Used internally. 905 this(HENHMETAFILE hemf, HDC hdcref = null, bool owned = true) { 906 this.hemf = hemf; 907 GetEnhMetaFileHeader(hemf, ENHMETAHEADER.sizeof, &emfh); 908 assert(hdcref || owned); 909 if (!hdcref) { 910 this.hdcref = GetDC(null); 911 this.owned = true; 912 } 913 } 914 915 /// Load from a emf file. 916 this(string fileName, HDC hdcref = null) { 917 import std.utf; 918 919 auto tmp = GetEnhMetaFileW(fileName.toUTF16z()); 920 if (!tmp) { 921 throw new DflException("Unable to load EnhanceMetaFile from file '" ~ fileName ~ "'"); 922 } 923 this(tmp, hdcref); 924 } 925 926 void dispose() { 927 DeleteEnhMetaFile(hemf); 928 hemf = null; 929 if (owned) { 930 ReleaseDC(null, hdcref); 931 } 932 } 933 934 ~this() { 935 dispose(); 936 } 937 938 final HENHMETAFILE handle() @property { 939 return hemf; 940 } 941 942 Rect bounds() const nothrow @property { 943 with (emfh) { 944 auto rc = RECT(MulDiv(rclBounds.left * 1000, 945 szlDevice.cx * GetDeviceCaps(cast(HDC) hdcref, HORZSIZE), 946 szlMicrometers.cx * GetDeviceCaps(cast(HDC) hdcref, HORZRES)), 947 MulDiv(rclBounds.top * 1000, 948 szlDevice.cy * GetDeviceCaps(cast(HDC) hdcref, VERTSIZE), 949 szlMicrometers.cy * GetDeviceCaps(cast(HDC) hdcref, VERTRES)), 950 MulDiv(rclBounds.right * 1000, 951 szlDevice.cx * GetDeviceCaps(cast(HDC) hdcref, HORZSIZE), 952 szlMicrometers.cx * GetDeviceCaps(cast(HDC) hdcref, HORZRES)), 953 MulDiv(rclBounds.bottom * 1000, 954 szlDevice.cy * GetDeviceCaps(cast(HDC) hdcref, VERTSIZE), 955 szlMicrometers.cy * GetDeviceCaps(cast(HDC) hdcref, VERTRES))); 956 return Rect(&rc); 957 } 958 } 959 960 override int width() const nothrow @property { 961 with (emfh) 962 return MulDiv(rclFrame.right - rclFrame.left, szlDevice.cx * 10, szlMicrometers.cx); 963 } 964 965 override int height() const nothrow @property { 966 with (emfh) 967 return MulDiv(rclFrame.bottom - rclFrame.top, szlDevice.cy * 10, szlMicrometers.cy); 968 } 969 970 override Size size() const nothrow @property { 971 return Size(width, height); 972 } 973 974 Rect frameRectangle() const nothrow @property { 975 with (emfh) { 976 return Rect(MulDiv(rclFrame.left, szlDevice.cx * 10, 977 szlMicrometers.cx), MulDiv(rclFrame.top, szlDevice.cy * 10, 978 szlMicrometers.cy), width, height); 979 } 980 } 981 982 override void draw(Graphics g, Point pt) { 983 auto sz = size; 984 RECT rc; 985 Rect(pt.x, pt.y, sz.width, sz.height).getRect(&rc); 986 PlayEnhMetaFile(g.handle, hemf, &rc); 987 } 988 989 override void drawStretched(Graphics g, Rect r) { 990 RECT rc; 991 r.getRect(&rc); 992 PlayEnhMetaFile(g.handle, hemf, &rc); 993 } 994 } 995 996 class Picture : Image { 997 // Note: requires OleInitialize(null). 998 999 // Throws exception on failure. 1000 this(DStream stm) { 1001 this.ipic = _fromDStream(stm); 1002 if (!this.ipic) { 1003 throw new DflException("Unable to load picture from stream"); 1004 } 1005 } 1006 1007 // Throws exception on failure. 1008 this(Dstring fileName) { 1009 this.ipic = _fromFileName(fileName); 1010 if (!this.ipic) { 1011 throw new DflException("Unable to load picture from file '" ~ fileName ~ "'"); 1012 } 1013 } 1014 1015 this(void[] mem) { 1016 this.ipic = _fromMemory(mem); 1017 if (!this.ipic) { 1018 throw new DflException("Unable to load picture from memory"); 1019 } 1020 } 1021 1022 private this(IPicture ipic) { 1023 this.ipic = ipic; 1024 } 1025 1026 // Returns null on failure instead of throwing exception. 1027 static Picture fromStream(DStream stm) { 1028 auto ipic = _fromDStream(stm); 1029 if (!ipic) { 1030 return null; 1031 } 1032 return new Picture(ipic); 1033 } 1034 1035 // Returns null on failure instead of throwing exception. 1036 static Picture fromFile(Dstring fileName) { 1037 auto ipic = _fromFileName(fileName); 1038 if (!ipic) { 1039 return null; 1040 } 1041 return new Picture(ipic); 1042 } 1043 1044 static Picture fromMemory(void[] mem) { 1045 auto ipic = _fromMemory(mem); 1046 if (!ipic) { 1047 return null; 1048 } 1049 return new Picture(ipic); 1050 } 1051 1052 final void draw(HDC hdc, Point pt) { // package 1053 int lhx, lhy; 1054 int width, height; 1055 lhx = loghimX; 1056 lhy = loghimY; 1057 width = MAP_LOGHIM_TO_PIX(lhx, GetDeviceCaps(hdc, LOGPIXELSX)); 1058 height = MAP_LOGHIM_TO_PIX(lhy, GetDeviceCaps(hdc, LOGPIXELSY)); 1059 ipic.Render(hdc, pt.x, pt.y + height, width, -height, 0, 0, lhx, lhy, null); 1060 } 1061 1062 final override void draw(Graphics g, Point pt) { 1063 return draw(g.handle, pt); 1064 } 1065 1066 final void drawStretched(HDC hdc, Rect r) { // package 1067 int lhx, lhy; 1068 lhx = loghimX; 1069 lhy = loghimY; 1070 ipic.Render(hdc, r.x, r.y + r.height, r.width, -r.height, 0, 0, lhx, lhy, null); 1071 } 1072 1073 final override void drawStretched(Graphics g, Rect r) { 1074 return drawStretched(g.handle, r); 1075 } 1076 1077 final @property OLE_XSIZE_HIMETRIC loghimX() { 1078 OLE_XSIZE_HIMETRIC xsz; 1079 if (S_OK != ipic.get_Width(&xsz)) { 1080 return 0; // ? 1081 } 1082 return xsz; 1083 } 1084 1085 final @property OLE_YSIZE_HIMETRIC loghimY() { 1086 OLE_YSIZE_HIMETRIC ysz; 1087 if (S_OK != ipic.get_Height(&ysz)) { 1088 return 0; // ? 1089 } 1090 return ysz; 1091 } 1092 1093 final override @property int width() { 1094 Graphics g; 1095 int result; 1096 g = Graphics.getScreen(); 1097 result = getWidth(g); 1098 g.dispose(); 1099 return result; 1100 } 1101 1102 final override @property int height() { 1103 Graphics g; 1104 int result; 1105 g = Graphics.getScreen(); 1106 result = getHeight(g); 1107 g.dispose(); 1108 return result; 1109 } 1110 1111 final override @property Size size() { 1112 Graphics g; 1113 Size result; 1114 g = Graphics.getScreen(); 1115 result = getSize(g); 1116 g.dispose(); 1117 return result; 1118 } 1119 1120 final int getWidth(HDC hdc) { // package 1121 return MAP_LOGHIM_TO_PIX(loghimX, GetDeviceCaps(hdc, LOGPIXELSX)); 1122 } 1123 1124 final int getWidth(Graphics g) { 1125 return getWidth(g.handle); 1126 } 1127 1128 final int getHeight(HDC hdc) { // package 1129 return MAP_LOGHIM_TO_PIX(loghimY, GetDeviceCaps(hdc, LOGPIXELSX)); 1130 } 1131 1132 final int getHeight(Graphics g) { 1133 return getHeight(g.handle); 1134 } 1135 1136 final Size getSize(HDC hdc) { // package 1137 return Size(getWidth(hdc), getHeight(hdc)); 1138 } 1139 1140 final Size getSize(Graphics g) { 1141 return Size(getWidth(g), getHeight(g)); 1142 } 1143 1144 void dispose() { 1145 if (HBITMAP.init != _hbmimgtype) { 1146 DeleteObject(_hbmimgtype); 1147 _hbmimgtype = HBITMAP.init; 1148 } 1149 1150 if (ipic) { 1151 ipic.Release(); 1152 ipic = null; 1153 } 1154 } 1155 1156 ~this() { 1157 dispose(); 1158 } 1159 1160 final HBITMAP toHBitmap(HDC hdc) { // package 1161 HDC memdc; 1162 HBITMAP result; 1163 HGDIOBJ oldbm; 1164 memdc = CreateCompatibleDC(hdc); 1165 if (!memdc) { 1166 throw new DflException("Device error"); 1167 } 1168 try { 1169 Size sz; 1170 sz = getSize(hdc); 1171 result = CreateCompatibleBitmap(hdc, sz.width, sz.height); 1172 if (!result) { 1173 bad_bm: 1174 throw new DflException("Unable to allocate image"); 1175 } 1176 oldbm = SelectObject(memdc, result); 1177 draw(memdc, Point(0, 0)); 1178 } 1179 finally { 1180 if (oldbm) { 1181 SelectObject(memdc, oldbm); 1182 } 1183 DeleteDC(memdc); 1184 } 1185 return result; 1186 } 1187 1188 final Bitmap toBitmap(HDC hdc) { // package 1189 HBITMAP hbm; 1190 hbm = toHBitmap(hdc); 1191 if (!hbm) { 1192 throw new DflException("Unable to create bitmap"); 1193 } 1194 return new Bitmap(hbm, true); // Owned. 1195 } 1196 1197 final Bitmap toBitmap() { 1198 Bitmap result; 1199 scope Graphics g = Graphics.getScreen(); 1200 result = toBitmap(g); 1201 //g.dispose(); // scope'd 1202 return result; 1203 } 1204 1205 final Bitmap toBitmap(Graphics g) { 1206 return toBitmap(g.handle); 1207 } 1208 1209 HBITMAP _hbmimgtype; 1210 1211 override int _imgtype(HGDIOBJ* ph) { // internal 1212 if (ph) { 1213 if (HBITMAP.init == _hbmimgtype) { 1214 scope Graphics g = Graphics.getScreen(); 1215 _hbmimgtype = toHBitmap(g.handle); 1216 //g.dispose(); // scope'd 1217 } 1218 1219 *ph = _hbmimgtype; 1220 } 1221 return 1; 1222 } 1223 1224 private IPicture ipic = null; 1225 1226 static IPicture _fromIStream(IStream istm) { 1227 IPicture ipic; 1228 // IID_IPicture from uiid.d. 1229 // if I use directy: 1230 // REFIID refiid = &IID_IPicture; 1231 // compiler complains 1232 // is not a lvalue 1233 const(GUID) X = { 1234 0x7BF80980, 0xBF32, 0x101A, [0x8B, 0xBB, 0x00, 0xAA, 0x00, 0x30, 0x0C, 0xAB]}; 1235 REFIID refiid = &X; 1236 switch (OleLoadPicture(istm, 0, FALSE, refiid, cast(void**)&ipic)) { 1237 case S_OK: 1238 return ipic; 1239 1240 debug (DFL_X) { 1241 case E_OUTOFMEMORY: 1242 debug assert(0, "Picture: Out of memory"); 1243 break; 1244 case E_NOINTERFACE: 1245 debug assert(0, "Picture: The object does not support the interface"); 1246 break; 1247 case E_UNEXPECTED: 1248 debug assert(0, "Picture: Unexpected error"); 1249 break; 1250 case E_POINTER: 1251 debug assert(0, "Picture: Invalid pointer"); 1252 break; 1253 case E_FAIL: 1254 debug assert(0, "Picture: Fail"); 1255 break; 1256 } 1257 1258 default: 1259 } 1260 return null; 1261 } 1262 1263 static IPicture _fromDStream(DStream stm) { 1264 assert(stm !is null); 1265 scope DStreamToIStream istm = new DStreamToIStream(stm); 1266 return _fromIStream(istm); 1267 } 1268 1269 static IPicture _fromFileName(Dstring fileName) { 1270 // FIX: alias dfl.internal.winapi.HANDLE HANDLE; // Otherwise, odd conflict with wine. 1271 1272 HANDLE hf; 1273 HANDLE hg; 1274 void* pg; 1275 DWORD dwsz, dw; 1276 1277 hf = dfl.internal.utf.createFile(fileName, GENERIC_READ, 1278 FILE_SHARE_READ, null, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, null); 1279 if (!hf) { 1280 return null; 1281 } 1282 1283 dwsz = GetFileSize(hf, null); 1284 if (0xFFFFFFFF == dwsz) { 1285 failclose: 1286 CloseHandle(hf); 1287 return null; 1288 } 1289 1290 hg = GlobalAlloc(GMEM_MOVEABLE, dwsz); 1291 if (!hg) { 1292 goto failclose; 1293 } 1294 1295 pg = GlobalLock(hg); 1296 if (!pg) { 1297 CloseHandle(hf); 1298 CloseHandle(hg); 1299 return null; 1300 } 1301 1302 if (!ReadFile(hf, pg, dwsz, &dw, null) || dwsz != dw) { 1303 CloseHandle(hf); 1304 GlobalUnlock(hg); 1305 CloseHandle(hg); 1306 return null; 1307 } 1308 1309 CloseHandle(hf); 1310 GlobalUnlock(hg); 1311 1312 IStream istm; 1313 IPicture ipic; 1314 1315 if (S_OK != CreateStreamOnHGlobal(hg, TRUE, &istm)) { 1316 CloseHandle(hg); 1317 return null; 1318 } 1319 // Don't need to CloseHandle(hg) due to 2nd param being TRUE. 1320 1321 ipic = _fromIStream(istm); 1322 istm.Release(); 1323 return ipic; 1324 } 1325 1326 static IPicture _fromMemory(void[] mem) { 1327 return _fromIStream(new MemoryIStream(mem)); 1328 } 1329 1330 } 1331 1332 enum TextTrimming : UINT { 1333 NONE = 0, 1334 ELLIPSIS = DT_END_ELLIPSIS, 1335 ELLIPSIS_PATH = DT_PATH_ELLIPSIS, 1336 } 1337 1338 enum TextFormatFlags : UINT { 1339 NO_PREFIX = DT_NOPREFIX, 1340 DIRECTION_RIGHT_TO_LEFT = DT_RTLREADING, 1341 WORD_BREAK = DT_WORDBREAK, 1342 SINGLE_LINE = DT_SINGLELINE, 1343 NO_CLIP = DT_NOCLIP, 1344 LINE_LIMIT = DT_EDITCONTROL, 1345 } 1346 1347 enum TextAlignment : UINT { 1348 LEFT = DT_LEFT, /// 1349 RIGHT = DT_RIGHT, 1350 CENTER = DT_CENTER, 1351 1352 TOP = DT_TOP, /// Single line only alignment. 1353 BOTTOM = DT_BOTTOM, 1354 MIDDLE = DT_VCENTER, 1355 } 1356 1357 class TextFormat { 1358 1359 this() { 1360 } 1361 1362 this(TextFormat tf) { 1363 _trim = tf._trim; 1364 _flags = tf._flags; 1365 _align = tf._align; 1366 _params = tf._params; 1367 } 1368 1369 this(TextFormatFlags flags) { 1370 _flags = flags; 1371 } 1372 1373 static @property TextFormat genericDefault() { 1374 TextFormat result; 1375 result = new TextFormat; 1376 result._trim = TextTrimming.NONE; 1377 result._flags = TextFormatFlags.NO_PREFIX | TextFormatFlags.WORD_BREAK 1378 | TextFormatFlags.NO_CLIP | TextFormatFlags.LINE_LIMIT; 1379 return result; 1380 } 1381 1382 static @property TextFormat genericTypographic() { 1383 return new TextFormat; 1384 } 1385 1386 final @property void alignment(TextAlignment ta) { 1387 _align = ta; 1388 } 1389 1390 final @property TextAlignment alignment() { 1391 return _align; 1392 } 1393 1394 final @property void formatFlags(TextFormatFlags tff) { 1395 _flags = tff; 1396 } 1397 1398 final @property TextFormatFlags formatFlags() { 1399 return _flags; 1400 } 1401 1402 final @property void trimming(TextTrimming tt) { 1403 _trim = tt; 1404 } 1405 1406 final @property TextTrimming trimming() { 1407 return _trim; 1408 } 1409 1410 // Units of the average character width. 1411 1412 final @property void tabLength(int tablen) { 1413 _params.iTabLength = tablen; 1414 } 1415 1416 final @property int tabLength() { 1417 return _params.iTabLength; 1418 } 1419 1420 // Units of the average character width. 1421 1422 final @property void leftMargin(int sz) { 1423 _params.iLeftMargin = sz; 1424 } 1425 1426 final @property int leftMargin() { 1427 return _params.iLeftMargin; 1428 } 1429 1430 // Units of the average character width. 1431 1432 final @property void rightMargin(int sz) { 1433 _params.iRightMargin = sz; 1434 } 1435 1436 final @property int rightMargin() { 1437 return _params.iRightMargin; 1438 } 1439 1440 private: 1441 TextTrimming _trim = TextTrimming.NONE; // TextTrimming.CHARACTER. 1442 TextFormatFlags _flags = TextFormatFlags.NO_PREFIX | TextFormatFlags.WORD_BREAK; 1443 TextAlignment _align = TextAlignment.LEFT; 1444 package DRAWTEXTPARAMS _params = {DRAWTEXTPARAMS.sizeof, 8, 0, 0}; 1445 } 1446 1447 class Screen { 1448 1449 static @property Screen primaryScreen() { 1450 version (DFL_MULTIPLE_SCREENS) { 1451 _getScreens(); 1452 if (_screens.length > 0) { 1453 if (_screens.length == 1) { 1454 return _screens[0]; 1455 } 1456 MONITORINFO mi; 1457 for (int i = 0; i < _screens.length; i++) { 1458 _screens[i]._getInfo(mi); 1459 if (mi.dwFlags & MONITORINFOF_PRIMARY) { 1460 return _screens[i]; 1461 } 1462 } 1463 } 1464 } 1465 if (!_ps) { 1466 _setPs(); 1467 } 1468 return _ps; 1469 } 1470 1471 @property Rect bounds() { 1472 version (DFL_MULTIPLE_SCREENS) { 1473 if (HMONITOR.init != hmonitor) { 1474 MONITORINFO mi; 1475 _getInfo(mi); 1476 return Rect(&mi.rcMonitor); 1477 } 1478 } 1479 RECT area; 1480 if (!GetWindowRect(GetDesktopWindow(), &area)) { 1481 assert(0); 1482 } 1483 return Rect(&area); 1484 } 1485 1486 @property Rect workingArea() { 1487 version (DFL_MULTIPLE_SCREENS) { 1488 if (HMONITOR.init != hmonitor) { 1489 MONITORINFO mi; 1490 _getInfo(mi); 1491 return Rect(&mi.rcWork); 1492 } 1493 } 1494 RECT area; 1495 if (!SystemParametersInfoA(SPI_GETWORKAREA, 0, &area, FALSE)) { 1496 return bounds; 1497 } 1498 return Rect(&area); 1499 } 1500 1501 version (DFL_MULTIPLE_SCREENS) { 1502 1503 debug { 1504 static @property void fakeMultipleScreens(bool byes) { 1505 if (byes) { 1506 allScreens(); // Force populating. 1507 if (_screens.length < 2) { 1508 _screens ~= new Screen(HMFAKE); 1509 } 1510 } 1511 } 1512 1513 static @property bool fakeMultipleScreens() { 1514 return _screens.length > 1 && HMFAKE == _screens[1].hmonitor; 1515 } 1516 1517 private enum HMONITOR HMFAKE = cast(HMONITOR) 1969253357; 1518 } 1519 1520 static @property Screen[] allScreens() { 1521 version (DFL_MULTIPLE_SCREENS) { 1522 _getScreens(); 1523 if (_screens.length > 0) { 1524 return _screens; 1525 } 1526 } 1527 if (_screens.length < 1) { 1528 synchronized { 1529 _screens = new Screen[1]; 1530 if (!_ps) { 1531 _setPs(); 1532 } 1533 _screens[0] = _ps; 1534 } 1535 } 1536 return _screens; 1537 } 1538 1539 static Screen fromHandle(HWND hwnd) { 1540 version (DFL_MULTIPLE_SCREENS) { 1541 version (SUPPORTS_MULTIPLE_SCREENS) { 1542 alias fromWindow = MonitorFromWindow; 1543 } else { 1544 auto fromWindow = cast(typeof(&MonitorFromWindow)) GetProcAddress( 1545 GetModuleHandleA("user32.dll"), "MonitorFromWindow"); 1546 if (!fromWindow) { 1547 //throw new DflException("Multiple screens not supported"); 1548 goto _def; 1549 } 1550 } 1551 HMONITOR hm = fromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); 1552 debug { 1553 if (fakeMultipleScreens && hm == _screens[0].hmonitor) { 1554 RECT rect; 1555 if (GetWindowRect(hwnd, &rect)) { 1556 Rect r = Rect(&rect); 1557 if (_withinFakeScreen(r)) { 1558 return _screens[1]; 1559 } 1560 } 1561 } 1562 } 1563 return _findScreen(hm); 1564 } 1565 _def: 1566 return primaryScreen; 1567 } 1568 1569 static Screen fromControl(IWindow ctrl) { 1570 return fromHandle(ctrl.handle); 1571 } 1572 1573 static Screen fromPoint(Point pt) { 1574 version (DFL_MULTIPLE_SCREENS) { 1575 version (SUPPORTS_MULTIPLE_SCREENS) { 1576 alias fromPoint = MonitorFromPoint; 1577 } else { 1578 auto fromPoint = cast(typeof(&MonitorFromPoint)) GetProcAddress( 1579 GetModuleHandleA("user32.dll"), "MonitorFromPoint"); 1580 if (!fromPoint) { 1581 //throw new DflException("Multiple screens not supported"); 1582 goto _def; 1583 } 1584 } 1585 HMONITOR hm = fromPoint(pt.point, MONITOR_DEFAULTTOPRIMARY); 1586 debug { 1587 if (fakeMultipleScreens && hm == _screens[0].hmonitor) { 1588 Rect r = Rect(pt, Size(0, 0)); 1589 if (_withinFakeScreen(r)) { 1590 return _screens[1]; 1591 } 1592 } 1593 } 1594 return _findScreen(hm); 1595 } 1596 _def: 1597 return primaryScreen; 1598 } 1599 1600 static Screen fromRectangle(Rect r) { 1601 version (DFL_MULTIPLE_SCREENS) { 1602 version (SUPPORTS_MULTIPLE_SCREENS) { 1603 alias fromRect = MonitorFromRect; 1604 } else { 1605 auto fromRect = cast(typeof(&MonitorFromRect)) GetProcAddress( 1606 GetModuleHandleA("user32.dll"), "MonitorFromRect"); 1607 if (!fromRect) { 1608 //throw new DflException("Multiple screens not supported"); 1609 goto _def; 1610 } 1611 } 1612 RECT rect; 1613 r.getRect(&rect); 1614 HMONITOR hm = fromRect(&rect, MONITOR_DEFAULTTOPRIMARY); 1615 debug { 1616 if (fakeMultipleScreens && hm == _screens[0].hmonitor) { 1617 if (_withinFakeScreen(r)) { 1618 return _screens[1]; 1619 } 1620 } 1621 } 1622 return _findScreen(hm); 1623 } 1624 _def: 1625 return primaryScreen; 1626 } 1627 1628 } 1629 1630 private: 1631 1632 static void _setPs() { 1633 synchronized { 1634 if (!_ps) { 1635 _ps = new Screen(); 1636 } 1637 } 1638 } 1639 1640 this() { 1641 } 1642 1643 this(HMONITOR hmonitor) { 1644 this.hmonitor = hmonitor; 1645 } 1646 1647 HMONITOR hmonitor; 1648 1649 static Screen _ps; // Primary screen; might not be used. 1650 static Screen[] _screens; 1651 1652 version (DFL_MULTIPLE_SCREENS) { 1653 1654 bool foundThis = true; // Used during _getScreens callback. 1655 1656 static Screen _findScreen(HMONITOR hm) { 1657 foreach (Screen s; allScreens) { 1658 if (s.hmonitor == hm) { 1659 return s; 1660 } 1661 } 1662 return primaryScreen; 1663 } 1664 1665 static void _getScreens() { 1666 // Note: monitors can change, so always enum, 1667 // but update the array by removing old ones and adding new ones. 1668 for (int i = 0; i < _screens.length; i++) { 1669 _screens[i].foundThis = false; 1670 debug { 1671 if (HMFAKE == _screens[i].hmonitor) { 1672 _screens[i].foundThis = true; 1673 } 1674 } 1675 } 1676 version (SUPPORTS_MULTIPLE_SCREENS) { 1677 pragma(msg, "DFL: multiple screens supported at compile time"); 1678 1679 alias enumScreens = EnumDisplayMonitors; 1680 } else { 1681 auto enumScreens = cast(typeof(&EnumDisplayMonitors)) GetProcAddress( 1682 GetModuleHandleA("user32.dll"), "EnumDisplayMonitors"); 1683 if (!enumScreens) { 1684 //throw new DflException("Multiple screens not supported"); 1685 return; 1686 } 1687 } 1688 if (!enumScreens(null, null, &_gettingScreens, 0)) { 1689 //throw new DflException("Failed to enumerate screens"); 1690 return; 1691 } 1692 { 1693 int numremoved = 0; 1694 for (int i = 0; i < _screens.length; i++) { 1695 if (!_screens[i].foundThis) { 1696 numremoved++; 1697 } 1698 } 1699 if (numremoved > 0) { 1700 Screen[] newscreens = new Screen[_screens.length - numremoved]; 1701 for (int i = 0, nsi = 0; i < _screens.length; i++) { 1702 if (_screens[i].foundThis) { 1703 newscreens[nsi++] = _screens[i]; 1704 } 1705 } 1706 _screens = newscreens; 1707 } 1708 } 1709 } 1710 1711 debug { 1712 static bool _withinFakeScreen(Rect r) { 1713 Rect fr = _screens[1].bounds; 1714 //return r.right >= fr.x; 1715 if (r.x >= fr.x) { 1716 return true; 1717 } 1718 if (r.right < fr.x) { 1719 return false; 1720 } 1721 { 1722 // See which side it's in most. 1723 RECT rect; 1724 r.getRect(&rect); 1725 RECT w0 = rect; 1726 assert(w0.right >= fr.width); 1727 w0.right = fr.width; 1728 RECT w1 = rect; 1729 assert(w1.left <= fr.width); 1730 w1.left = fr.width; 1731 return Rect(&w1).width > Rect(&w0).width; 1732 } 1733 } 1734 } 1735 1736 void _getInfo(ref MONITORINFO info) { 1737 version (SUPPORTS_MULTIPLE_SCREENS) { 1738 alias getMI = GetMonitorInfoA; 1739 } else { 1740 auto getMI = cast(typeof(&GetMonitorInfoA)) GetProcAddress( 1741 GetModuleHandleA("user32.dll"), "GetMonitorInfoA"); 1742 if (!getMI) { 1743 throw new DflException( 1744 "Error getting screen information (unable to find GetMonitorInfoA)"); 1745 } 1746 } 1747 info.cbSize = MONITORINFO.sizeof; 1748 HMONITOR hm = hmonitor; 1749 int fake = -1; 1750 debug { 1751 if (fakeMultipleScreens) { 1752 if (HMFAKE == hm) { 1753 fake = 1; 1754 hm = _screens[0].hmonitor; 1755 } else if (hm == _screens[0].hmonitor) { 1756 fake = 0; 1757 } 1758 } 1759 } 1760 if (!getMI(hm, &info)) { 1761 throw new DflException("Unable to get screen information"); 1762 } 1763 debug { 1764 if (1 == fake) { 1765 info.dwFlags &= ~MONITORINFOF_PRIMARY; 1766 { 1767 Rect r = Rect(&info.rcMonitor); 1768 int w = r.width >> 1; 1769 r.x = r.x + w; 1770 r.width = r.width - w; 1771 r.getRect(&info.rcMonitor); 1772 } 1773 { 1774 Rect r = Rect(&info.rcWork); 1775 int w = r.width >> 1; 1776 r.x = r.x + w; 1777 r.width = r.width - w; 1778 r.getRect(&info.rcWork); 1779 } 1780 } else if (0 == fake) { 1781 { 1782 Rect r = Rect(&info.rcMonitor); 1783 int w = r.width >> 1; 1784 r.width = r.width - w; 1785 r.getRect(&info.rcMonitor); 1786 } 1787 { 1788 Rect r = Rect(&info.rcWork); 1789 int w = r.width >> 1; 1790 r.width = r.width - w; 1791 r.getRect(&info.rcWork); 1792 } 1793 } 1794 } 1795 } 1796 1797 } 1798 } 1799 1800 version (DFL_MULTIPLE_SCREENS) { 1801 private extern (Windows) BOOL _gettingScreens(HMONITOR hmonitor, 1802 HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) nothrow { 1803 for (int i = 0; i < Screen._screens.length; i++) { 1804 if (hmonitor == Screen._screens[i].hmonitor) { 1805 Screen._screens[i].foundThis = true; 1806 return TRUE; // Continue. 1807 } 1808 } 1809 // Didn't find it from old list, so add it. 1810 Screen._screens ~= new Screen(hmonitor); 1811 return TRUE; // Continue. 1812 } 1813 1814 } 1815 1816 class Graphics { 1817 // Used internally. 1818 this(HDC hdc, bool owned = true) { 1819 this.hdc = hdc; 1820 this.owned = owned; 1821 } 1822 1823 ~this() { 1824 if (owned) { 1825 dispose(); 1826 } 1827 } 1828 1829 // Used internally. 1830 final void drawSizeGrip(int right, int bottom) { // package 1831 Color light, dark; 1832 int x, y; 1833 1834 light = SystemColors.controlLightLight; 1835 dark = SystemColors.controlDark; 1836 scope Pen lightPen = new Pen(light); 1837 scope Pen darkPen = new Pen(dark); 1838 x = right; 1839 y = bottom; 1840 1841 x -= 3; 1842 y -= 3; 1843 drawLine(darkPen, x, bottom, right, y); 1844 x--; 1845 y--; 1846 drawLine(darkPen, x, bottom, right, y); 1847 drawLine(lightPen, x - 1, bottom, right, y - 1); 1848 1849 x -= 3; 1850 y -= 3; 1851 drawLine(darkPen, x, bottom, right, y); 1852 x--; 1853 y--; 1854 drawLine(darkPen, x, bottom, right, y); 1855 drawLine(lightPen, x - 1, bottom, right, y - 1); 1856 1857 x -= 3; 1858 y -= 3; 1859 drawLine(darkPen, x, bottom, right, y); 1860 x--; 1861 y--; 1862 drawLine(darkPen, x, bottom, right, y); 1863 drawLine(lightPen, x - 1, bottom, right, y - 1); 1864 } 1865 1866 // Used internally. 1867 // vSplit=true means the move grip moves left to right; false means top to bottom. 1868 final void drawMoveGrip(Rect movableArea, bool vSplit = true, size_t count = 5) { // package 1869 enum MSPACE = 4; 1870 enum MWIDTH = 3; 1871 enum MHEIGHT = 3; 1872 1873 if (!count || !movableArea.width || !movableArea.height) { 1874 return; 1875 } 1876 1877 Color norm, light, dark, ddark; 1878 int x, y; 1879 size_t iw; 1880 1881 norm = SystemColors.control; 1882 light = SystemColors.controlLightLight.blendColor(norm); // center 1883 //dark = SystemColors.controlDark.blendColor(norm); // top 1884 ubyte ubmin(int ub) { 1885 if (ub <= 0) { 1886 return 0; 1887 } 1888 return cast(ubyte) ub; 1889 } 1890 1891 dark = Color(ubmin(cast(int) norm.r - 0x10), 1892 ubmin(cast(int) norm.g - 0x10), ubmin(cast(int) norm.b - 0x10)); 1893 //ddark = SystemColors.controlDarkDark; // bottom 1894 ddark = SystemColors.controlDark.blendColor(Color(0x10, 0x10, 0x10)); // bottom 1895 //scope Pen lightPen = new Pen(light); 1896 scope Pen darkPen = new Pen(dark); 1897 scope Pen ddarkPen = new Pen(ddark); 1898 1899 void drawSingleMoveGrip() { 1900 Point[3] pts; 1901 1902 pts[0].x = x + MWIDTH - 2; 1903 pts[0].y = y; 1904 pts[1].x = x; 1905 pts[1].y = y; 1906 pts[2].x = x; 1907 pts[2].y = y + MHEIGHT - 1; 1908 drawLines(darkPen, pts); 1909 1910 pts[0].x = x + MWIDTH - 1; 1911 pts[0].y = y + 1; 1912 pts[1].x = pts[0].x; 1913 pts[1].y = y + MHEIGHT - 1; 1914 pts[2].x = x; 1915 pts[2].y = pts[1].y; 1916 drawLines(ddarkPen, pts); 1917 1918 fillRectangle(light, x + 1, y + 1, 1, 1); 1919 } 1920 1921 if (vSplit) { 1922 x = movableArea.x + (movableArea.width / 2 - MWIDTH / 2); 1923 //y = movableArea.height / 2 - ((MWIDTH * count) + (MSPACE * (count - 1))) / 2; 1924 y = movableArea.y + (movableArea.height / 2 - ((MWIDTH * count) + (MSPACE * count)) / 2); 1925 1926 for (iw = 0; iw != count; iw++) { 1927 drawSingleMoveGrip(); 1928 y += MHEIGHT + MSPACE; 1929 } 1930 } else { // hSplit 1931 //x = movableArea.width / 2 - ((MHEIGHT * count) + (MSPACE * (count - 1))) / 2; 1932 x = movableArea.x + (movableArea.width / 2 - ((MHEIGHT * count) + (MSPACE * count)) / 2); 1933 y = movableArea.y + (movableArea.height / 2 - MHEIGHT / 2); 1934 1935 for (iw = 0; iw != count; iw++) { 1936 drawSingleMoveGrip(); 1937 x += MWIDTH + MSPACE; 1938 } 1939 } 1940 } 1941 1942 package final TextFormat getCachedTextFormat() { 1943 static TextFormat fmt = null; 1944 if (!fmt) { 1945 fmt = TextFormat.genericDefault; 1946 } 1947 return fmt; 1948 } 1949 1950 // Windows 95/98/Me limits -text- to 8192 characters. 1951 1952 final void drawText(Dstring text, Font font, Color color, Rect r, TextFormat fmt) { 1953 // Should SaveDC/RestoreDC be used instead? 1954 1955 COLORREF prevColor; 1956 HFONT prevFont; 1957 int prevBkMode; 1958 1959 prevColor = SetTextColor(hdc, color.toRgb()); 1960 prevFont = cast(HFONT) SelectObject(hdc, font ? font.handle : null); 1961 prevBkMode = SetBkMode(hdc, TRANSPARENT); 1962 1963 RECT rect; 1964 r.getRect(&rect); 1965 dfl.internal.utf.drawTextEx(hdc, text, &rect, 1966 DT_EXPANDTABS | DT_TABSTOP | fmt._trim | fmt._flags | fmt._align, &fmt._params); 1967 1968 // Reset stuff. 1969 //if(CLR_INVALID != prevColor) 1970 SetTextColor(hdc, prevColor); 1971 //if(prevFont) 1972 SelectObject(hdc, prevFont); 1973 //if(prevBkMode) 1974 SetBkMode(hdc, prevBkMode); 1975 } 1976 1977 final void drawText(Dstring text, Font font, Color color, Rect r) { 1978 return drawText(text, font, color, r, getCachedTextFormat()); 1979 } 1980 1981 final void drawTextDisabled(Dstring text, Font font, Color color, 1982 Color backColor, Rect r, TextFormat fmt) { 1983 r.offset(1, 1); 1984 //drawText(text, font, Color(24, color).solidColor(backColor), r, fmt); // Lighter, lower one. 1985 //drawText(text, font, Color.fromRgb(~color.toRgb() & 0xFFFFFF), r, fmt); // Lighter, lower one. 1986 drawText(text, font, Color(192, 1987 Color.fromRgb(~color.toRgb() & 0xFFFFFF)).solidColor(backColor), r, fmt); // Lighter, lower one. 1988 r.offset(-1, -1); 1989 drawText(text, font, Color(128, color).solidColor(backColor), r, fmt); 1990 } 1991 1992 final void drawTextDisabled(Dstring text, Font font, Color color, Color backColor, 1993 Rect r) { 1994 return drawTextDisabled(text, font, color, backColor, r, getCachedTextFormat()); 1995 } 1996 1997 /+ 1998 final Size measureText(Dstring text, Font font) { 1999 SIZE sz; 2000 HFONT prevFont; 2001 2002 prevFont = cast(HFONT)SelectObject(hdc, font ? font.handle : null); 2003 2004 dfl.internal.utf.getTextExtentPoint32(hdc, text, &sz); 2005 2006 //if(prevFont) 2007 SelectObject(hdc, prevFont); 2008 2009 return Size(sz.cx, sz.cy); 2010 } 2011 +/ 2012 2013 private enum int DEFAULT_MEASURE_SIZE = short.max; // Has to be smaller because it's 16-bits on win9x. 2014 2015 final Size measureText(Dstring text, Font font, int maxWidth, TextFormat fmt) { 2016 RECT rect; 2017 HFONT prevFont; 2018 2019 rect.left = 0; 2020 rect.top = 0; 2021 rect.right = maxWidth; 2022 rect.bottom = DEFAULT_MEASURE_SIZE; 2023 2024 prevFont = cast(HFONT) SelectObject(hdc, font ? font.handle : null); 2025 2026 if (!dfl.internal.utf.drawTextEx(hdc, text, &rect, 2027 DT_EXPANDTABS | DT_TABSTOP | fmt._trim | fmt._flags | fmt._align | DT_CALCRECT | DT_NOCLIP, 2028 &fmt._params)) { 2029 //throw new DflException("Text measure error"); 2030 rect.left = 0; 2031 rect.top = 0; 2032 rect.right = 0; 2033 rect.bottom = 0; 2034 } 2035 2036 //if(prevFont) 2037 SelectObject(hdc, prevFont); 2038 2039 return Size(rect.right - rect.left, rect.bottom - rect.top); 2040 } 2041 2042 final Size measureText(Dstring text, Font font, TextFormat fmt) { 2043 return measureText(text, font, DEFAULT_MEASURE_SIZE, fmt); 2044 } 2045 2046 final Size measureText(Dstring text, Font font, int maxWidth) { 2047 return measureText(text, font, maxWidth, getCachedTextFormat()); 2048 } 2049 2050 final Size measureText(Dstring text, Font font) { 2051 return measureText(text, font, DEFAULT_MEASURE_SIZE, getCachedTextFormat()); 2052 } 2053 2054 /+ 2055 // Doesn't work... dfl.internal.utf.drawTextEx uses a different buffer! 2056 // /// 2057 final Dstring getTrimmedText(Dstring text, Font font, Rect r, TextFormat fmt) { // deprecated 2058 switch(fmt.trimming) { 2059 case TextTrimming.ELLIPSIS: 2060 case TextTrimming.ELLIPSIS_PATH: { 2061 char[] newtext; 2062 RECT rect; 2063 HFONT prevFont; 2064 2065 newtext = text.dup; 2066 r.getRect(&rect); 2067 prevFont = cast(HFONT)SelectObject(hdc, font ? font.handle : null); 2068 2069 // DT_CALCRECT needs to prevent it from actually drawing. 2070 if(!dfl.internal.utf.drawTextEx(hdc, newtext, &rect, DT_EXPANDTABS | DT_TABSTOP | 2071 fmt._trim | fmt._flags | fmt._align | DT_CALCRECT | DT_MODIFYSTRING | DT_NOCLIP, &fmt._params)) { 2072 //throw new DflException("Text trimming error"); 2073 } 2074 2075 //if(prevFont) 2076 SelectObject(hdc, prevFont); 2077 2078 for(size_t iw = 0; iw != newtext.length; iw++) { 2079 if(!newtext[iw]) { 2080 return newtext[0 .. iw]; 2081 } 2082 } 2083 //return newtext; 2084 // There was no change, so no sense in keeping the duplicate. 2085 delete newtext; 2086 return text; 2087 } 2088 break; 2089 2090 default: 2091 return text; 2092 } 2093 } 2094 2095 // /// 2096 final Dstring getTrimmedText(Dstring text, Font font, Rect r, TextTrimming trim) { 2097 scope fmt = new TextFormat(TextFormatFlags.NO_PREFIX | TextFormatFlags.WORD_BREAK | 2098 TextFormatFlags.NO_CLIP | TextFormatFlags.LINE_LIMIT); 2099 fmt.trimming = trim; 2100 return getTrimmedText(text, font, r, fmt); 2101 } 2102 +/ 2103 2104 final void drawIcon(Icon icon, Rect r) { 2105 // DrawIconEx operates differently if the width or height is zero 2106 // so bail out if zero and pretend the zero size icon was drawn. 2107 int width = r.width; 2108 if (!width) { 2109 return; 2110 } 2111 int height = r.height; 2112 if (!height) { 2113 return; 2114 } 2115 2116 DrawIconEx(handle, r.x, r.y, icon.handle, width, height, 0, null, DI_NORMAL); 2117 } 2118 2119 final void drawIcon(Icon icon, int x, int y) { 2120 DrawIconEx(handle, x, y, icon.handle, 0, 0, 0, null, DI_NORMAL); 2121 } 2122 2123 final void fillRectangle(Brush brush, Rect r) { 2124 fillRectangle(brush, r.x, r.y, r.width, r.height); 2125 } 2126 2127 final void fillRectangle(Brush brush, int x, int y, int width, int height) { 2128 RECT rect; 2129 rect.left = x; 2130 rect.right = x + width; 2131 rect.top = y; 2132 rect.bottom = y + height; 2133 FillRect(handle, &rect, brush.handle); 2134 } 2135 2136 // Extra function. 2137 final void fillRectangle(Color color, Rect r) { 2138 fillRectangle(color, r.x, r.y, r.width, r.height); 2139 } 2140 2141 // Extra function. 2142 final void fillRectangle(Color color, int x, int y, int width, int height) { 2143 RECT rect; 2144 int prevBkColor; 2145 2146 prevBkColor = SetBkColor(hdc, color.toRgb()); 2147 2148 rect.left = x; 2149 rect.top = y; 2150 rect.right = x + width; 2151 rect.bottom = y + height; 2152 ExtTextOutA(hdc, x, y, ETO_OPAQUE, &rect, "", 0, null); 2153 2154 // Reset stuff. 2155 //if(CLR_INVALID != prevBkColor) 2156 SetBkColor(hdc, prevBkColor); 2157 } 2158 2159 final void fillRegion(Brush brush, Region region) { 2160 FillRgn(handle, region.handle, brush.handle); 2161 } 2162 2163 static Graphics fromHwnd(HWND hwnd) { 2164 return new CommonGraphics(hwnd, GetDC(hwnd)); 2165 } 2166 2167 /// Get the entire screen's Graphics for the primary monitor. 2168 static Graphics getScreen() { 2169 return new CommonGraphics(null, GetWindowDC(null)); 2170 } 2171 2172 final void drawLine(Pen pen, Point start, Point end) { 2173 drawLine(pen, start.x, start.y, end.x, end.y); 2174 } 2175 2176 final void drawLine(Pen pen, int startX, int startY, int endX, int endY) { 2177 HPEN prevPen; 2178 2179 prevPen = SelectObject(hdc, pen.handle); 2180 2181 MoveToEx(hdc, startX, startY, null); 2182 LineTo(hdc, endX, endY); 2183 2184 // Reset stuff. 2185 SelectObject(hdc, prevPen); 2186 } 2187 2188 // First two points is the first line, the other points link a line 2189 // to the previous point. 2190 final void drawLines(Pen pen, Point[] points) { 2191 assert(points.length >= 2, "Not enough line points."); 2192 2193 HPEN prevPen; 2194 int i; 2195 2196 prevPen = SelectObject(hdc, pen.handle); 2197 2198 MoveToEx(hdc, points[0].x, points[0].y, null); 2199 for (i = 1;;) { 2200 LineTo(hdc, points[i].x, points[i].y); 2201 2202 if (++i == points.length) { 2203 break; 2204 } 2205 } 2206 2207 // Reset stuff. 2208 SelectObject(hdc, prevPen); 2209 } 2210 2211 final void drawArc(Pen pen, int x, int y, int width, int height, int arcX1, 2212 int arcY1, int arcX2, int arcY2) { 2213 HPEN prevPen; 2214 2215 prevPen = SelectObject(hdc, pen.handle); 2216 2217 Arc(hdc, x, y, x + width, y + height, arcX1, arcY1, arcX2, arcY2); 2218 2219 // Reset stuff. 2220 SelectObject(hdc, prevPen); 2221 } 2222 2223 final void drawArc(Pen pen, Rect r, Point arc1, Point arc2) { 2224 drawArc(pen, r.x, r.y, r.width, r.height, arc1.x, arc1.y, arc2.x, arc2.y); 2225 } 2226 2227 final void drawBezier(Pen pen, Point[4] points) { 2228 HPEN prevPen; 2229 POINT* cpts; 2230 2231 prevPen = SelectObject(hdc, pen.handle); 2232 2233 // This assumes a Point is laid out exactly like a POINT. 2234 static assert(Point.sizeof == POINT.sizeof); 2235 cpts = cast(POINT*) cast(Point*) points; 2236 2237 PolyBezier(hdc, cpts, 4); 2238 2239 // Reset stuff. 2240 SelectObject(hdc, prevPen); 2241 } 2242 2243 final void drawBezier(Pen pen, Point pt1, Point pt2, Point pt3, Point pt4) { 2244 Point[4] points; 2245 points[0] = pt1; 2246 points[1] = pt2; 2247 points[2] = pt3; 2248 points[3] = pt4; 2249 drawBezier(pen, points); 2250 } 2251 2252 // First 4 points are the first bezier, each next 3 are the next 2253 // beziers, using the previous last point as the starting point. 2254 final void drawBeziers(Pen pen, Point[] points) { 2255 if (points.length < 1 || (points.length - 1) % 3) { 2256 assert(0); // Bad number of points. 2257 //return; // Let PolyBezier() do what it wants with the bad number. 2258 } 2259 2260 HPEN prevPen; 2261 POINT* cpts; 2262 2263 prevPen = SelectObject(hdc, pen.handle); 2264 2265 // This assumes a Point is laid out exactly like a POINT. 2266 static assert(Point.sizeof == POINT.sizeof); 2267 cpts = cast(POINT*) cast(Point*) points; 2268 2269 PolyBezier(hdc, cpts, points.length); 2270 2271 // Reset stuff. 2272 SelectObject(hdc, prevPen); 2273 } 2274 2275 // TODO: drawCurve(), drawClosedCurve() ... 2276 2277 final void drawEllipse(Pen pen, Rect r) { 2278 drawEllipse(pen, r.x, r.y, r.width, r.height); 2279 } 2280 2281 final void drawEllipse(Pen pen, int x, int y, int width, int height) { 2282 HPEN prevPen; 2283 HBRUSH prevBrush; 2284 2285 prevPen = SelectObject(hdc, pen.handle); 2286 prevBrush = SelectObject(hdc, cast(HBRUSH) GetStockObject(NULL_BRUSH)); // Don't fill it in. 2287 2288 Ellipse(hdc, x, y, x + width, y + height); 2289 2290 // Reset stuff. 2291 SelectObject(hdc, prevPen); 2292 SelectObject(hdc, prevBrush); 2293 } 2294 2295 // TODO: drawPie() 2296 2297 final void drawPolygon(Pen pen, Point[] points) { 2298 if (points.length < 2) { 2299 assert(0); // Need at least 2 points. 2300 //return; 2301 } 2302 2303 HPEN prevPen; 2304 HBRUSH prevBrush; 2305 POINT* cpts; 2306 2307 prevPen = SelectObject(hdc, pen.handle); 2308 prevBrush = SelectObject(hdc, cast(HBRUSH) GetStockObject(NULL_BRUSH)); // Don't fill it in. 2309 2310 // This assumes a Point is laid out exactly like a POINT. 2311 static assert(Point.sizeof == POINT.sizeof); 2312 cpts = cast(POINT*) cast(Point*) points; 2313 2314 Polygon(hdc, cpts, points.length); 2315 2316 // Reset stuff. 2317 SelectObject(hdc, prevPen); 2318 SelectObject(hdc, prevBrush); 2319 } 2320 2321 final void drawRectangle(Pen pen, Rect r) { 2322 drawRectangle(pen, r.x, r.y, r.width, r.height); 2323 } 2324 2325 final void drawRectangle(Pen pen, int x, int y, int width, int height) { 2326 HPEN prevPen; 2327 HBRUSH prevBrush; 2328 2329 prevPen = SelectObject(hdc, pen.handle); 2330 prevBrush = SelectObject(hdc, cast(HBRUSH) GetStockObject(NULL_BRUSH)); // Don't fill it in. 2331 2332 Rectangle(hdc, x, y, x + width, y + height); 2333 2334 // Reset stuff. 2335 SelectObject(hdc, prevPen); 2336 SelectObject(hdc, prevBrush); 2337 } 2338 2339 /+ 2340 final void drawRectangle(Color c, Rect r) { 2341 drawRectangle(c, r.x, r.y, r.width, r.height); 2342 } 2343 2344 2345 final void drawRectangle(Color c, int x, int y, int width, int height) { 2346 2347 } 2348 +/ 2349 2350 final void drawRectangles(Pen pen, Rect[] rs) { 2351 HPEN prevPen; 2352 HBRUSH prevBrush; 2353 2354 prevPen = SelectObject(hdc, pen.handle); 2355 prevBrush = SelectObject(hdc, cast(HBRUSH) GetStockObject(NULL_BRUSH)); // Don't fill it in. 2356 2357 foreach (ref Rect r; rs) { 2358 Rectangle(hdc, r.x, r.y, r.x + r.width, r.y + r.height); 2359 } 2360 2361 // Reset stuff. 2362 SelectObject(hdc, prevPen); 2363 SelectObject(hdc, prevBrush); 2364 } 2365 2366 // Force pending graphics operations. 2367 final void flush() { 2368 GdiFlush(); 2369 } 2370 2371 final Color getNearestColor(Color c) { 2372 COLORREF cref; 2373 cref = GetNearestColor(handle, c.toRgb()); 2374 if (CLR_INVALID == cref) { 2375 return Color.empty; 2376 } 2377 return Color.fromRgb(c.a, cref); // Preserve alpha. 2378 } 2379 2380 final Size getScaleSize(Font f) { 2381 // http://support.microsoft.com/kb/125681 2382 Size result; 2383 version (DIALOG_BOX_SCALE) { 2384 enum SAMPLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 2385 result = measureText(SAMPLE, f); 2386 result.width = (result.width / (SAMPLE.length / 2) + 1) / 2; 2387 TEXTMETRICA tma; 2388 if (GetTextMetricsA(handle, &tma)) { 2389 result.height = tma.tmHeight; 2390 } 2391 } else { 2392 enum SAMPLE = "Abcdefghijklmnopqrstuvwxyz"; 2393 result = measureText(SAMPLE, f); 2394 result.width /= SAMPLE.length; 2395 } 2396 return result; 2397 } 2398 2399 final bool copyTo(HDC dest, int destX, int destY, int width, int height, 2400 int srcX = 0, int srcY = 0, DWORD rop = SRCCOPY) { // package 2401 return cast(bool) BitBlt(dest, destX, destY, 2402 width, height, this.handle, srcX, srcY, rop); 2403 } 2404 2405 final bool copyTo(Graphics destGraphics, int destX, int destY, int width, 2406 int height, int srcX = 0, int srcY = 0, DWORD rop = SRCCOPY) { 2407 return copyTo(destGraphics.handle, destX, destY, width, height, srcX, srcY, 2408 rop); 2409 } 2410 2411 final bool copyTo(Graphics destGraphics, Rect bounds) { 2412 return copyTo(destGraphics.handle, bounds.x, bounds.y, bounds.width, bounds.height); 2413 } 2414 2415 final @property HDC handle() { 2416 return hdc; 2417 } 2418 2419 void dispose() { 2420 assert(owned); 2421 DeleteDC(hdc); 2422 hdc = null; 2423 } 2424 2425 private: 2426 HDC hdc; 2427 bool owned = true; 2428 } 2429 2430 /// Graphics for a surface in memory. 2431 class MemoryGraphics : Graphics { 2432 2433 // Graphics compatible with the current screen. 2434 this(int width, int height) { 2435 HDC hdc; 2436 hdc = GetWindowDC(null); 2437 scope (exit) 2438 ReleaseDC(null, hdc); 2439 this(width, height, hdc); 2440 } 2441 2442 // graphicsCompatible cannot be another MemoryGraphics. 2443 this(int width, int height, Graphics graphicsCompatible) { 2444 if (cast(MemoryGraphics) graphicsCompatible) { 2445 //throw new DflException("Graphics cannot be compatible with memory"); 2446 assert(0, "Graphics cannot be compatible with memory"); 2447 } 2448 this(width, height, graphicsCompatible.handle); 2449 } 2450 2451 // Used internally. 2452 this(int width, int height, HDC hdcCompatible) { // package 2453 _w = width; 2454 _h = height; 2455 2456 hbm = CreateCompatibleBitmap(hdcCompatible, width, height); 2457 if (!hbm) { 2458 throw new DflException("Unable to allocate Graphics memory"); 2459 } 2460 scope (failure) { 2461 DeleteObject(hbm); 2462 //hbm = HBITMAP.init; 2463 } 2464 2465 HDC hdcc; 2466 hdcc = CreateCompatibleDC(hdcCompatible); 2467 if (!hdcc) { 2468 throw new DflException("Unable to allocate Graphics"); 2469 } 2470 scope (failure) 2471 DeleteDC(hdcc); 2472 2473 hbmOld = SelectObject(hdcc, hbm); 2474 scope (failure) 2475 SelectObject(hdcc, hbmOld); 2476 2477 super(hdcc); 2478 } 2479 2480 final @property int width() { 2481 return _w; 2482 } 2483 2484 final @property int height() { 2485 return _h; 2486 } 2487 2488 final Size size() { 2489 return Size(_w, _h); 2490 } 2491 2492 final @property HBITMAP hbitmap() { // getter // package 2493 return hbm; 2494 } 2495 2496 // Needs to copy so it can be selected into other DC`s. 2497 final HBITMAP toHBitmap(HDC hdc) { // package 2498 HDC memdc; 2499 HBITMAP result; 2500 HGDIOBJ oldbm; 2501 memdc = CreateCompatibleDC(hdc); 2502 if (!memdc) { 2503 throw new DflException("Device error"); 2504 } 2505 try { 2506 result = CreateCompatibleBitmap(hdc, width, height); 2507 if (!result) { 2508 bad_bm: 2509 throw new DflException("Unable to allocate image"); 2510 } 2511 oldbm = SelectObject(memdc, result); 2512 copyTo(memdc, 0, 0, width, height); 2513 } 2514 finally { 2515 if (oldbm) { 2516 SelectObject(memdc, oldbm); 2517 } 2518 DeleteDC(memdc); 2519 } 2520 return result; 2521 } 2522 2523 final Bitmap toBitmap(HDC hdc) { // package 2524 HBITMAP hbm; 2525 hbm = toHBitmap(hdc); 2526 if (!hbm) { 2527 throw new DflException("Unable to create bitmap"); 2528 } 2529 return new Bitmap(hbm, true); // Owned. 2530 } 2531 2532 final Bitmap toBitmap() { 2533 Graphics g; 2534 Bitmap result; 2535 g = Graphics.getScreen(); 2536 result = toBitmap(g); 2537 g.dispose(); 2538 return result; 2539 } 2540 2541 final Bitmap toBitmap(Graphics g) { 2542 return toBitmap(g.handle); 2543 } 2544 2545 override void dispose() { 2546 SelectObject(hdc, hbmOld); 2547 hbmOld = HGDIOBJ.init; 2548 DeleteObject(hbm); 2549 hbm = HBITMAP.init; 2550 super.dispose(); 2551 } 2552 2553 private: 2554 HGDIOBJ hbmOld; 2555 HBITMAP hbm; 2556 int _w, _h; 2557 } 2558 2559 final class EmfGraphics : Graphics { 2560 private: 2561 HDC _hdc; 2562 Rect _area; 2563 public: 2564 2565 this(Graphics refGraphics = null, Rect area = Rect.init, 2566 string filename = null, string description = null) { 2567 _area = area; 2568 RECT rc; 2569 RECT* pRc; 2570 HDC hdcref; 2571 if (refGraphics) { 2572 hdcref = refGraphics.handle; 2573 } else { 2574 hdcref = GetDC(null); 2575 } 2576 scope (exit) { 2577 if (!refGraphics) { 2578 ReleaseDC(null, hdcref); 2579 } 2580 } 2581 if (area != Rect.init) { 2582 auto tmphdc = CreateEnhMetaFileW(hdcref, null, null, null); 2583 auto tmpemf = CloseEnhMetaFile(tmphdc); 2584 ENHMETAHEADER tmphdr; 2585 GetEnhMetaFileHeader(tmpemf, ENHMETAHEADER.sizeof, &tmphdr); 2586 DeleteEnhMetaFile(tmpemf); 2587 rc.left = MulDiv(_area.x, GetDeviceCaps(hdcref, HORZSIZE) * 100, tmphdr.szlDevice.cx); 2588 rc.top = MulDiv(_area.y, GetDeviceCaps(hdcref, VERTSIZE) * 100, tmphdr.szlDevice.cy); 2589 rc.right = MulDiv(_area.right, GetDeviceCaps(hdcref, HORZSIZE) * 100, 2590 tmphdr.szlDevice.cx); 2591 rc.bottom = MulDiv(_area.bottom, GetDeviceCaps(hdcref, 2592 VERTSIZE) * 100, tmphdr.szlDevice.cy); 2593 pRc = &rc; 2594 } 2595 import std.utf; 2596 2597 _hdc = CreateEnhMetaFileW(hdcref, 2598 filename.length ? filename.toUTF16z() : null, pRc, 2599 description.length ? description.toUTF16z() : null); 2600 super(_hdc, false); 2601 } 2602 2603 this(Rect area, string filename = null, string description = null) { 2604 this(null, area, filename, description); 2605 } 2606 2607 this(uint width, uint height, string filename = null, string description = null) { 2608 this(null, Rect(0, 0, width, height), filename, description); 2609 } 2610 2611 override void dispose() { 2612 super.dispose(); 2613 if (_hdc) { 2614 DeleteEnhMetaFile(CloseEnhMetaFile(_hdc)); 2615 } 2616 } 2617 2618 Size size() const pure nothrow @property { 2619 return _area.size; 2620 } 2621 2622 uint width() const pure nothrow @property { 2623 return _area.width; 2624 } 2625 2626 uint height() const pure nothrow @property { 2627 return _area.height; 2628 } 2629 2630 Rect frameRectangle() const pure nothrow @property { 2631 return _area; 2632 } 2633 2634 EnhancedMetaFile toEnhancedMetaFile() { 2635 return new EnhancedMetaFile(CloseEnhMetaFile(_hdc)); 2636 } 2637 } 2638 2639 // Use with GetDC()/GetWindowDC()/GetDCEx() so that 2640 // the HDC is properly released instead of deleted. 2641 package class CommonGraphics : Graphics { 2642 // Used internally. 2643 this(HWND hwnd, HDC hdc, bool owned = true) { 2644 super(hdc, owned); 2645 this.hwnd = hwnd; 2646 } 2647 2648 override void dispose() { 2649 ReleaseDC(hwnd, hdc); 2650 hdc = null; 2651 } 2652 2653 package: 2654 HWND hwnd; 2655 } 2656 2657 class Icon : Image { 2658 // Used internally. 2659 this(HICON hi, bool owned = true) { 2660 this.hi = hi; 2661 this.owned = owned; 2662 } 2663 2664 // Load from an ico file. 2665 this(Dstring fileName) { 2666 this.hi = cast(HICON) dfl.internal.utf.loadImage(null, fileName, 2667 IMAGE_ICON, 0, 0, LR_LOADFROMFILE); 2668 if (!this.hi) { 2669 throw new DflException("Unable to load icon from file '" ~ fileName ~ "'"); 2670 } 2671 } 2672 2673 deprecated static Icon fromHandle(HICON hi) { 2674 return new Icon(hi, false); // Not owned. Up to caller to manage or call dispose(). 2675 } 2676 2677 // -bm- can be null. 2678 // NOTE: the bitmaps in -ii- need to be deleted! _deleteBitmaps() is a shortcut. 2679 private void _getInfo(ICONINFO* ii, BITMAP* bm = null) { 2680 if (GetIconInfo(hi, ii)) { 2681 if (!bm) { 2682 return; 2683 } 2684 2685 HBITMAP hbm; 2686 if (ii.hbmColor) { 2687 hbm = ii.hbmColor; 2688 } else { // Monochrome. 2689 hbm = ii.hbmMask; 2690 } 2691 if (GetObjectA(hbm, BITMAP.sizeof, bm) == BITMAP.sizeof) { 2692 return; 2693 } 2694 } 2695 2696 // Fell through, failed. 2697 throw new DflException("Unable to get image information"); 2698 } 2699 2700 private void _deleteBitmaps(ICONINFO* ii) { 2701 DeleteObject(ii.hbmColor); 2702 ii.hbmColor = null; 2703 DeleteObject(ii.hbmMask); 2704 ii.hbmMask = null; 2705 } 2706 2707 final Bitmap toBitmap() { 2708 ICONINFO ii; 2709 BITMAP bm; 2710 _getInfo(&ii, &bm); 2711 // Not calling _deleteBitmaps() because I'm keeping one. 2712 2713 HBITMAP hbm; 2714 if (ii.hbmColor) { 2715 hbm = ii.hbmColor; 2716 DeleteObject(ii.hbmMask); 2717 } else { // Monochrome. 2718 hbm = ii.hbmMask; 2719 } 2720 2721 return new Bitmap(hbm, true); // Yes owned. 2722 } 2723 2724 final override void draw(Graphics g, Point pt) { 2725 g.drawIcon(this, pt.x, pt.y); 2726 } 2727 2728 final override void drawStretched(Graphics g, Rect r) { 2729 g.drawIcon(this, r); 2730 } 2731 2732 final override @property Size size() { 2733 ICONINFO ii; 2734 BITMAP bm; 2735 _getInfo(&ii, &bm); 2736 _deleteBitmaps(&ii); 2737 return Size(bm.bmWidth, bm.bmHeight); 2738 } 2739 2740 final override @property int width() { 2741 return size.width; 2742 } 2743 2744 final override @property int height() { 2745 return size.height; 2746 } 2747 2748 ~this() { 2749 if (owned) { 2750 dispose(); 2751 } 2752 } 2753 2754 override int _imgtype(HGDIOBJ* ph) { // internal 2755 if (ph) { 2756 *ph = cast(HGDIOBJ) hi; 2757 } 2758 return 2; 2759 } 2760 2761 void dispose() { 2762 assert(owned); 2763 DestroyIcon(hi); 2764 hi = null; 2765 } 2766 2767 final @property HICON handle() { 2768 return hi; 2769 } 2770 2771 private: 2772 HICON hi; 2773 bool owned = true; 2774 } 2775 2776 enum GraphicsUnit : ubyte { // docmain ? 2777 2778 PIXEL, 2779 2780 DISPLAY, // 1/75 inch. 2781 2782 DOCUMENT, // 1/300 inch. 2783 2784 INCH, // 1 inch, der. 2785 2786 MILLIMETER, // 25.4 millimeters in 1 inch. 2787 2788 POINT, // 1/72 inch. 2789 //WORLD, // ? 2790 TWIP, // Extra. 1/1440 inch. 2791 } 2792 2793 /+ 2794 // TODO: check if correct implementation. 2795 enum GenericFontFamilies { 2796 MONOSPACE = FF_MODERN, 2797 SANS_SERIF = FF_ROMAN, 2798 SERIF = FF_SWISS, 2799 } 2800 +/ 2801 2802 /+ 2803 abstract class FontCollection { 2804 abstract @property FontFamily[] families(); 2805 } 2806 2807 2808 class FontFamily { 2809 /+ 2810 this(GenericFontFamilies genericFamily) { 2811 2812 } 2813 +/ 2814 2815 2816 this(Dstring name) { 2817 2818 } 2819 2820 2821 this(Dstring name, FontCollection fontCollection) { 2822 2823 } 2824 2825 2826 final @property Dstring name() { 2827 2828 } 2829 2830 2831 static @property FontFamily[] families() { 2832 2833 } 2834 2835 2836 /+ 2837 // TODO: implement. 2838 2839 static @property FontFamily genericMonospace() { 2840 2841 } 2842 2843 2844 static @property FontFamily genericSansSerif() { 2845 2846 } 2847 2848 2849 static @property FontFamily genericSerif() { 2850 2851 } 2852 +/ 2853 } 2854 +/ 2855 2856 // Flags. 2857 enum FontStyle : ubyte { 2858 REGULAR = 0, /// 2859 BOLD = 1, 2860 ITALIC = 2, 2861 UNDERLINE = 4, 2862 STRIKEOUT = 8, 2863 } 2864 2865 enum FontSmoothing { 2866 DEFAULT = DEFAULT_QUALITY, 2867 ON = ANTIALIASED_QUALITY, 2868 OFF = NONANTIALIASED_QUALITY, 2869 } 2870 2871 class Font { 2872 // Used internally. 2873 static void LOGFONTAtoLogFont(ref LogFont lf, LOGFONTA* plfa) { // package // deprecated 2874 lf.lfa = *plfa; 2875 lf.faceName = dfl.internal.utf.fromAnsiz(plfa.lfFaceName.ptr); 2876 } 2877 2878 // Used internally. 2879 static void LOGFONTWtoLogFont(ref LogFont lf, LOGFONTW* plfw) { // package // deprecated 2880 lf.lfw = *plfw; 2881 lf.faceName = dfl.internal.utf.fromUnicodez(plfw.lfFaceName.ptr); 2882 } 2883 2884 // Used internally. 2885 this(HFONT hf, LOGFONTA* plfa, bool owned = true) { // package // deprecated 2886 LogFont lf; 2887 LOGFONTAtoLogFont(lf, plfa); 2888 2889 this.hf = hf; 2890 this.owned = owned; 2891 this._unit = GraphicsUnit.POINT; 2892 2893 _fstyle = _style(lf); 2894 _initLf(lf); 2895 } 2896 2897 // Used internally. 2898 this(HFONT hf, ref LogFont lf, bool owned = true) { // package 2899 this.hf = hf; 2900 this.owned = owned; 2901 this._unit = GraphicsUnit.POINT; 2902 2903 _fstyle = _style(lf); 2904 _initLf(lf); 2905 } 2906 2907 // Used internally. 2908 this(HFONT hf, bool owned = true) { // package 2909 this.hf = hf; 2910 this.owned = owned; 2911 this._unit = GraphicsUnit.POINT; 2912 2913 LogFont lf; 2914 _info(lf); 2915 2916 _fstyle = _style(lf); 2917 _initLf(lf); 2918 } 2919 2920 // Used internally. 2921 this(LOGFONTA* plfa, bool owned = true) { // package // deprecated 2922 LogFont lf; 2923 LOGFONTAtoLogFont(lf, plfa); 2924 2925 this(_create(lf), lf, owned); 2926 } 2927 2928 // Used internally. 2929 this(ref LogFont lf, bool owned = true) { // package 2930 this(_create(lf), lf, owned); 2931 } 2932 2933 package static HFONT _create(ref LogFont lf) { 2934 HFONT result; 2935 result = dfl.internal.utf.createFontIndirect(lf); 2936 if (!result) { 2937 throw new DflException("Unable to create font"); 2938 } 2939 return result; 2940 } 2941 2942 private static void _style(ref LogFont lf, FontStyle style) { 2943 lf.lf.lfWeight = (style & FontStyle.BOLD) ? FW_BOLD : FW_NORMAL; 2944 lf.lf.lfItalic = (style & FontStyle.ITALIC) ? TRUE : FALSE; 2945 lf.lf.lfUnderline = (style & FontStyle.UNDERLINE) ? TRUE : FALSE; 2946 lf.lf.lfStrikeOut = (style & FontStyle.STRIKEOUT) ? TRUE : FALSE; 2947 } 2948 2949 private static FontStyle _style(ref LogFont lf) { 2950 FontStyle style = FontStyle.REGULAR; 2951 2952 if (lf.lf.lfWeight >= FW_BOLD) { 2953 style |= FontStyle.BOLD; 2954 } 2955 if (lf.lf.lfItalic) { 2956 style |= FontStyle.ITALIC; 2957 } 2958 if (lf.lf.lfUnderline) { 2959 style |= FontStyle.UNDERLINE; 2960 } 2961 if (lf.lf.lfStrikeOut) { 2962 style |= FontStyle.STRIKEOUT; 2963 } 2964 2965 return style; 2966 } 2967 2968 package void _info(LOGFONTA* lf) { // deprecated 2969 if (GetObjectA(hf, LOGFONTA.sizeof, lf) != LOGFONTA.sizeof) { 2970 throw new DflException("Unable to get font information"); 2971 } 2972 } 2973 2974 package void _info(LOGFONTW* lf) { // deprecated 2975 auto proc = cast(GetObjectWProc) GetProcAddress(GetModuleHandleA("gdi32.dll"), 2976 "GetObjectW"); 2977 2978 if (!proc || proc(hf, LOGFONTW.sizeof, lf) != LOGFONTW.sizeof) { 2979 throw new DflException("Unable to get font information"); 2980 } 2981 } 2982 2983 package void _info(ref LogFont lf) { 2984 if (!dfl.internal.utf.getLogFont(hf, lf)) { 2985 throw new DflException("Unable to get font information"); 2986 } 2987 } 2988 2989 package static LONG getLfHeight(float emSize, GraphicsUnit unit) { 2990 LONG result; 2991 HDC hdc; 2992 2993 final switch (unit) { 2994 case GraphicsUnit.PIXEL: 2995 result = cast(LONG) emSize; 2996 break; 2997 2998 case GraphicsUnit.POINT: 2999 hdc = GetWindowDC(null); 3000 result = MulDiv(cast(int)(emSize * 100), GetDeviceCaps(hdc, LOGPIXELSY), 3001 72 * 100); 3002 ReleaseDC(null, hdc); 3003 break; 3004 3005 case GraphicsUnit.DISPLAY: 3006 hdc = GetWindowDC(null); 3007 result = MulDiv(cast(int)(emSize * 100), GetDeviceCaps(hdc, LOGPIXELSY), 3008 75 * 100); 3009 ReleaseDC(null, hdc); 3010 break; 3011 3012 case GraphicsUnit.MILLIMETER: 3013 hdc = GetWindowDC(null); 3014 result = MulDiv(cast(int)(emSize * 100), GetDeviceCaps(hdc, LOGPIXELSY), 3015 2540); 3016 ReleaseDC(null, hdc); 3017 break; 3018 3019 case GraphicsUnit.INCH: 3020 hdc = GetWindowDC(null); 3021 result = cast(LONG)(emSize * cast(float) GetDeviceCaps(hdc, LOGPIXELSY)); 3022 ReleaseDC(null, hdc); 3023 break; 3024 3025 case GraphicsUnit.DOCUMENT: 3026 hdc = GetWindowDC(null); 3027 result = MulDiv(cast(int)(emSize * 100), GetDeviceCaps(hdc, LOGPIXELSY), 3028 300 * 100); 3029 ReleaseDC(null, hdc); 3030 break; 3031 3032 case GraphicsUnit.TWIP: 3033 hdc = GetWindowDC(null); 3034 result = MulDiv(cast(int)(emSize * 100), GetDeviceCaps(hdc, LOGPIXELSY), 3035 1440 * 100); 3036 ReleaseDC(null, hdc); 3037 break; 3038 } 3039 3040 return result; 3041 } 3042 3043 package static float getEmSize(HDC hdc, LONG lfHeight, GraphicsUnit toUnit) { 3044 float result; 3045 3046 if (lfHeight < 0) { 3047 lfHeight = -lfHeight; 3048 } 3049 3050 final switch (toUnit) { 3051 case GraphicsUnit.PIXEL: 3052 result = cast(float) lfHeight; 3053 break; 3054 3055 case GraphicsUnit.POINT: 3056 result = cast(float) MulDiv(lfHeight, 72, 3057 GetDeviceCaps(hdc, LOGPIXELSY)); 3058 break; 3059 3060 case GraphicsUnit.DISPLAY: 3061 result = cast(float) MulDiv(lfHeight, 75, 3062 GetDeviceCaps(hdc, LOGPIXELSY)); 3063 break; 3064 3065 case GraphicsUnit.MILLIMETER: 3066 result = cast(float) MulDiv(lfHeight, 254, 3067 GetDeviceCaps(hdc, LOGPIXELSY)) / 10.0; 3068 break; 3069 3070 case GraphicsUnit.INCH: 3071 result = cast(float) lfHeight / cast(float) GetDeviceCaps(hdc, 3072 LOGPIXELSY); 3073 break; 3074 3075 case GraphicsUnit.DOCUMENT: 3076 result = cast(float) MulDiv(lfHeight, 300, 3077 GetDeviceCaps(hdc, LOGPIXELSY)); 3078 break; 3079 3080 case GraphicsUnit.TWIP: 3081 result = cast(float) MulDiv(lfHeight, 1440, 3082 GetDeviceCaps(hdc, LOGPIXELSY)); 3083 break; 3084 } 3085 3086 return result; 3087 } 3088 3089 package static float getEmSize(LONG lfHeight, GraphicsUnit toUnit) { 3090 if (GraphicsUnit.PIXEL == toUnit) { 3091 if (lfHeight < 0) { 3092 return cast(float)-lfHeight; 3093 } 3094 return cast(float) lfHeight; 3095 } 3096 HDC hdc; 3097 hdc = GetWindowDC(null); 3098 float result = getEmSize(hdc, lfHeight, toUnit); 3099 ReleaseDC(null, hdc); 3100 return result; 3101 } 3102 3103 this(Font font, FontStyle style) { 3104 LogFont lf; 3105 _unit = font._unit; 3106 font._info(lf); 3107 _style(lf, style); 3108 this(_create(lf)); 3109 3110 _fstyle = style; 3111 _initLf(font, lf); 3112 } 3113 3114 this(Dstring name, float emSize, GraphicsUnit unit) { 3115 this(name, emSize, FontStyle.REGULAR, unit); 3116 } 3117 3118 this(Dstring name, float emSize, FontStyle style = FontStyle.REGULAR, 3119 GraphicsUnit unit = GraphicsUnit.POINT) { 3120 this(name, emSize, style, unit, DEFAULT_CHARSET, FontSmoothing.DEFAULT); 3121 } 3122 3123 this(Dstring name, float emSize, FontStyle style, GraphicsUnit unit, FontSmoothing smoothing) { 3124 this(name, emSize, style, unit, DEFAULT_CHARSET, smoothing); 3125 } 3126 3127 // 3128 // This is a somewhat internal function. 3129 // -gdiCharSet- is one of *_CHARSET from wingdi.h 3130 this(Dstring name, float emSize, FontStyle style, GraphicsUnit unit, 3131 ubyte gdiCharSet, FontSmoothing smoothing = FontSmoothing.DEFAULT) { 3132 LogFont lf; 3133 3134 lf.faceName = name; 3135 lf.lf.lfCharSet = gdiCharSet; 3136 lf.lf.lfQuality = cast(BYTE) smoothing; 3137 lf.lf.lfOutPrecision = OUT_DEFAULT_PRECIS; 3138 lf.lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; 3139 lf.lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; 3140 3141 this(lf, emSize, style, unit); 3142 } 3143 3144 // 3145 // This is a somewhat internal function. 3146 this(ref LogFont lf, float emSize, FontStyle style, GraphicsUnit unit) { 3147 _unit = unit; 3148 3149 lf.lf.lfHeight = -getLfHeight(emSize, unit); 3150 _style(lf, style); 3151 3152 this(_create(lf)); 3153 3154 _fstyle = style; 3155 _initLf(lf); 3156 } 3157 3158 ~this() { 3159 if (owned) { 3160 DeleteObject(hf); 3161 } 3162 } 3163 3164 final @property HFONT handle() { 3165 return hf; 3166 } 3167 3168 final @property GraphicsUnit unit() { 3169 return _unit; 3170 } 3171 3172 final @property float size() { 3173 /+ 3174 LOGFONTA lf; 3175 _info(&lf); 3176 return getEmSize(lf.lf.lfHeight, _unit); 3177 +/ 3178 return getEmSize(this.lfHeight, _unit); 3179 } 3180 3181 final float getSize(GraphicsUnit unit) { 3182 /+ 3183 LOGFONTA lf; 3184 _info(&lf); 3185 return getEmSize(lf.lf.lfHeight, unit); 3186 +/ 3187 return getEmSize(this.lfHeight, unit); 3188 } 3189 3190 final float getSize(GraphicsUnit unit, Graphics g) { 3191 return getEmSize(g.handle, this.lfHeight, unit); 3192 } 3193 3194 final @property FontStyle style() { 3195 return _fstyle; 3196 } 3197 3198 final @property Dstring name() { 3199 return lfName; 3200 } 3201 3202 final @property ubyte gdiCharSet() { 3203 return lfCharSet; 3204 } 3205 3206 /+ 3207 private void _initLf(LOGFONTA* lf) { 3208 this.lfHeight = lf.lfHeight; 3209 this.lfName = stringFromStringz(lf.lfFaceName.ptr).dup; 3210 this.lfCharSet = lf.lfCharSet; 3211 } 3212 +/ 3213 3214 private void _initLf(ref LogFont lf) { 3215 this.lfHeight = lf.lf.lfHeight; 3216 this.lfName = lf.faceName; 3217 this.lfCharSet = lf.lf.lfCharSet; 3218 } 3219 3220 /+ 3221 private void _initLf(Font otherfont, LOGFONTA* lf) { 3222 this.lfHeight = otherfont.lfHeight; 3223 this.lfName = otherfont.lfName; 3224 this.lfCharSet = otherfont.lfCharSet; 3225 } 3226 +/ 3227 3228 private void _initLf(Font otherfont, ref LogFont lf) { 3229 this.lfHeight = otherfont.lfHeight; 3230 this.lfName = otherfont.lfName; 3231 this.lfCharSet = otherfont.lfCharSet; 3232 } 3233 3234 private: 3235 HFONT hf; 3236 GraphicsUnit _unit; 3237 bool owned = true; 3238 FontStyle _fstyle; 3239 3240 LONG lfHeight; 3241 Dstring lfName; 3242 ubyte lfCharSet; 3243 } 3244 3245 enum PenStyle : UINT { 3246 SOLID = PS_SOLID, /// 3247 DASH = PS_DASH, 3248 DOT = PS_DOT, 3249 DASH_DOT = PS_DASHDOT, 3250 DASH_DOT_DOT = PS_DASHDOTDOT, 3251 NULL = PS_NULL, 3252 INSIDE_FRAME = PS_INSIDEFRAME, 3253 } 3254 3255 // If the pen width is greater than 1 the style cannot have dashes or dots. 3256 class Pen { 3257 // Used internally. 3258 this(HPEN hp, bool owned = true) { 3259 this.hp = hp; 3260 this.owned = owned; 3261 } 3262 3263 this(Color color, int width = 1, PenStyle ps = PenStyle.SOLID) { 3264 hp = CreatePen(ps, width, color.toRgb()); 3265 } 3266 3267 ~this() { 3268 if (owned) { 3269 DeleteObject(hp); 3270 } 3271 } 3272 3273 final @property HPEN handle() { 3274 return hp; 3275 } 3276 3277 private: 3278 HPEN hp; 3279 bool owned = true; 3280 } 3281 3282 class Brush { 3283 // Used internally. 3284 this(HBRUSH hb, bool owned = true) { 3285 this.hb = hb; 3286 this.owned = owned; 3287 } 3288 3289 protected this() { 3290 } 3291 3292 ~this() { 3293 if (owned) { 3294 DeleteObject(hb); 3295 } 3296 } 3297 3298 final @property HBRUSH handle() { 3299 return hb; 3300 } 3301 3302 private: 3303 HBRUSH hb; 3304 bool owned = true; 3305 } 3306 3307 class SolidBrush : Brush { 3308 3309 this(Color c) { 3310 super(CreateSolidBrush(c.toRgb())); 3311 } 3312 3313 /+ 3314 final @property void color(Color c) { 3315 // delete.. 3316 super.hb = CreateSolidBrush(c.toRgb()); 3317 } 3318 +/ 3319 3320 final @property Color color() { 3321 Color result; 3322 LOGBRUSH lb; 3323 3324 if (GetObjectA(hb, lb.sizeof, &lb)) { 3325 result = Color.fromRgb(lb.lbColor); 3326 } 3327 3328 return result; 3329 } 3330 } 3331 3332 // PatternBrush has the win9x/ME limitation of not supporting images larger than 8x8 pixels. 3333 // TextureBrush supports any size images but requires GDI+. 3334 3335 /+ 3336 class PatternBrush: Brush { 3337 //CreatePatternBrush() ... 3338 } 3339 +/ 3340 3341 /+ 3342 class TextureBrush: Brush { 3343 // GDI+ ... 3344 } 3345 +/ 3346 3347 enum HatchStyle : LONG { 3348 HORIZONTAL = HS_HORIZONTAL, /// 3349 VERTICAL = HS_VERTICAL, 3350 FORWARD_DIAGONAL = HS_FDIAGONAL, 3351 BACKWARD_DIAGONAL = HS_BDIAGONAL, 3352 CROSS = HS_CROSS, 3353 DIAGONAL_CROSS = HS_DIAGCROSS, 3354 } 3355 3356 class HatchBrush : Brush { 3357 3358 this(HatchStyle hs, Color c) { 3359 super(CreateHatchBrush(hs, c.toRgb())); 3360 } 3361 3362 final @property Color foregroundColor() { 3363 Color result; 3364 LOGBRUSH lb; 3365 3366 if (GetObjectA(hb, lb.sizeof, &lb)) { 3367 result = Color.fromRgb(lb.lbColor); 3368 } 3369 3370 return result; 3371 } 3372 3373 final @property HatchStyle hatchStyle() { 3374 HatchStyle result; 3375 LOGBRUSH lb; 3376 3377 if (GetObjectA(hb, lb.sizeof, &lb)) { 3378 result = cast(HatchStyle) lb.lbHatch; 3379 } 3380 3381 return result; 3382 } 3383 } 3384 3385 class Region { 3386 // Used internally. 3387 this(HRGN hrgn, bool owned = true) { 3388 this.hrgn = hrgn; 3389 this.owned = owned; 3390 } 3391 3392 ~this() { 3393 if (owned) { 3394 DeleteObject(hrgn); 3395 } 3396 } 3397 3398 final @property HRGN handle() { 3399 return hrgn; 3400 } 3401 3402 override Dequ opEquals(Object o) { 3403 Region rgn = cast(Region) o; 3404 if (!rgn) { 3405 return 0; // Not equal. 3406 } 3407 return opEquals(rgn); 3408 } 3409 3410 Dequ opEquals(Region rgn) { 3411 return hrgn == rgn.hrgn; 3412 } 3413 3414 private HRGN hrgn; 3415 private bool owned = true; 3416 } 3417 3418 // from wincom 3419 private LONG MAP_LOGHIM_TO_PIX(LONG x, LONG logpixels) { 3420 return MulDiv(logpixels, x, 2540); 3421 }