2 * This file is part of ShapeFusion (Copyright 2000 Tito Dal Canton)
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.
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.
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
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.
26 #include "FrameBrowser.h"
27 #include "utilities.h"
29 DEFINE_EVENT_TYPE(wxEVT_FRAMEBROWSER)
30 DEFINE_EVENT_TYPE(wxEVT_FRAMEBROWSER_DELETE)
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)
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)
44 SetBackgroundColour(wxColour(255, 255, 255));
45 mThumbnailPen.SetColour(200, 200, 200);
46 mSelectionPen.SetColour(0, 0, 0);
47 mSelectionPen.SetWidth(3);
52 mWhiteTransparency = true;
56 void FrameBrowser::OnPaint(wxPaintEvent& e)
58 wxPaintDC tempdc(this);
62 GetClientSize(&cw, &ch);
63 CalcUnscrolledPosition(0, 0, &rx, &ry);
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;
71 if (y + mThumbnailSize < ry)
76 int bw = mThumbnails[i].GetWidth(),
77 bh = mThumbnails[i].GetHeight();
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);
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);
91 // widget resized, recalculate virtual size to correctly wrap thumbnails
92 void FrameBrowser::OnSize(wxSizeEvent& e)
97 void FrameBrowser::OnMouseDown(wxMouseEvent& e)
103 mouse = e.GetLogicalPosition(dc);
104 switch (e.GetButton()) {
105 case wxMOUSE_BTN_LEFT:
106 // handle frame selection
108 int new_selection = -1;
110 for (unsigned int i = 0; i < mThumbnailPositions.size(); i++) {
111 wxRect test(mThumbnailPositions[i].x, mThumbnailPositions[i].y,
112 mThumbnailSize, mThumbnailSize);
114 if (test.Contains(mouse)) {
119 if (new_selection != mSelection) {
120 mSelection = new_selection;
123 // send selection event
124 wxCommandEvent event(wxEVT_FRAMEBROWSER, GetId());
126 event.SetEventObject(this);
127 event.SetInt(mSelection);
128 GetEventHandler()->ProcessEvent(event);
132 case wxMOUSE_BTN_RIGHT:
138 void FrameBrowser::OnKeyDown(wxKeyEvent &e)
140 switch (e.GetKeyCode()) {
146 int new_selection = mSelection;
148 if (mSelection >= 0 && mSelection < (int)mFrames.size()) {
149 switch (e.GetKeyCode()) {
151 if (mSelection % mNumCols > 0)
155 if (mSelection % mNumCols < (mNumCols-1))
159 if (mSelection / mNumCols > 0)
160 new_selection -= mNumCols;
163 if (mSelection / mNumCols < (mNumRows-1))
164 new_selection += mNumCols;
167 } else if (mFrames.size() > 0) {
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;
176 // send frame selection event
177 wxCommandEvent event(wxEVT_FRAMEBROWSER, GetId());
179 event.SetEventObject(this);
180 event.SetInt(mSelection);
181 GetEventHandler()->ProcessEvent(event);
186 // send a frame delete event
187 if (mSelection >= 0 && mSelection < (int)mFrames.size()) {
188 wxCommandEvent event(wxEVT_FRAMEBROWSER_DELETE, GetId());
190 event.SetEventObject(this);
191 event.SetInt(mSelection);
192 GetEventHandler()->ProcessEvent(event);
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)
210 void FrameBrowser::Thaw(void)
212 if (mFrozenCount > 0) {
214 if (mFrozenCount == 0) {
221 int FrameBrowser::GetSelection(void) const
226 void FrameBrowser::SetThumbnailSize(int size)
229 mThumbnailSize = size;
234 if (mFrozenCount == 0) {
242 void FrameBrowser::SetTranspPixelsDisplay(bool show)
244 mWhiteTransparency = show;
245 if (mFrozenCount == 0) {
251 // add a new ShapesFrame to the thumbnail list
252 void FrameBrowser::AddFrame(ShapesFrame *fp)
255 mFrames.push_back(fp);
256 mThumbnails.push_back(CreateThumbnail(fp));
257 if (mFrozenCount == 0) {
264 // add a ShapesBitmap to the bitmap pointer list.
265 // Call before adding frames!
266 void FrameBrowser::AddBitmap(ShapesBitmap *bp)
269 if (bp->Pixels() != NULL)
270 mBitmaps.push_back(bp);
272 wxLogError(wxT("FrameBrowser: someone tried to add a bitmap with NULL pixels"));
276 // clear the thumbnail list
277 void FrameBrowser::Clear(void)
282 mThumbnailPositions.clear();
284 if (mFrozenCount == 0) {
290 // clear just the bitmap pointer list
291 void FrameBrowser::ClearBitmaps(void)
296 // call before adding frames!
297 void FrameBrowser::SetColorTable(ShapesColorTable *ct)
300 if (mFrozenCount == 0) {
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)
312 int numframes = mThumbnails.size(),
315 GetClientSize(&width, &height);
317 SetVirtualSize(0, 0);
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,
328 // find greatest dimension among all referenced bitmaps
329 for (unsigned int i = 0; i < mFrames.size(); i++) {
330 int bitmapindex = mFrames[i]->BitmapIndex();
332 if (bitmapindex < 0 || bitmapindex >= (int)mBitmaps.size())
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();
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;
344 if (numrows * numcols < numframes)
346 int total_height = numrows * (new_tn_size + mMargin) + mMargin;
348 if (total_height > height || (new_tn_size + 2 * mMargin) > width) {
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;
360 if (new_tn_size != mThumbnailSize) {
361 mThumbnailSize = new_tn_size;
368 mNumCols = (width - mMargin) / (mThumbnailSize + mMargin);
369 mNumRows = (mNumCols > 0) ? (numframes / mNumCols) : numframes;
371 if (mNumRows * mNumCols < numframes)
374 SetVirtualSize(width, mNumRows * (mThumbnailSize + mMargin) + mMargin);
376 // recalculate thumbnail positions
377 int x = mMargin, y = mMargin;
379 mThumbnailPositions.clear();
380 for (int i = 0; i < numframes; i++) {
381 mThumbnailPositions.push_back(wxPoint(x, y));
383 x += mThumbnailSize + mMargin;
384 if (x + mThumbnailSize + mMargin > width) {
386 y += mThumbnailSize + mMargin;
391 // transform an ShapesFrame to a wxBitmap thumbnail
392 wxBitmap FrameBrowser::CreateThumbnail(ShapesFrame *fp)
394 if (fp->BitmapIndex() < 0 || fp->BitmapIndex() >= (int)mBitmaps.size()) {
395 // invalid or unset bitmap
396 return BadThumbnail(mThumbnailSize);
399 ShapesBitmap *bp = mBitmaps[fp->BitmapIndex()];
400 wxImage newimg(bp->Width(), bp->Height());
402 // decode the bitmap to a wxImage
404 newimg = ShapesBitmapToImage(bp, mColorTable, mWhiteTransparency);
406 // apply frame transformations
407 if (fp->IsXmirrored())
408 newimg = newimg.Mirror(true);
409 if (fp->IsYmirrored())
410 newimg = newimg.Mirror(false);
412 return ImageThumbnail(newimg, mThumbnailSize, true);
416 void FrameBrowser::RebuildThumbnail(unsigned int i)
418 if (i < mThumbnails.size() && i < mFrames.size())
419 mThumbnails[i] = CreateThumbnail(mFrames[i]);
422 // redecode frames to bitmaps (after color table change, frame parameter variations etc)
423 void FrameBrowser::RebuildThumbnails(void)
425 for (unsigned int i = 0; i < mFrames.size(); i++)
426 mThumbnails[i] = CreateThumbnail(mFrames[i]);