OSDN Git Service

イニシャルコミット。
[marathon/ShapeFusion.git] / Shapes / FrameBrowser.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 //
19 // FrameBrowser
20 // A widget for displaying a list of ShapesFrame's
21 // as a scrollable list of selectable thumbnails. Each thumbnail
22 // displays the frame's associated bitmap, altered with
23 // the specified mirror transformations.
24 // 
25 #include "wx/image.h"
26 #include "FrameBrowser.h"
27 #include "utilities.h"
28
29 DEFINE_EVENT_TYPE(wxEVT_FRAMEBROWSER)
30 DEFINE_EVENT_TYPE(wxEVT_FRAMEBROWSER_DELETE)
31
32 BEGIN_EVENT_TABLE(FrameBrowser, wxScrolledWindow)
33         EVT_PAINT(FrameBrowser::OnPaint)
34         EVT_SIZE(FrameBrowser::OnSize)
35         EVT_LEFT_DOWN(FrameBrowser::OnMouseDown)
36         EVT_RIGHT_DOWN(FrameBrowser::OnMouseDown)
37         EVT_KEY_DOWN(FrameBrowser::OnKeyDown)
38 END_EVENT_TABLE()
39
40 FrameBrowser::FrameBrowser(wxWindow *parent, wxWindowID id):
41         wxScrolledWindow(parent, id, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER | wxFULL_REPAINT_ON_RESIZE),
42         mColorTable(NULL), mNumCols(0), mNumRows(0), mFrozenCount(0)
43 {
44         SetBackgroundColour(wxColour(255, 255, 255));
45         mThumbnailPen.SetColour(200, 200, 200);
46         mSelectionPen.SetColour(0, 0, 0);
47         mSelectionPen.SetWidth(3);
48         SetScrollRate(0, 2);
49         mThumbnailSize = 64;
50         mAutoSize = false;
51         mMargin = 7;
52         mWhiteTransparency = true;
53         mSelection = -1;
54 }
55
56 void FrameBrowser::OnPaint(wxPaintEvent& e)
57 {
58         wxPaintDC   tempdc(this);
59         int                     cw, ch, rx, ry;
60
61         DoPrepareDC(tempdc);
62         GetClientSize(&cw, &ch);
63         CalcUnscrolledPosition(0, 0, &rx, &ry);
64         // draw thumbnails
65         tempdc.SetPen(mThumbnailPen);
66         tempdc.SetBrush(*wxTRANSPARENT_BRUSH);
67         for (unsigned int i = 0; i < mThumbnails.size(); i++) {
68                 int     x = mThumbnailPositions[i].x,
69                         y = mThumbnailPositions[i].y;
70
71                 if (y + mThumbnailSize < ry)
72                         continue;
73                 if (y > ry + ch)
74                         break;
75
76                 int     bw = mThumbnails[i].GetWidth(),
77                         bh = mThumbnails[i].GetHeight();
78
79                 if ((int)i == mSelection) {
80                         tempdc.DrawBitmap(mThumbnails[i], x + mThumbnailSize/2 - bw/2, y + mThumbnailSize/2 - bh/2);
81                         tempdc.SetPen(mSelectionPen);
82                         tempdc.DrawRectangle(x-2, y-2, mThumbnailSize+4, mThumbnailSize+4);
83                         tempdc.SetPen(mThumbnailPen);
84                 } else {
85                         tempdc.DrawRectangle(x-1, y-1, mThumbnailSize+2, mThumbnailSize+2);
86                         tempdc.DrawBitmap(mThumbnails[i], x + mThumbnailSize/2 - bw/2, y + mThumbnailSize/2 - bh/2);
87                 }
88         }
89 }
90
91 // widget resized, recalculate virtual size to correctly wrap thumbnails
92 void FrameBrowser::OnSize(wxSizeEvent& e)
93 {
94         UpdateVirtualSize();
95 }
96
97 void FrameBrowser::OnMouseDown(wxMouseEvent& e)
98 {
99         wxClientDC      dc(this);
100         wxPoint         mouse;
101
102         DoPrepareDC(dc);
103         mouse = e.GetLogicalPosition(dc);
104         switch (e.GetButton()) {
105                 case wxMOUSE_BTN_LEFT:
106                         // handle frame selection
107                         {
108                                 int     new_selection = -1;
109
110                                 for (unsigned int i = 0; i < mThumbnailPositions.size(); i++) {
111                                         wxRect  test(mThumbnailPositions[i].x, mThumbnailPositions[i].y,
112                                                                         mThumbnailSize, mThumbnailSize);
113
114                                         if (test.Contains(mouse)) {
115                                                 new_selection = i;
116                                                 break;
117                                         }
118                                 }
119                                 if (new_selection != mSelection) {
120                                         mSelection = new_selection;
121                                         Refresh();
122
123                                         // send selection event
124                                         wxCommandEvent  event(wxEVT_FRAMEBROWSER, GetId());
125
126                                         event.SetEventObject(this);
127                                         event.SetInt(mSelection);
128                                         GetEventHandler()->ProcessEvent(event);
129                                 }
130                         }
131                         break;
132                 case wxMOUSE_BTN_RIGHT:
133                         break;
134         }
135         e.Skip();
136 }
137
138 void FrameBrowser::OnKeyDown(wxKeyEvent &e)
139 {
140         switch (e.GetKeyCode()) {
141                 case WXK_LEFT:
142                 case WXK_RIGHT:
143                 case WXK_UP:
144                 case WXK_DOWN:
145                         {
146                                 int new_selection = mSelection;
147
148                                 if (mSelection >= 0 && mSelection < (int)mFrames.size()) {
149                                         switch (e.GetKeyCode()) {
150                                                 case WXK_LEFT:
151                                                         if (mSelection % mNumCols > 0)
152                                                                 new_selection--;
153                                                         break;
154                                                 case WXK_RIGHT:
155                                                         if (mSelection % mNumCols < (mNumCols-1))
156                                                                 new_selection++;
157                                                         break;
158                                                 case WXK_UP:
159                                                         if (mSelection / mNumCols > 0)
160                                                                 new_selection -= mNumCols;
161                                                         break;
162                                                 case WXK_DOWN:
163                                                         if (mSelection / mNumCols < (mNumRows-1))
164                                                                 new_selection += mNumCols;
165                                                         break;
166                                         }
167                                 } else if (mFrames.size() > 0) {
168                                         new_selection = 0;
169                                 }
170                                 if (new_selection != mSelection && new_selection >= 0
171                                                 && new_selection < (int)mFrames.size()) {
172                                         // TODO scroll to show the new selection
173                                         mSelection = new_selection;
174                                         Refresh();
175                                         
176                                         // send frame selection event
177                                         wxCommandEvent  event(wxEVT_FRAMEBROWSER, GetId());
178                                         
179                                         event.SetEventObject(this);
180                                         event.SetInt(mSelection);
181                                         GetEventHandler()->ProcessEvent(event);
182                                 }
183                         }
184                         break;
185                 case WXK_DELETE:
186                         // send a frame delete event
187                         if (mSelection >= 0 && mSelection < (int)mFrames.size()) {
188                                 wxCommandEvent  event(wxEVT_FRAMEBROWSER_DELETE, GetId());
189
190                                 event.SetEventObject(this);
191                                 event.SetInt(mSelection);
192                                 GetEventHandler()->ProcessEvent(event);
193                         }
194                         break;
195                 default:
196                         e.Skip();
197                         break;
198         }
199 }
200
201 // the Freeze()/Thaw() combo is necessary to get a reasonably
202 // responsive interface. Otherwise we would be doing bursts
203 // of UpdateVirtualSize() and potentially RebuildThumbnails()
204 // every time the user changes collection
205 void FrameBrowser::Freeze(void)
206 {
207         mFrozenCount++;
208 }
209
210 void FrameBrowser::Thaw(void)
211 {
212         if (mFrozenCount > 0) {
213                 mFrozenCount--;
214                 if (mFrozenCount == 0) {
215                         UpdateVirtualSize();
216                         Refresh();
217                 }
218         }
219 }
220
221 int FrameBrowser::GetSelection(void) const
222 {
223         return mSelection;
224 }
225
226 void FrameBrowser::SetThumbnailSize(int size)
227 {
228         if (size > 0) {
229                 mThumbnailSize = size;
230                 mAutoSize = false;
231         } else {
232                 mAutoSize = true;
233         }
234         if (mFrozenCount == 0) {
235                 UpdateVirtualSize();
236                 if (!mAutoSize)
237                         RebuildThumbnails();
238                 Refresh();
239         }
240 }
241
242 void FrameBrowser::SetTranspPixelsDisplay(bool show)
243 {
244         mWhiteTransparency = show;
245         if (mFrozenCount == 0) {
246                 RebuildThumbnails();
247                 Refresh();
248         }
249 }
250
251 // add a new ShapesFrame to the thumbnail list
252 void FrameBrowser::AddFrame(ShapesFrame *fp)
253 {
254         if (fp != NULL) {
255                 mFrames.push_back(fp);
256                 mThumbnails.push_back(CreateThumbnail(fp));
257                 if (mFrozenCount == 0) {
258                         UpdateVirtualSize();
259                         Refresh();
260                 }
261         }
262 }
263
264 // add a ShapesBitmap to the bitmap pointer list.
265 // Call before adding frames!
266 void FrameBrowser::AddBitmap(ShapesBitmap *bp)
267 {
268         if (bp != NULL) {
269                 if (bp->Pixels() != NULL)
270                         mBitmaps.push_back(bp);
271                 else
272                         wxLogError(wxT("FrameBrowser: someone tried to add a bitmap with NULL pixels"));
273         }
274 }
275
276 // clear the thumbnail list
277 void FrameBrowser::Clear(void)
278 {
279         mThumbnails.clear();
280         mFrames.clear();
281         mBitmaps.clear();
282         mThumbnailPositions.clear();
283         mSelection = -1;
284         if (mFrozenCount == 0) {
285                 UpdateVirtualSize();
286                 Refresh();
287         }
288 }
289
290 // clear just the bitmap pointer list
291 void FrameBrowser::ClearBitmaps(void)
292 {
293         mBitmaps.clear();
294 }
295
296 // call before adding frames!
297 void FrameBrowser::SetColorTable(ShapesColorTable *ct)
298 {
299         mColorTable = ct;
300         if (mFrozenCount == 0) {
301                 RebuildThumbnails();
302                 Refresh();
303         }
304 }
305
306 // calculate and set the wxWindow virtual size, based on the
307 // number of thumbnails, thumbnail dimensions and given visible
308 // size. Also pre-calculate thumbnail positions
309 void FrameBrowser::UpdateVirtualSize(void)
310 {
311         wxClientDC      dc(this);
312         int                     numframes = mThumbnails.size(),
313                                 width, height;
314
315         GetClientSize(&width, &height);
316         if (numframes < 1) {
317                 SetVirtualSize(0, 0);
318                 return;
319         }
320
321         if (mAutoSize && numframes > 0) {
322                 // calculate the best tn_size
323                 // (its maximum value not requiring window scrolling)
324                 int     max_bitmap_dimension = 10,
325                         new_tn_size;
326
327                 SetScrollRate(0, 0);
328                 // find greatest dimension among all referenced bitmaps
329                 for (unsigned int i = 0; i < mFrames.size(); i++) {
330                         int     bitmapindex = mFrames[i]->BitmapIndex();
331
332                         if (bitmapindex < 0 || bitmapindex >= (int)mBitmaps.size())
333                                 continue;
334                         if (mBitmaps[bitmapindex]->Width() > max_bitmap_dimension)
335                                 max_bitmap_dimension = mBitmaps[bitmapindex]->Width();
336                         if (mBitmaps[bitmapindex]->Height() > max_bitmap_dimension)
337                                 max_bitmap_dimension = mBitmaps[bitmapindex]->Height();
338                 }
339                 // start with a small size (margin) and increase it until overflow
340                 for (new_tn_size = mMargin; ; new_tn_size++) {
341                         int     numcols = (width - mMargin) / (new_tn_size + mMargin),
342                                 numrows = (numcols > 0) ? (numframes / numcols) : numframes;
343
344                         if (numrows * numcols < numframes)
345                                 numrows++;
346                         int     total_height = numrows * (new_tn_size + mMargin) + mMargin;
347
348                         if (total_height > height || (new_tn_size + 2 * mMargin) > width) {
349                                 // here we are
350                                 new_tn_size--;
351                                 break;
352                         }
353                         if (new_tn_size >= max_bitmap_dimension) {
354                                 // no point in wasting window space with huge
355                                 // thumbnails showing small bitmaps at their center
356                                 new_tn_size = max_bitmap_dimension;
357                                 break;
358                         }
359                 }
360                 if (new_tn_size != mThumbnailSize) {
361                         mThumbnailSize = new_tn_size;
362                         RebuildThumbnails();
363                 }
364         } else {
365                 SetScrollRate(0, 2);
366         }
367
368         mNumCols = (width - mMargin) / (mThumbnailSize + mMargin);
369         mNumRows = (mNumCols > 0) ? (numframes / mNumCols) : numframes;
370
371         if (mNumRows * mNumCols < numframes)
372                 mNumRows++;
373
374         SetVirtualSize(width, mNumRows * (mThumbnailSize + mMargin) + mMargin);
375
376         // recalculate thumbnail positions
377         int     x = mMargin, y = mMargin;
378
379         mThumbnailPositions.clear();
380         for (int i = 0; i < numframes; i++) {
381                 mThumbnailPositions.push_back(wxPoint(x, y));
382
383                 x += mThumbnailSize + mMargin;
384                 if (x + mThumbnailSize + mMargin > width) {
385                         x = mMargin;
386                         y += mThumbnailSize + mMargin;
387                 }
388         }
389 }
390
391 // transform an ShapesFrame to a wxBitmap thumbnail
392 wxBitmap FrameBrowser::CreateThumbnail(ShapesFrame *fp)
393 {
394         if (fp->BitmapIndex() < 0 || fp->BitmapIndex() >= (int)mBitmaps.size()) {
395                 // invalid or unset bitmap
396                 return BadThumbnail(mThumbnailSize);
397         } else {
398                 // valid bitmap
399                 ShapesBitmap    *bp = mBitmaps[fp->BitmapIndex()];
400                 wxImage                 newimg(bp->Width(), bp->Height());
401
402                 // decode the bitmap to a wxImage
403                 if (mColorTable)
404                         newimg = ShapesBitmapToImage(bp, mColorTable, mWhiteTransparency);
405
406                 // apply frame transformations
407                 if (fp->IsXmirrored())
408                         newimg = newimg.Mirror(true);
409                 if (fp->IsYmirrored())
410                         newimg = newimg.Mirror(false);
411
412                 return ImageThumbnail(newimg, mThumbnailSize, true);
413         }
414 }
415
416 void FrameBrowser::RebuildThumbnail(unsigned int i)
417 {
418         if (i < mThumbnails.size() && i < mFrames.size())
419                 mThumbnails[i] = CreateThumbnail(mFrames[i]);
420 }
421
422 // redecode frames to bitmaps (after color table change, frame parameter variations etc)
423 void FrameBrowser::RebuildThumbnails(void)
424 {
425         for (unsigned int i = 0; i < mFrames.size(); i++)
426                 mThumbnails[i] = CreateThumbnail(mFrames[i]);
427 }
428