wxRTC table layout now uses cell content to calculate column widths if no other width...
[wxWidgets.git] / src / msw / imaglist.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/imaglist.cpp
3 // Purpose: wxImageList implementation for Win32
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #ifndef WX_PRECOMP
27 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
28 #include "wx/window.h"
29 #include "wx/icon.h"
30 #include "wx/dc.h"
31 #include "wx/string.h"
32 #include "wx/dcmemory.h"
33 #include "wx/intl.h"
34 #include "wx/log.h"
35 #include "wx/image.h"
36 #include <stdio.h>
37 #endif
38
39 #include "wx/imaglist.h"
40 #include "wx/dc.h"
41 #include "wx/msw/dc.h"
42 #include "wx/msw/dib.h"
43 #include "wx/msw/private.h"
44
45 // ----------------------------------------------------------------------------
46 // wxWin macros
47 // ----------------------------------------------------------------------------
48
49 IMPLEMENT_DYNAMIC_CLASS(wxImageList, wxObject)
50
51 #define GetHImageList() ((HIMAGELIST)m_hImageList)
52
53 // ----------------------------------------------------------------------------
54 // private functions
55 // ----------------------------------------------------------------------------
56
57 // returns the mask if it's valid, otherwise the bitmap mask and, if it's not
58 // valid neither, a "solid" mask (no transparent zones at all)
59 static HBITMAP GetMaskForImage(const wxBitmap& bitmap, const wxBitmap& mask);
60
61 // ============================================================================
62 // implementation
63 // ============================================================================
64
65 // ----------------------------------------------------------------------------
66 // wxImageList creation/destruction
67 // ----------------------------------------------------------------------------
68
69 wxImageList::wxImageList()
70 {
71 m_hImageList = 0;
72 }
73
74 // Creates an image list
75 bool wxImageList::Create(int width, int height, bool mask, int initial)
76 {
77 UINT flags = 0;
78
79 // as we want to be able to use 32bpp bitmaps in the image lists, we always
80 // use ILC_COLOR32, even if the display resolution is less -- the system
81 // will make the best effort to show the bitmap if we do this resulting in
82 // quite acceptable display while using a lower depth ILC_COLOR constant
83 // (e.g. ILC_COLOR16) shows completely broken bitmaps
84 #ifdef __WXWINCE__
85 flags |= ILC_COLOR;
86 #else
87 flags |= ILC_COLOR32;
88 #endif
89
90 if ( mask )
91 flags |= ILC_MASK;
92
93 // Grow by 1, I guess this is reasonable behaviour most of the time
94 m_hImageList = (WXHIMAGELIST) ImageList_Create(width, height, flags,
95 initial, 1);
96 if ( !m_hImageList )
97 {
98 wxLogLastError(wxT("ImageList_Create()"));
99 }
100
101 return m_hImageList != 0;
102 }
103
104 wxImageList::~wxImageList()
105 {
106 if ( m_hImageList )
107 {
108 ImageList_Destroy(GetHImageList());
109 m_hImageList = 0;
110 }
111 }
112
113 // ----------------------------------------------------------------------------
114 // wxImageList attributes
115 // ----------------------------------------------------------------------------
116
117 // Returns the number of images in the image list.
118 int wxImageList::GetImageCount() const
119 {
120 wxASSERT_MSG( m_hImageList, wxT("invalid image list") );
121
122 return ImageList_GetImageCount(GetHImageList());
123 }
124
125 // Returns the size (same for all images) of the images in the list
126 bool wxImageList::GetSize(int WXUNUSED(index), int &width, int &height) const
127 {
128 wxASSERT_MSG( m_hImageList, wxT("invalid image list") );
129
130 return ImageList_GetIconSize(GetHImageList(), &width, &height) != 0;
131 }
132
133 // ----------------------------------------------------------------------------
134 // wxImageList operations
135 // ----------------------------------------------------------------------------
136
137 // Adds a bitmap, and optionally a mask bitmap.
138 // Note that wxImageList creates new bitmaps, so you may delete
139 // 'bitmap' and 'mask'.
140 int wxImageList::Add(const wxBitmap& bitmap, const wxBitmap& mask)
141 {
142 HBITMAP hbmp;
143
144 #if wxUSE_WXDIB && wxUSE_IMAGE
145 // wxBitmap normally stores alpha in pre-multiplied format but
146 // ImageList_Draw() does pre-multiplication internally so we need to undo
147 // the pre-multiplication here. Converting back and forth like this is, of
148 // course, very inefficient but it's better than wrong appearance so we do
149 // this for now until a better way can be found.
150 AutoHBITMAP hbmpRelease;
151 if ( bitmap.HasAlpha() )
152 {
153 hbmp = wxDIB(bitmap.ConvertToImage(),
154 wxDIB::PixelFormat_NotPreMultiplied).Detach();
155 hbmpRelease.Init(hbmp);
156 }
157 else
158 #endif // wxUSE_WXDIB && wxUSE_IMAGE
159 hbmp = GetHbitmapOf(bitmap);
160
161 AutoHBITMAP hbmpMask(GetMaskForImage(bitmap, mask));
162
163 int index = ImageList_Add(GetHImageList(), hbmp, hbmpMask);
164 if ( index == -1 )
165 {
166 wxLogError(_("Couldn't add an image to the image list."));
167 }
168
169 return index;
170 }
171
172 // Adds a bitmap, using the specified colour to create the mask bitmap
173 // Note that wxImageList creates new bitmaps, so you may delete
174 // 'bitmap'.
175 int wxImageList::Add(const wxBitmap& bitmap, const wxColour& maskColour)
176 {
177 HBITMAP hbmp;
178
179 #if wxUSE_WXDIB && wxUSE_IMAGE
180 // See the comment in overloaded Add() above.
181 AutoHBITMAP hbmpRelease;
182 if ( bitmap.HasAlpha() )
183 {
184 hbmp = wxDIB(bitmap.ConvertToImage(),
185 wxDIB::PixelFormat_NotPreMultiplied).Detach();
186 hbmpRelease.Init(hbmp);
187 }
188 else
189 #endif // wxUSE_WXDIB && wxUSE_IMAGE
190 hbmp = GetHbitmapOf(bitmap);
191
192 int index = ImageList_AddMasked(GetHImageList(),
193 hbmp,
194 wxColourToRGB(maskColour));
195 if ( index == -1 )
196 {
197 wxLogError(_("Couldn't add an image to the image list."));
198 }
199
200 return index;
201 }
202
203 // Adds a bitmap and mask from an icon.
204 int wxImageList::Add(const wxIcon& icon)
205 {
206 int index = ImageList_AddIcon(GetHImageList(), GetHiconOf(icon));
207 if ( index == -1 )
208 {
209 wxLogError(_("Couldn't add an image to the image list."));
210 }
211
212 return index;
213 }
214
215 // Replaces a bitmap, optionally passing a mask bitmap.
216 // Note that wxImageList creates new bitmaps, so you may delete
217 // 'bitmap' and 'mask'.
218 bool wxImageList::Replace(int index,
219 const wxBitmap& bitmap,
220 const wxBitmap& mask)
221 {
222 HBITMAP hbmp;
223
224 #if wxUSE_WXDIB && wxUSE_IMAGE
225 // See the comment in Add() above.
226 AutoHBITMAP hbmpRelease;
227 if ( bitmap.HasAlpha() )
228 {
229 hbmp = wxDIB(bitmap.ConvertToImage(),
230 wxDIB::PixelFormat_NotPreMultiplied).Detach();
231 hbmpRelease.Init(hbmp);
232 }
233 else
234 #endif // wxUSE_WXDIB && wxUSE_IMAGE
235 hbmp = GetHbitmapOf(bitmap);
236
237 AutoHBITMAP hbmpMask(GetMaskForImage(bitmap, mask));
238
239 if ( !ImageList_Replace(GetHImageList(), index, hbmp, hbmpMask) )
240 {
241 wxLogLastError(wxT("ImageList_Replace()"));
242 return false;
243 }
244
245 return true;
246 }
247
248 // Replaces a bitmap and mask from an icon.
249 bool wxImageList::Replace(int i, const wxIcon& icon)
250 {
251 bool ok = ImageList_ReplaceIcon(GetHImageList(), i, GetHiconOf(icon)) != -1;
252 if ( !ok )
253 {
254 wxLogLastError(wxT("ImageList_ReplaceIcon()"));
255 }
256
257 return ok;
258 }
259
260 // Removes the image at the given index.
261 bool wxImageList::Remove(int index)
262 {
263 bool ok = ImageList_Remove(GetHImageList(), index) != 0;
264 if ( !ok )
265 {
266 wxLogLastError(wxT("ImageList_Remove()"));
267 }
268
269 return ok;
270 }
271
272 // Remove all images
273 bool wxImageList::RemoveAll()
274 {
275 // don't use ImageList_RemoveAll() because mingw32 headers don't have it
276 return Remove(-1);
277 }
278
279 // Draws the given image on a dc at the specified position.
280 // If 'solidBackground' is true, Draw sets the image list background
281 // colour to the background colour of the wxDC, to speed up
282 // drawing by eliminating masked drawing where possible.
283 bool wxImageList::Draw(int index,
284 wxDC& dc,
285 int x, int y,
286 int flags,
287 bool solidBackground)
288 {
289 wxDCImpl *impl = dc.GetImpl();
290 wxMSWDCImpl *msw_impl = wxDynamicCast( impl, wxMSWDCImpl );
291 if (!msw_impl)
292 return false;
293
294 HDC hDC = GetHdcOf(*msw_impl);
295 wxCHECK_MSG( hDC, false, wxT("invalid wxDC in wxImageList::Draw") );
296
297 COLORREF clr = CLR_NONE; // transparent by default
298 if ( solidBackground )
299 {
300 const wxBrush& brush = dc.GetBackground();
301 if ( brush.IsOk() )
302 {
303 clr = wxColourToRGB(brush.GetColour());
304 }
305 }
306
307 ImageList_SetBkColor(GetHImageList(), clr);
308
309 UINT style = 0;
310 if ( flags & wxIMAGELIST_DRAW_NORMAL )
311 style |= ILD_NORMAL;
312 if ( flags & wxIMAGELIST_DRAW_TRANSPARENT )
313 style |= ILD_TRANSPARENT;
314 if ( flags & wxIMAGELIST_DRAW_SELECTED )
315 style |= ILD_SELECTED;
316 if ( flags & wxIMAGELIST_DRAW_FOCUSED )
317 style |= ILD_FOCUS;
318
319 bool ok = ImageList_Draw(GetHImageList(), index, hDC, x, y, style) != 0;
320 if ( !ok )
321 {
322 wxLogLastError(wxT("ImageList_Draw()"));
323 }
324
325 return ok;
326 }
327
328 // Get the bitmap
329 wxBitmap wxImageList::GetBitmap(int index) const
330 {
331 #if wxUSE_WXDIB && wxUSE_IMAGE
332 int bmp_width = 0, bmp_height = 0;
333 GetSize(index, bmp_width, bmp_height);
334
335 wxBitmap bitmap(bmp_width, bmp_height);
336 wxMemoryDC dc;
337 dc.SelectObject(bitmap);
338
339 // draw it the first time to find a suitable mask colour
340 ((wxImageList*)this)->Draw(index, dc, 0, 0, wxIMAGELIST_DRAW_TRANSPARENT);
341 dc.SelectObject(wxNullBitmap);
342
343 // find the suitable mask colour
344 wxImage image = bitmap.ConvertToImage();
345 unsigned char r = 0, g = 0, b = 0;
346 image.FindFirstUnusedColour(&r, &g, &b);
347
348 // redraw whole image and bitmap in the mask colour
349 image.Create(bmp_width, bmp_height);
350 image.Replace(0, 0, 0, r, g, b);
351 bitmap = wxBitmap(image);
352
353 // redraw icon over the mask colour to actually draw it
354 dc.SelectObject(bitmap);
355 ((wxImageList*)this)->Draw(index, dc, 0, 0, wxIMAGELIST_DRAW_TRANSPARENT);
356 dc.SelectObject(wxNullBitmap);
357
358 // get the image, set the mask colour and convert back to get transparent bitmap
359 image = bitmap.ConvertToImage();
360 image.SetMaskColour(r, g, b);
361 bitmap = wxBitmap(image);
362 #else
363 wxBitmap bitmap;
364 #endif
365 return bitmap;
366 }
367
368 // Get the icon
369 wxIcon wxImageList::GetIcon(int index) const
370 {
371 HICON hIcon = ImageList_ExtractIcon(0, GetHImageList(), index);
372 if (hIcon)
373 {
374 wxIcon icon;
375 icon.SetHICON((WXHICON)hIcon);
376
377 int iconW, iconH;
378 GetSize(index, iconW, iconH);
379 icon.SetSize(iconW, iconH);
380
381 return icon;
382 }
383 else
384 return wxNullIcon;
385 }
386
387 // ----------------------------------------------------------------------------
388 // helpers
389 // ----------------------------------------------------------------------------
390
391 static HBITMAP GetMaskForImage(const wxBitmap& bitmap, const wxBitmap& mask)
392 {
393 #if wxUSE_IMAGE
394 wxBitmap bitmapWithMask;
395 #endif // wxUSE_IMAGE
396
397 HBITMAP hbmpMask;
398 wxMask *pMask;
399 bool deleteMask = false;
400
401 if ( mask.IsOk() )
402 {
403 hbmpMask = GetHbitmapOf(mask);
404 pMask = NULL;
405 }
406 else
407 {
408 pMask = bitmap.GetMask();
409
410 #if wxUSE_IMAGE
411 // check if we don't have alpha in this bitmap -- we can create a mask
412 // from it (and we need to do it for the older systems which don't
413 // support 32bpp bitmaps natively)
414 if ( !pMask )
415 {
416 wxImage img(bitmap.ConvertToImage());
417 if ( img.HasAlpha() )
418 {
419 img.ConvertAlphaToMask();
420 bitmapWithMask = wxBitmap(img);
421 pMask = bitmapWithMask.GetMask();
422 }
423 }
424 #endif // wxUSE_IMAGE
425
426 if ( !pMask )
427 {
428 // use the light grey count as transparent: the trouble here is
429 // that the light grey might have been changed by Windows behind
430 // our back, so use the standard colour map to get its real value
431 wxCOLORMAP *cmap = wxGetStdColourMap();
432 wxColour col;
433 wxRGBToColour(col, cmap[wxSTD_COL_BTNFACE].from);
434
435 pMask = new wxMask(bitmap, col);
436
437 deleteMask = true;
438 }
439
440 hbmpMask = (HBITMAP)pMask->GetMaskBitmap();
441 }
442
443 // windows mask convention is opposite to the wxWidgets one
444 HBITMAP hbmpMaskInv = wxInvertMask(hbmpMask);
445
446 if ( deleteMask )
447 {
448 delete pMask;
449 }
450
451 return hbmpMaskInv;
452 }