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