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