OSDN Git Service

イニシャルコミット。
[marathon/ShapeFusion.git] / Shapes / BitmapBrowser.cpp
1 /*
2  * This file is part of ShapeFusion (Copyright 2000 Tito Dal Canton)
3  *
4  * ShapeFusion is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * ShapeFusion is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with ShapeFusion; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 #include "wx/image.h"
19 #include "BitmapBrowser.h"
20 #include "utilities.h"
21
22 DEFINE_EVENT_TYPE(wxEVT_BITMAPBROWSER)
23 DEFINE_EVENT_TYPE(wxEVT_BITMAPBROWSER_DELETE)
24
25 BEGIN_EVENT_TABLE(BitmapBrowser, wxScrolledWindow)
26         EVT_PAINT(BitmapBrowser::OnPaint)
27         EVT_SIZE(BitmapBrowser::OnSize)
28         EVT_LEFT_DOWN(BitmapBrowser::OnMouseDown)
29         EVT_RIGHT_DOWN(BitmapBrowser::OnMouseDown)
30         EVT_KEY_DOWN(BitmapBrowser::OnKeyDown)
31 END_EVENT_TABLE()
32
33 BitmapBrowser::BitmapBrowser(wxWindow *parent, wxWindowID id):
34         wxScrolledWindow(parent, id, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER | wxFULL_REPAINT_ON_RESIZE),
35         mColorTable(NULL), mSelection(-1), mNumCols(0), mNumRows(0), mFrozenCount(0)
36 {
37         SetBackgroundColour(wxColour(255, 255, 255));
38         mThumbnailPen.SetColour(200, 200, 200);
39         mSelectionPen.SetColour(0, 0, 0);
40         mSelectionPen.SetWidth(3);
41         SetScrollRate(0, 2);
42         mThumbnailSize = 64;
43         mMargin = 7;
44         mWhiteTransparency = true;
45         mAutoSize = false;
46 }
47
48 void BitmapBrowser::OnPaint(wxPaintEvent& e)
49 {
50         wxPaintDC   tempdc(this);
51         int                     cw, ch, rx, ry;
52
53         DoPrepareDC(tempdc);
54         GetClientSize(&cw, &ch);
55         CalcUnscrolledPosition(0, 0, &rx, &ry);
56         // draw thumbnails
57         tempdc.SetPen(mThumbnailPen);
58         tempdc.SetBrush(*wxTRANSPARENT_BRUSH);
59         for (int i = 0; i < (int)mBitmaps.size(); i++) {
60                 int     x = mThumbnailPositions[i].x,
61                         y = mThumbnailPositions[i].y;
62
63                 if (y + mThumbnailSize < ry)
64                         continue;
65                 if (y > ry + ch)
66                         break;
67                         
68                 int     bw = mThumbnails[i].GetWidth(),
69                         bh = mThumbnails[i].GetHeight();
70
71                 if (i == mSelection) {
72                         tempdc.DrawBitmap(mThumbnails[i], x + mThumbnailSize/2 - bw/2, y + mThumbnailSize/2 - bh/2);
73                         tempdc.SetPen(mSelectionPen);
74                         tempdc.DrawRectangle(x-2, y-2, mThumbnailSize+4, mThumbnailSize+4);
75                         tempdc.SetPen(mThumbnailPen);
76                 } else {
77                         tempdc.DrawRectangle(x-1, y-1, mThumbnailSize+2, mThumbnailSize+2);
78                         tempdc.DrawBitmap(mThumbnails[i], x + mThumbnailSize/2 - bw/2, y + mThumbnailSize/2 - bh/2);
79                 }
80         }
81 }
82
83 // widget resized, recalculate virtual size to correctly wrap thumbnails
84 void BitmapBrowser::OnSize(wxSizeEvent& e)
85 {
86         UpdateVirtualSize();
87 }
88
89 // handle clicks received by the widget
90 void BitmapBrowser::OnMouseDown(wxMouseEvent& e)
91 {
92         wxClientDC      dc(this);
93         wxPoint         mouse;
94
95         DoPrepareDC(dc);
96         mouse = e.GetLogicalPosition(dc);
97         switch (e.GetButton()) {
98                 case wxMOUSE_BTN_LEFT:
99                         // handle bitmap selection
100                         {
101                                 int     new_selection = -1;
102
103                                 for (unsigned int i = 0; i < mThumbnailPositions.size(); i++) {
104                                         wxRect  test(mThumbnailPositions[i].x, mThumbnailPositions[i].y, mThumbnailSize, mThumbnailSize);
105
106                                         if (test.Contains(mouse)) {
107                                                 new_selection = i;
108                                                 break;
109                                         }
110                                 }
111                                 if (new_selection != mSelection) {
112                                         mSelection = new_selection;
113                                         Refresh();
114
115                                         // send selection event
116                                         wxCommandEvent  event(wxEVT_BITMAPBROWSER, GetId());
117
118                                         event.SetEventObject(this);
119                                         event.SetInt(mSelection);
120                                         GetEventHandler()->ProcessEvent(event);
121                                 }
122                         }
123                         break;
124                 case wxMOUSE_BTN_RIGHT:
125                         break;
126         }
127         e.Skip();
128 }
129
130 // handle keydown events
131 void BitmapBrowser::OnKeyDown(wxKeyEvent &e)
132 {
133         switch (e.GetKeyCode()) {
134                 case WXK_LEFT:
135                 case WXK_RIGHT:
136                 case WXK_UP:
137                 case WXK_DOWN:
138                         {
139                                 int     new_selection = mSelection;
140
141                                 if (mSelection >= 0 && mSelection < (int)mBitmaps.size()) {
142                                         switch (e.GetKeyCode()) {
143                                                 case WXK_LEFT:
144                                                         if (mSelection % mNumCols > 0)
145                                                                 new_selection--;
146                                                         break;
147                                                 case WXK_RIGHT:
148                                                         if (mSelection % mNumCols < (mNumCols-1))
149                                                                 new_selection++;
150                                                         break;
151                                                 case WXK_UP:
152                                                         if (mSelection / mNumCols > 0)
153                                                                 new_selection -= mNumCols;
154                                                         break;
155                                                 case WXK_DOWN:
156                                                         if (mSelection / mNumCols < (mNumRows-1))
157                                                                 new_selection += mNumCols;
158                                                         break;
159                                         }
160                                 } else if (mBitmaps.size() > 0) {
161                                         new_selection = 0;
162                                 }
163                                 if (new_selection != mSelection && new_selection >= 0
164                                                 && new_selection < (int)mBitmaps.size()) {
165                                         // TODO scroll to show the new selection
166                                         mSelection = new_selection;
167                                         Refresh();
168
169                                         // send bitmap selection event
170                                         wxCommandEvent  event(wxEVT_BITMAPBROWSER, GetId());
171
172                                         event.SetEventObject(this);
173                                         event.SetInt(mSelection);
174                                         GetEventHandler()->ProcessEvent(event);
175                                 }
176                         }
177                         break;
178                 case WXK_DELETE:
179                         // send a bitmap delete event
180                         if (mSelection >= 0 && mSelection < (int)mBitmaps.size()) {
181                                 wxCommandEvent  event(wxEVT_BITMAPBROWSER_DELETE, GetId());
182
183                                 event.SetEventObject(this);
184                                 event.SetInt(mSelection);
185                                 GetEventHandler()->ProcessEvent(event);
186                         }
187                         break;
188                 default:
189                         e.Skip();
190                         break;
191         }
192 }
193
194 // the Freeze()/Thaw() combo is necessary to get a reasonably
195 // responsive interface. Otherwise we would be doing bursts
196 // of UpdateVirtualSize() and potentially RebuildThumbnails()
197 // every time the user changes collection
198 void BitmapBrowser::Freeze(void)
199 {
200         mFrozenCount++;
201 }
202
203 void BitmapBrowser::Thaw(void)
204 {
205         if (mFrozenCount > 0) {
206                 mFrozenCount--;
207                 if (mFrozenCount == 0) {
208                         UpdateVirtualSize();
209                         Refresh();
210                 }
211         }
212 }
213
214 int BitmapBrowser::GetSelection(void) const
215 {
216         return mSelection;
217 }
218
219 // set the thumbnail size in pixels. Specify -1 to enable best-fit mode.
220 void BitmapBrowser::SetThumbnailSize(int size)
221 {
222         if (size > 0) {
223                 mThumbnailSize = size;
224                 mAutoSize = false;
225         } else {
226                 mAutoSize = true;
227         }
228         if (mFrozenCount == 0) {
229                 UpdateVirtualSize();
230                 if (!mAutoSize)
231                         RebuildThumbnails();
232                 Refresh();
233         }
234 }
235
236 void BitmapBrowser::SetTranspPixelsDisplay(bool show)
237 {
238         mWhiteTransparency = show;
239         if (mFrozenCount == 0) {
240                 RebuildThumbnails();
241                 Refresh();
242         }
243 }
244
245 // add a new ShapesBitmap to the thumbnail list
246 void BitmapBrowser::AddBitmap(ShapesBitmap *bp)
247 {
248         if (bp != NULL) {
249                 if (bp->Pixels() != NULL) {
250                         mBitmaps.push_back(bp);
251                         mThumbnails.push_back(CreateThumbnail(bp));
252                         if (mFrozenCount == 0) {
253                                 UpdateVirtualSize();
254                                 Refresh();
255                         }
256                 } else {
257                         wxLogError(wxT("[BitmapBrowser] Added a bitmap with NULL pixels"));
258                 }
259         }
260 }
261
262 // clear the thumbnail list
263 void BitmapBrowser::Clear(void)
264 {
265         mThumbnails.clear();
266         mBitmaps.clear();
267         mThumbnailPositions.clear();
268         mSelection = -1;
269         UpdateVirtualSize();
270         if (mFrozenCount == 0)
271                 Refresh();
272 }
273
274 // call before adding bitmaps!
275 void BitmapBrowser::SetColorTable(ShapesColorTable *ct)
276 {
277         mColorTable = ct;
278         if (mFrozenCount == 0) {
279                 RebuildThumbnails();
280                 Refresh();
281         }
282 }
283
284 // calculate and set the wxWindow virtual size, based on the
285 // number of thumbnails, thumbnail dimensions and given visible
286 // size. Also pre-calculate thumbnail positions
287 void BitmapBrowser::UpdateVirtualSize(void)
288 {
289         wxClientDC              dc(this);
290         unsigned int    numbitmaps = mBitmaps.size();
291         int                             width, height;
292
293         GetClientSize(&width, &height);
294         if (numbitmaps < 1) {
295                 SetVirtualSize(0, 0);
296                 return;
297         }
298
299         if (mAutoSize) {
300                 // calculate the best mThumbnailSize
301                 // (maximum value not requiring window scrolling)
302                 int     max_bitmap_dimension = 10,
303                         new_tn_size;
304
305                 SetScrollRate(0, 0);
306                 // find greatest dimension among all bitmaps
307                 for (unsigned int i = 0; i < numbitmaps; i++) {
308                         if (mBitmaps[i]->Width() > max_bitmap_dimension)
309                                 max_bitmap_dimension = mBitmaps[i]->Width();
310                         if (mBitmaps[i]->Height() > max_bitmap_dimension)
311                                 max_bitmap_dimension = mBitmaps[i]->Height();
312                 }
313                 // FIXME a better algorithm, without looping?
314                 for (new_tn_size = mMargin; ; new_tn_size++) {
315                         int     numcols = (width - mMargin) / (new_tn_size + mMargin),
316                                 numrows = (numcols > 0) ? (numbitmaps / numcols) : numbitmaps;
317
318                         if (numrows * numcols < (int)numbitmaps)
319                                 numrows++;
320                         int     total_height = numrows * (new_tn_size + mMargin) + mMargin;
321
322                         if (total_height > height || (new_tn_size + 2 * mMargin) > width) {
323                                 new_tn_size--;
324                                 break;
325                         }
326                         if (new_tn_size >= max_bitmap_dimension) {
327                                 // no point in wasting window space with huge
328                                 // thumbnails showing small bitmaps at their center
329                                 new_tn_size = max_bitmap_dimension;
330                                 break;
331                         }
332                 }
333                 if (new_tn_size != mThumbnailSize) {
334                         mThumbnailSize = new_tn_size;
335                         RebuildThumbnails();
336                 }
337         } else {
338                 SetScrollRate(0, 2);
339         }
340
341         mNumCols = (width - mMargin) / (mThumbnailSize + mMargin);
342         mNumRows = (mNumCols > 0) ? (numbitmaps / mNumCols) : numbitmaps;
343
344         if (mNumRows * mNumCols < (int)numbitmaps)
345                 mNumRows++;
346
347         SetVirtualSize(width, mNumRows * (mThumbnailSize + mMargin) + mMargin);
348
349         // recalculate thumbnail positions
350         int     x = mMargin,
351                 y = mMargin;
352
353         mThumbnailPositions.clear();
354         for (unsigned int i = 0; i < numbitmaps; i++) {
355                 mThumbnailPositions.push_back(wxPoint(x, y));
356
357                 x += mThumbnailSize + mMargin;
358                 if (x + mThumbnailSize + mMargin > width) {
359                         x = mMargin;
360                         y += mThumbnailSize + mMargin;
361                 }
362         }
363 }
364
365 // transform a ShapesBitmap to a wxBitmap thumbnail
366 wxBitmap BitmapBrowser::CreateThumbnail(ShapesBitmap *bp)
367 {
368         wxImage newimg;
369
370         if (mColorTable)
371                 newimg = ShapesBitmapToImage(bp, mColorTable, mWhiteTransparency);
372         return ImageThumbnail(newimg, mThumbnailSize, true);
373 }
374
375 void BitmapBrowser::RebuildThumbnail(unsigned int i)
376 {
377         if (i < mBitmaps.size())
378                 mThumbnails[i] = CreateThumbnail(mBitmaps[i]);
379 }
380
381 // just re-decode the ShapesBitmaps to their thumbnail previews,
382 // without touching window sizes or thumbnail positions.
383 // Useful to update the display at the end of SetColorTable or after
384 // altering the ShapesBitmaps
385 void BitmapBrowser::RebuildThumbnails(void)
386 {
387         for (unsigned int i = 0; i < mBitmaps.size(); i++)
388                 mThumbnails[i] = CreateThumbnail(mBitmaps[i]);
389 }
390