1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 5 /// 6 module dfl.imagelist; 7 8 import dfl.base, dfl.drawing, dfl.internal.winapi; 9 import dfl.collections; 10 11 12 version(DFL_NO_IMAGELIST) 13 { 14 } 15 else 16 { 17 /// 18 class ImageList // docmain 19 { 20 /// 21 class ImageCollection 22 { 23 protected this() 24 { 25 } 26 27 28 void insert(int index, Image img) 29 { 30 if(index >= _images.length) 31 { 32 add(img); 33 } 34 else 35 { 36 assert(0, "Must add images to the end of the image list"); 37 } 38 } 39 40 41 final void addStrip(Image img) 42 { 43 HGDIOBJ hgo; 44 if(1 != img._imgtype(&hgo)) 45 { 46 debug 47 { 48 assert(0, "Image list: addStrip needs bitmap"); 49 } 50 else 51 { 52 _unableimg(); 53 } 54 } 55 56 auto sz = imageSize; 57 if(img.height != sz.height 58 || img.width % sz.width) 59 { 60 debug 61 { 62 assert(0, "Image list: invalid image size"); 63 } 64 else 65 { 66 _unableimg(); 67 } 68 } 69 int num = img.width / sz.width; 70 71 /+ 72 if(1 == num) 73 { 74 add(img); 75 return; 76 } 77 +/ 78 79 auto _hdl = handle; // _addhbitmap needs the handle! Could avoid this in the future. 80 _addhbitmap(hgo); 81 82 int x = 0; 83 for(; num; num--) 84 { 85 auto sp = new StripPart(); 86 sp.origImg = img; 87 sp.hbm = hgo; 88 sp.partBounds = Rect(x, 0, sz.width, sz.height); 89 90 _images ~= sp; 91 92 x += sz.width; 93 } 94 } 95 96 97 package: 98 99 Image[] _images; 100 101 102 static class StripPart: Image 103 { 104 override @property Size size() // getter 105 { 106 return partBounds.size; 107 } 108 109 110 override void draw(Graphics g, Point pt) 111 { 112 HDC memdc; 113 memdc = CreateCompatibleDC(g.handle); 114 try 115 { 116 HGDIOBJ hgo; 117 hgo = SelectObject(memdc, hbm); 118 BitBlt(g.handle, pt.x, pt.y, partBounds.width, partBounds.height, memdc, partBounds.x, partBounds.y, SRCCOPY); 119 SelectObject(memdc, hgo); // Old bitmap. 120 } 121 finally 122 { 123 DeleteDC(memdc); 124 } 125 } 126 127 128 override void drawStretched(Graphics g, Rect r) 129 { 130 HDC memdc; 131 memdc = CreateCompatibleDC(g.handle); 132 try 133 { 134 HGDIOBJ hgo; 135 int lstretch; 136 hgo = SelectObject(memdc, hbm); 137 lstretch = SetStretchBltMode(g.handle, COLORONCOLOR); 138 StretchBlt(g.handle, r.x, r.y, r.width, r.height, 139 memdc, partBounds.x, partBounds.y, partBounds.width, partBounds.height, SRCCOPY); 140 SetStretchBltMode(g.handle, lstretch); 141 SelectObject(memdc, hgo); // Old bitmap. 142 } 143 finally 144 { 145 DeleteDC(memdc); 146 } 147 } 148 149 150 Image origImg; // Hold this so the HBITMAP doesn't get collected. 151 HBITMAP hbm; 152 Rect partBounds; 153 } 154 155 156 void _adding(size_t idx, Image val) 157 { 158 assert(val !is null); 159 160 switch(val._imgtype(null)) 161 { 162 case 1: 163 case 2: 164 break; 165 default: 166 debug 167 { 168 assert(0, "Image list: invalid image type"); 169 } 170 else 171 { 172 _unableimg(); 173 } 174 } 175 176 if(val.size != imageSize) 177 { 178 debug 179 { 180 assert(0, "Image list: invalid image size"); 181 } 182 else 183 { 184 _unableimg(); 185 } 186 } 187 } 188 189 190 void _added(size_t idx, Image val) 191 { 192 if(isHandleCreated) 193 { 194 //if(idx >= _images.length) // Can't test for this here because -val- is already added to the array. 195 _addimg(val); 196 } 197 } 198 199 200 void _removed(size_t idx, Image val) 201 { 202 if(isHandleCreated) 203 { 204 if(size_t.max == idx) // Clear all. 205 { 206 imageListRemove(handle, -1); 207 } 208 else 209 { 210 imageListRemove(handle, idx); 211 } 212 } 213 } 214 215 216 public: 217 218 mixin ListWrapArray!(Image, _images, 219 _adding, _added, 220 _blankListCallback!(Image), _removed, 221 false, false, false); 222 } 223 224 225 this() 226 { 227 InitCommonControls(); 228 229 _cimages = new ImageCollection(); 230 _transcolor = Color.transparent; 231 } 232 233 234 /// 235 final @property void colorDepth(ColorDepth depth) // setter 236 { 237 assert(!isHandleCreated); 238 239 this._depth = depth; 240 } 241 242 /// ditto 243 final @property ColorDepth colorDepth() // getter 244 { 245 return _depth; 246 } 247 248 249 /// 250 final @property void transparentColor(Color tc) // setter 251 { 252 assert(!isHandleCreated); 253 254 _transcolor = tc; 255 } 256 257 /// ditto 258 final @property Color transparentColor() // getter 259 { 260 return _transcolor; 261 } 262 263 264 /// 265 final @property void imageSize(Size sz) // setter 266 { 267 assert(!isHandleCreated); 268 269 assert(sz.width && sz.height); 270 271 _w = sz.width; 272 _h = sz.height; 273 } 274 275 /// ditto 276 final @property Size imageSize() // getter 277 { 278 return Size(_w, _h); 279 } 280 281 282 /// 283 final @property ImageCollection images() // getter 284 { 285 return _cimages; 286 } 287 288 289 /// 290 final @property void tag(Object t) // setter 291 { 292 this._tag = t; 293 } 294 295 /// ditto 296 final @property Object tag() // getter 297 { 298 return this._tag; 299 } 300 301 302 /+ // Actually, forget about these; just draw with the actual images. 303 /// 304 final void draw(Graphics g, Point pt, int index) 305 { 306 return draw(g, pt.x, pt.y, index); 307 } 308 309 /// ditto 310 final void draw(Graphics g, int x, int y, int index) 311 { 312 imageListDraw(handle, index, g.handle, x, y, ILD_NORMAL); 313 } 314 315 /// ditto 316 // stretch 317 final void draw(Graphics g, int x, int y, int width, int height, int index) 318 { 319 // ImageList_DrawEx operates differently if the width or height is zero 320 // so bail out if zero and pretend the zero size image was drawn. 321 if(!width) 322 return; 323 if(!height) 324 return; 325 326 imageListDrawEx(handle, index, g.handle, x, y, width, height, 327 CLR_NONE, CLR_NONE, ILD_NORMAL); // ? 328 } 329 +/ 330 331 332 /// 333 final @property bool isHandleCreated() // getter 334 { 335 return HIMAGELIST.init != _hil; 336 } 337 338 deprecated alias isHandleCreated handleCreated; 339 340 341 /// 342 final @property HIMAGELIST handle() // getter 343 { 344 if(!isHandleCreated) 345 _createimagelist(); 346 return _hil; 347 } 348 349 350 /// 351 void dispose() 352 { 353 return dispose(true); 354 } 355 356 /// ditto 357 void dispose(bool disposing) 358 { 359 if(isHandleCreated) 360 imageListDestroy(_hil); 361 _hil = HIMAGELIST.init; 362 363 if(disposing) 364 { 365 //_cimages._images = null; // Not GC-safe in dtor. 366 //_cimages = null; // Could cause bad things. 367 } 368 } 369 370 371 ~this() 372 { 373 dispose(); 374 } 375 376 377 private: 378 379 ColorDepth _depth = ColorDepth.DEPTH_8BIT; 380 Color _transcolor; 381 ImageCollection _cimages; 382 HIMAGELIST _hil; 383 int _w = 16, _h = 16; 384 Object _tag; 385 386 387 void _createimagelist() 388 { 389 if(isHandleCreated) 390 { 391 imageListDestroy(_hil); 392 _hil = HIMAGELIST.init; 393 } 394 395 UINT flags = ILC_MASK; 396 switch(_depth) 397 { 398 case ColorDepth.DEPTH_4BIT: flags |= ILC_COLOR4; break; 399 default: case ColorDepth.DEPTH_8BIT: flags |= ILC_COLOR8; break; 400 case ColorDepth.DEPTH_16BIT: flags |= ILC_COLOR16; break; 401 case ColorDepth.DEPTH_24BIT: flags |= ILC_COLOR24; break; 402 case ColorDepth.DEPTH_32BIT: flags |= ILC_COLOR32; break; 403 } 404 405 // Note: cGrow is not a limit, but how many images to preallocate each grow. 406 _hil = imageListCreate(_w, _h, flags, _cimages._images.length, 4 + _cimages._images.length / 4); 407 if(!_hil) 408 throw new DflException("Unable to create image list"); 409 410 foreach(img; _cimages._images) 411 { 412 _addimg(img); 413 } 414 } 415 416 417 void _unableimg() 418 { 419 throw new DflException("Unable to add image to image list"); 420 } 421 422 423 int _addimg(Image img) 424 { 425 assert(isHandleCreated); 426 427 HGDIOBJ hgo; 428 int result; 429 switch(img._imgtype(&hgo)) 430 { 431 case 1: 432 result = _addhbitmap(hgo); 433 break; 434 435 case 2: 436 result = imageListAddIcon(_hil, cast(HICON)hgo); 437 break; 438 439 default: 440 result = -1; 441 } 442 443 //if(-1 == result) 444 // _unableimg(); 445 return result; 446 } 447 448 int _addhbitmap(HBITMAP hbm) 449 { 450 assert(isHandleCreated); 451 452 COLORREF cr; 453 if(_transcolor == Color.empty 454 || _transcolor == Color.transparent) 455 { 456 cr = CLR_NONE; // ? 457 } 458 else 459 { 460 cr = _transcolor.toRgb(); 461 } 462 return imageListAddMasked(_hil, cast(HBITMAP)hbm, cr); 463 } 464 } 465 466 467 private extern(Windows) 468 { 469 // This was the only way I could figure out how to use the current actctx (Windows issue). 470 471 HIMAGELIST imageListCreate( 472 int cx, int cy, UINT flags, int cInitial, int cGrow) 473 { 474 alias typeof(&ImageList_Create) TProc; 475 static TProc proc = null; 476 if(!proc) 477 proc = cast(typeof(proc))GetProcAddress(GetModuleHandleA("comctl32.dll"), "ImageList_Create"); 478 return proc(cx, cy, flags, cInitial, cGrow); 479 } 480 481 int imageListAddIcon( 482 HIMAGELIST himl, HICON hicon) 483 { 484 alias typeof(&ImageList_AddIcon) TProc; 485 static TProc proc = null; 486 if(!proc) 487 proc = cast(typeof(proc))GetProcAddress(GetModuleHandleA("comctl32.dll"), "ImageList_AddIcon"); 488 return proc(himl, hicon); 489 } 490 491 int imageListAddMasked( 492 HIMAGELIST himl, HBITMAP hbmImage, COLORREF crMask) 493 { 494 alias typeof(&ImageList_AddMasked) TProc; 495 static TProc proc = null; 496 if(!proc) 497 proc = cast(typeof(proc))GetProcAddress(GetModuleHandleA("comctl32.dll"), "ImageList_AddMasked"); 498 return proc(himl, hbmImage, crMask); 499 } 500 501 BOOL imageListRemove( 502 HIMAGELIST himl, int i) 503 { 504 alias typeof(&ImageList_Remove) TProc; 505 static TProc proc = null; 506 if(!proc) 507 proc = cast(typeof(proc))GetProcAddress(GetModuleHandleA("comctl32.dll"), "ImageList_Remove"); 508 return proc(himl, i); 509 } 510 511 BOOL imageListDestroy( 512 HIMAGELIST himl) 513 { 514 alias typeof(&ImageList_Destroy) TProc; 515 static TProc proc = null; 516 if(!proc) 517 proc = cast(typeof(proc))GetProcAddress(GetModuleHandleA("comctl32.dll"), "ImageList_Destroy"); 518 return proc(himl); 519 } 520 } 521 } 522