OSDN Git Service

イニシャルコミット。
[marathon/ShapeFusion.git] / Shapes / SequenceView.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 "wx/dcbuffer.h"
20 #include "SequenceView.h"
21 #include "ShapesDocument.h"
22 #include "utilities.h"
23
24 DEFINE_EVENT_TYPE(wxEVT_SEQUENCEVIEW)
25
26 BEGIN_EVENT_TABLE(SequenceView, wxScrolledWindow)
27         EVT_PAINT(SequenceView::OnPaint)
28         EVT_SIZE(SequenceView::OnSize)
29         EVT_LEFT_DOWN(SequenceView::OnMouseDown)
30         EVT_LEFT_DCLICK(SequenceView::OnMouseDoubleClick)
31         EVT_RIGHT_DOWN(SequenceView::OnMouseDown)
32         EVT_LEFT_UP(SequenceView::OnMouseUp)
33         EVT_KEY_DOWN(SequenceView::OnKeyDown)
34         EVT_MOTION(SequenceView::OnMouseMove)
35 END_EVENT_TABLE()
36
37 const char      arrow_left_bits[] = { 0x08, 0x0c, 0x0e, 0x0f, 0x0e, 0x0c, 0x08 },
38                         arrow_right_bits[] = { 0x01, 0x03, 0x07, 0x0f, 0x07, 0x03, 0x01 };
39
40 const int THUMB_ARROW_MARGIN = 2;
41 const int THUMB_ARROW_BUTTON_MARGIN = 2;
42
43 SequenceView::SequenceView(wxWindow *parent, wxWindowID id):
44         wxScrolledWindow(parent, id, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER | wxFULL_REPAINT_ON_RESIZE),
45         mColorTable(NULL), mThumbnailSize(64), mMargin(7), mPrevBtnIcon(arrow_left_bits, 4, 7),
46         mNextBtnIcon(arrow_right_bits, 4, 7), mAutoSize(false), mSelection(-1), mFrameIndexes(NULL), mView(0)
47 {
48         SetBackgroundStyle(wxBG_STYLE_CUSTOM);
49         SetBackgroundColour(wxColour(255, 255, 255));
50         mThumbnailPen.SetColour(200, 200, 200);
51         mSelectionPen.SetColour(0, 0, 0);
52         mSelectionPen.SetWidth(3);
53         mAngleBrush.SetColour(200, 200, 200);
54         SetScrollRate(2, 2);
55         mAngleLabelSpace = 30;  // TODO get this from real font extent
56         mWhiteTransparency = true;
57         mNumberOfViews = 0;
58         mFramesPerView = 0;
59         mAnimationType = UNANIMATED;
60 }
61
62 void SequenceView::OnPaint(wxPaintEvent& e)
63 {
64         wxAutoBufferedPaintDC   tempdc(this);
65         wxPoint         mouse;
66         int                     cw, ch, rx, ry;
67
68         if (mFrameIndexes == NULL)
69                 return;
70
71         DoPrepareDC(tempdc);
72         tempdc.Clear();
73         GetClientSize(&cw, &ch);
74         CalcUnscrolledPosition(0, 0, &rx, &ry);
75         // get mouse position in scrolled coords
76         mouse = ScreenToClient(wxGetMousePosition());
77         mouse.x += rx;
78         mouse.y += ry;
79         // draw thumbnails
80         tempdc.SetPen(mThumbnailPen);
81         tempdc.SetBrush(*wxTRANSPARENT_BRUSH);
82         tempdc.SetTextForeground(wxColour(0, 0, 0));
83         tempdc.SetBackgroundMode(wxTRANSPARENT);
84         for (unsigned int i = 0; i < mThumbnails.size(); i++) {
85                 int     x = mThumbnailPositions[i].x,
86                         y = mThumbnailPositions[i].y,
87                         bw = mThumbnails[i].GetWidth(),
88                         bh = mThumbnails[i].GetHeight();
89
90                 if ((int)i == mSelection) {
91                         tempdc.DrawBitmap(mThumbnails[i], x + mThumbnailSize/2 - bw/2, y + mThumbnailSize/2 - bh/2);
92                         tempdc.SetPen(mSelectionPen);
93                         tempdc.DrawRectangle(x-2, y-2, mThumbnailSize+4, mThumbnailSize+4);
94                         tempdc.SetPen(mThumbnailPen);
95                 } else {
96                         tempdc.DrawRectangle(x-1, y-1, mThumbnailSize+2, mThumbnailSize+2);
97                         tempdc.DrawBitmap(mThumbnails[i], x + mThumbnailSize/2 - bw/2, y + mThumbnailSize/2 - bh/2);
98                 }
99                 // draw arrow buttons if mouse is over this thumbnail
100                 wxRect  tnrect(x, y, mThumbnailSize, mThumbnailSize);
101
102                 if (tnrect.Contains(mouse)) {
103                         wxString        label = wxString::Format(wxT("%d"), (*mFrameIndexes)[i]);
104                         int                     labelw, labelh;
105
106                         tempdc.SetFont(*wxSMALL_FONT);
107                         tempdc.DrawBitmap(mPrevBtnIcon, x + 2, y + mThumbnailSize - mPrevBtnIcon.GetHeight() - 2);
108                         tempdc.DrawBitmap(mNextBtnIcon, x + mThumbnailSize - mNextBtnIcon.GetWidth() - 2, y + mThumbnailSize - mNextBtnIcon.GetHeight() - 2);
109                         tempdc.GetTextExtent(label, &labelw, &labelh);
110                         tempdc.SetTextForeground(wxColour(255, 255, 255));
111                         tempdc.DrawText(label, x + (mThumbnailSize-labelw) / 2 - 1, y + mThumbnailSize - labelh - 2);
112                         tempdc.DrawText(label, x + (mThumbnailSize-labelw) / 2 + 1, y + mThumbnailSize - labelh - 2);
113                         tempdc.DrawText(label, x + (mThumbnailSize-labelw) / 2, y + mThumbnailSize - labelh - 2 - 1);
114                         tempdc.DrawText(label, x + (mThumbnailSize-labelw) / 2, y + mThumbnailSize - labelh - 2 + 1);
115                         tempdc.SetTextForeground(wxColour(0, 0, 0));
116                         tempdc.DrawText(label, x + (mThumbnailSize-labelw) / 2, y + mThumbnailSize - labelh - 2);
117                 }
118         }
119         if (mThumbnails.size() > 0 && mAnimationType != UNANIMATED && mAnimationType != ANIMATED_1) {
120                 // draw angle labels
121                 char            *deg = "°";
122                 wxString        deg2(deg, wxConvISO8859_1);     // FIXME does not work on OS X (draws infinity char)
123
124                 tempdc.SetFont(*wxSMALL_FONT);
125                 tempdc.SetBrush(mAngleBrush);
126                 for (int i = 0; i < mNumberOfViews; i++) {
127                         wxString        label = wxString::Format(wxT("%d"), i * 360 / mNumberOfViews) + deg2;
128                         int                     tw, th;
129         
130                         tempdc.GetTextExtent(label, &tw, &th);
131                         tempdc.DrawRectangle(mMargin-1, mMargin + i * (mThumbnailSize + mMargin) - 1, mAngleLabelSpace+2, mThumbnailSize+2);
132                         tempdc.DrawText(label, mMargin + mAngleLabelSpace - tw, mMargin + i * (mThumbnailSize + mMargin) + mThumbnailSize/2 - th/2);
133                 }
134         }
135 }
136
137 // widget resized, recalculate virtual size to correctly wrap thumbnails
138 void SequenceView::OnSize(wxSizeEvent& e)
139 {
140         UpdateVirtualSize();
141 }
142
143 void SequenceView::OnMouseDown(wxMouseEvent& e)
144 {
145         wxClientDC      dc(this);
146         wxPoint         mouse;
147
148         DoPrepareDC(dc);
149         mouse = e.GetLogicalPosition(dc);
150         switch (e.GetButton()) {
151                 case wxMOUSE_BTN_LEFT:
152                         // handle frame selection
153                         {
154                                 int     new_selection = -1;
155
156                                 for (unsigned int i = 0; i < mThumbnailPositions.size(); i++) {
157                                         wxRect  test(mThumbnailPositions[i].x, mThumbnailPositions[i].y,
158                                                                         mThumbnailSize, mThumbnailSize);
159
160                                         if (test.Contains(mouse)) {
161                                                 new_selection = i;
162                                                 break;
163                                         }
164                                 }
165                                 if (new_selection != mSelection) {
166                                         mSelection = new_selection;
167                                         Refresh();
168
169                                         // send selection event
170                                         wxCommandEvent  event(wxEVT_SEQUENCEVIEW, GetId());
171
172                                         event.SetEventObject(this);
173                                         event.SetInt(mSelection);
174                                         GetEventHandler()->ProcessEvent(event);
175                                 }
176                         }
177                         break;
178                 case wxMOUSE_BTN_RIGHT:
179                         break;
180         }
181         e.Skip();
182 }
183
184 void SequenceView::OnMouseUp(wxMouseEvent& e)
185 {
186         wxClientDC  dc(this);
187         wxPoint     mouse;
188         
189         DoPrepareDC(dc);
190         mouse = e.GetLogicalPosition(dc);
191         switch (e.GetButton()) {
192                 case wxMOUSE_BTN_LEFT:
193                         // clicks on the little arrows change the associated frame index
194                         // FIXME mouse tracking like real buttons (starting with a mouse down)
195                         {
196                                 // find wether the mouse was released over a thumbnail
197                                 int touched_thumbnail = -1;
198                                 
199                                 for (unsigned int i = 0; i < mThumbnailPositions.size(); i++) {
200                                         wxRect  test(mThumbnailPositions[i].x, mThumbnailPositions[i].y,
201                                                                         mThumbnailSize, mThumbnailSize);
202                                         
203                                         if (test.Contains(mouse)) {
204                                                 touched_thumbnail = i;
205                                                 break;
206                                         }
207                                 }
208                                 if (touched_thumbnail > -1) {
209                                         // find wether an arrow was touched
210                                         if (GetPrevArrowButtonRect(touched_thumbnail).Contains(mouse)) {
211                                                 if ((*mFrameIndexes)[touched_thumbnail] > -1) {
212                                                         (*mFrameIndexes)[touched_thumbnail]--;
213                                                         RebuildThumbnail(touched_thumbnail);
214                                                         Refresh();
215                                                         if (mView) {
216                                                                 static_cast<ShapesDocument*>(mView->GetDocument())->Modify(true);
217                                                         }
218                                                 }
219                                         } else if (GetNextArrowButtonRect(touched_thumbnail).Contains(mouse)) {
220                                                 if ((*mFrameIndexes)[touched_thumbnail] < ((int)mFrames.size()-1)) {
221                                                         (*mFrameIndexes)[touched_thumbnail]++;
222                                                         RebuildThumbnail(touched_thumbnail);
223                                                         Refresh();
224                                                         if (mView) {
225                                                                 static_cast<ShapesDocument*>(mView->GetDocument())->Modify(true);
226                                                         }
227                                                 }
228                                         }
229                                 }
230                         }
231                         break;
232         }
233         e.Skip();
234 }
235
236 void SequenceView::OnMouseMove(wxMouseEvent &e)
237 {
238         Refresh();
239 }
240
241 void SequenceView::OnKeyDown(wxKeyEvent &e)
242 {
243         if (e.ShiftDown()) {
244                 // edit frame index of selection
245                 switch (e.GetKeyCode()) {
246                         case WXK_LEFT:
247                                 if (mSelection >= 0) {
248                                         if ((*mFrameIndexes)[mSelection] > -1) {
249                                                 (*mFrameIndexes)[mSelection]--;
250                                                 RebuildThumbnail(mSelection);
251                                                 Refresh();
252                                                 if (mView) {
253                                                         static_cast<ShapesDocument*>(mView->GetDocument())->Modify(true);
254                                                 }
255                                         }
256                                 }
257                                 break;
258                         case WXK_RIGHT:
259                                 if (mSelection >= 0) {
260                                         if ((*mFrameIndexes)[mSelection] < ((int)mFrames.size()-1)) {
261                                                 (*mFrameIndexes)[mSelection]++;
262                                                 RebuildThumbnail(mSelection);
263                                                 Refresh();
264                                                 if (mView) {
265                                                         static_cast<ShapesDocument*>(mView->GetDocument())->Modify(true);
266                                                 }
267                                         }
268                                 }
269                                 break;
270                         default:
271                                 e.Skip();
272                                 break;
273                 }
274         } else {
275                 // edit selection with arrow keys
276                 int     new_selection = mSelection;
277
278                 if (mSelection >= 0 && mSelection < (mNumberOfViews * mFramesPerView)) {
279                         switch (e.GetKeyCode()) {
280                                 case WXK_LEFT:
281                                         if (mSelection % mFramesPerView > 0)
282                                                 new_selection--;
283                                         break;
284                                 case WXK_RIGHT:
285                                         if (mSelection % mFramesPerView < (mFramesPerView-1))
286                                                 new_selection++;
287                                         break;
288                                 case WXK_UP:
289                                         if (mSelection / mFramesPerView > 0)
290                                                 new_selection -= mFramesPerView;
291                                         break;
292                                 case WXK_DOWN:
293                                         if (mSelection / mFramesPerView < (mNumberOfViews-1))
294                                                 new_selection += mFramesPerView;
295                                         break;
296                                 case WXK_RETURN:
297                                 case WXK_NUMPAD_ENTER:
298                                         PopupFrameIndexDialog(mSelection);
299                                         break;
300                                 default:
301                                         e.Skip();
302                         }
303                 } else if (mNumberOfViews * mFramesPerView > 0) {
304                         new_selection = 0;
305                 }
306                 // TODO scroll to show the new position
307                 if (new_selection != mSelection) {
308                         mSelection = new_selection;
309                         Refresh();
310                         
311                         // send selection event
312                         wxCommandEvent  event(wxEVT_SEQUENCEVIEW, GetId());
313
314                         event.SetEventObject(this);
315                         event.SetInt(mSelection);
316                         GetEventHandler()->ProcessEvent(event);
317                 }
318         }
319 }
320
321 void SequenceView::OnMouseDoubleClick(wxMouseEvent& e)
322 {
323         wxClientDC dc(this);
324         wxPoint mouse;
325
326         DoPrepareDC(dc);
327         mouse = e.GetLogicalPosition(dc);
328         switch (e.GetButton()) {
329         case wxMOUSE_BTN_LEFT: 
330         {
331                 int selection = -1;
332                 for (unsigned int i = 0; i < mThumbnailPositions.size(); ++i) {
333                         wxRect test(mThumbnailPositions[i].x, mThumbnailPositions[i].y, mThumbnailSize, mThumbnailSize);
334                         if (test.Contains(mouse)) {
335                                 selection = i;
336                                 break;
337                         }
338                 }
339                 
340                 if (selection != -1) {
341                         if (!GetPrevArrowButtonRect(selection).Contains(mouse) &&
342                             !GetNextArrowButtonRect(selection).Contains(mouse)) {       
343                                 PopupFrameIndexDialog(selection);
344                         }
345                 }
346         }
347         break;
348         }
349 }
350
351 int SequenceView::GetSelection(void) const
352 {
353         return mSelection;
354 }
355
356 void SequenceView::SetThumbnailSize(int size)
357 {
358         if (size > 0) {
359                 mThumbnailSize = size;
360                 mAutoSize = false;
361         } else {
362                 mAutoSize = true;
363         }
364         UpdateVirtualSize();
365         if (!mAutoSize)
366                 RebuildThumbnails();
367         Refresh();
368 }
369
370 void SequenceView::SetTranspPixelsDisplay(bool show)
371 {
372         mWhiteTransparency = show;
373         RebuildThumbnails();
374         Refresh();
375 }
376
377 // add a new ShapesFrame to the thumbnail list
378 void SequenceView::AddFrame(ShapesFrame *fp)
379 {
380         if (fp != NULL)
381                 mFrames.push_back(fp);
382 }
383
384 // add a ShapesBitmap to the bitmap pointer list. Call before adding frames!
385 void SequenceView::AddBitmap(ShapesBitmap *bp)
386 {
387         if (bp != NULL) {
388                 if (bp->Pixels() != NULL)
389                         mBitmaps.push_back(bp);
390                 else
391                         wxLogError(wxT("SequenceView: someone tried to add a bitmap with NULL pixels"));
392         }
393 }
394
395 // set sequence parameters: frames per view, animation type and frame index array.
396 // Guess number of views from animation type. Also create thumbnails (add frames,
397 // bitmaps and set color table before!)
398 void SequenceView::SetSeqParameters(int animtype, int fpv, vector<short> *indexes, wxView* view)
399 {
400         mAnimationType = animtype;
401         mNumberOfViews = ActualNumberOfViews(animtype);
402         mFramesPerView = fpv;
403         mFrameIndexes = indexes;
404         mView = view;
405         RebuildThumbnails();
406         UpdateVirtualSize();
407         Refresh();
408 }
409
410 // clear the thumbnail list
411 void SequenceView::Clear(void)
412 {
413         mThumbnails.clear();
414         mFrames.clear();
415         mBitmaps.clear();
416         mThumbnailPositions.clear();
417         mSelection = -1;
418         UpdateVirtualSize();
419         Refresh();
420 }
421
422 // call before adding frames!
423 void SequenceView::SetColorTable(ShapesColorTable *ct)
424 {
425         mColorTable = ct;
426         RebuildThumbnails();
427         Refresh();
428 }
429
430 // calculate and set the wxWindow virtual size, based on the
431 // number of thumbnails, thumbnail dimensions and given visible
432 // size. Also pre-calculate thumbnail positions
433 // FIXME behave like a FrameBrowser if mAnimationType == UNANIMATED,
434 // because frames are just to be chosen randomly and not really
435 // an animation. 
436 void SequenceView::UpdateVirtualSize(void)
437 {
438         wxClientDC      dc(this);
439         int                     numframes = mThumbnails.size(),
440                                 width, height,
441                                 additional_pad = (mAnimationType == UNANIMATED || mAnimationType == ANIMATED_1)
442                                                                         ? 0 : (mAngleLabelSpace + mMargin);
443
444         if (numframes < 1 || mFrameIndexes == NULL) {
445                 SetVirtualSize(0, 0);
446                 return;
447         }
448         GetClientSize(&width, &height);
449         if (mAutoSize && numframes > 0) {
450                 // calculate the best tn_size
451                 // (its maximum value not requiring window scrolling)
452                 int     max_bitmap_dimension = 0,
453                         new_tn_size;
454
455                 SetScrollRate(0, 0);
456                 // find greatest dimension among all referenced bitmaps
457                 for (unsigned int i = 0; i < mFrameIndexes->size(); i++) {
458                         int     frame_index = (*mFrameIndexes)[i];
459
460                         if (frame_index < 0 || frame_index >= (int)mFrames.size())
461                                 continue;
462
463                         int     bitmap_index = mFrames[frame_index]->BitmapIndex();
464
465                         if (bitmap_index < 0 || bitmap_index >= (int)mBitmaps.size())
466                                 continue;
467                         if (mBitmaps[bitmap_index]->Width() > max_bitmap_dimension)
468                                 max_bitmap_dimension = mBitmaps[bitmap_index]->Width();
469                         if (mBitmaps[bitmap_index]->Height() > max_bitmap_dimension)
470                                 max_bitmap_dimension = mBitmaps[bitmap_index]->Height();
471                 }
472                 // start with a minimum size and increase it until overflow
473                 for (new_tn_size = 2*mMargin; ; new_tn_size++) {
474                         int     numcols = mFramesPerView,
475                                 numrows = mNumberOfViews,
476                                 total_width = numcols * (new_tn_size + mMargin) + mMargin + additional_pad,
477                                 total_height = numrows * (new_tn_size + mMargin) + mMargin;
478
479                         if (total_width > width || total_height > height) {
480                                 // here we are
481                                 new_tn_size--;
482                                 break;
483                         }
484                         if (max_bitmap_dimension > 0 && new_tn_size >= max_bitmap_dimension) {
485                                 // no point in wasting window space with huge
486                                 // thumbnails showing small bitmaps at their center
487                                 break;
488                         }
489                 }
490                 if (new_tn_size != mThumbnailSize) {
491                         mThumbnailSize = new_tn_size;
492                         RebuildThumbnails();
493                 }
494         } else {
495                 SetScrollRate(2, 2);
496         }
497
498         // now row & column count is simply mNumberOfViews and mFramesPerView,
499         // not the mess needed in the FrameBrowser
500         int     numcols = mFramesPerView,
501                 numrows = mNumberOfViews;
502
503         SetVirtualSize(numcols * (mThumbnailSize + mMargin) + mMargin + additional_pad,
504                                         numrows * (mThumbnailSize + mMargin) + mMargin);
505
506         // recalculate thumbnail positions
507         mThumbnailPositions.clear();
508         for (int r = 0; r < numrows; r++) {
509                 for (int c = 0; c < numcols; c++)
510                         mThumbnailPositions.push_back(wxPoint(mMargin + (mThumbnailSize + mMargin) * c + additional_pad,
511                                                                                         mMargin + (mThumbnailSize + mMargin) * r));
512         }
513 }
514
515 // transform an ShapesFrame to a wxBitmap thumbnail
516 wxBitmap SequenceView::CreateThumbnail(ShapesFrame *fp)
517 {
518         if (fp->BitmapIndex() < 0 || fp->BitmapIndex() >= (int)mBitmaps.size()) {
519                 // invalid or unset bitmap
520                 // FIXME we are rebuilding this wxBitmap each time! Build it once
521                 // and store it somewhere (rebuild upon tn_size change)
522                 return BadThumbnail(mThumbnailSize);
523         } else {
524                 // valid bitmap
525                 ShapesBitmap    *bp = mBitmaps[fp->BitmapIndex()];
526                 wxImage                 newimg(bp->Width(), bp->Height());
527
528                 // decode the bitmap to a wxImage
529                 if (mColorTable)
530                         newimg = ShapesBitmapToImage(bp, mColorTable, mWhiteTransparency);
531
532                 // apply frame transformations
533                 if (fp->IsXmirrored())
534                         newimg = newimg.Mirror(true);
535                 if (fp->IsYmirrored())
536                         newimg = newimg.Mirror(false);
537
538                 // TODO apply transfer mode
539
540                 return ImageThumbnail(newimg, mThumbnailSize, true);
541         }
542 }
543
544 void SequenceView::RebuildThumbnail(unsigned int i)
545 {
546         if (i < mThumbnails.size() && i < mFrameIndexes->size()) {
547                 if ((*mFrameIndexes)[i] >= 0 && (*mFrameIndexes)[i] < (int)mFrames.size())
548                         mThumbnails[i] = CreateThumbnail(mFrames[(*mFrameIndexes)[i]]);
549                 else
550                         mThumbnails[i] = BadThumbnail(mThumbnailSize);
551         }
552 }
553
554 // redecode frames to bitmaps (after color table change, frame parameter variations etc)
555 void SequenceView::RebuildThumbnails(void)
556 {
557         mThumbnails.clear();
558         if (mFrameIndexes != NULL) {
559                 for (unsigned int i = 0; i < mFrameIndexes->size(); i++) {
560                         if ((*mFrameIndexes)[i] >= 0 && (*mFrameIndexes)[i] < (int)mFrames.size())
561                                 mThumbnails.push_back(CreateThumbnail(mFrames[(*mFrameIndexes)[i]]));
562                         else
563                                 mThumbnails.push_back(BadThumbnail(mThumbnailSize));
564                 }
565         }
566 }
567
568 void SequenceView::PopupFrameIndexDialog(int selection)
569 {
570         long v = 0;
571         if (wxGetTextFromUser(_("Change frame:"), _("Chage frame"), wxString::Format(wxT("%i"), (*mFrameIndexes)[selection]), GetParent()).ToLong(&v)) {
572                 if (v >= -1 && v <= static_cast<int>(mFrames.size()) - 1) {
573                         (*mFrameIndexes)[selection] = v;
574                         RebuildThumbnail(selection);
575                         Refresh();
576                         if (mView) {
577                                 static_cast<ShapesDocument*>(mView->GetDocument())->Modify(true);
578                         }
579                 }
580         }
581 }
582
583 wxRect SequenceView::GetNextArrowButtonRect(int thumbnail_index) const
584 {
585         int thumb_x = mThumbnailPositions[thumbnail_index].x;
586         int thumb_y = mThumbnailPositions[thumbnail_index].y;
587
588         int x = thumb_x + mThumbnailSize - mNextBtnIcon.GetWidth() - THUMB_ARROW_MARGIN;
589         int y = thumb_y + mThumbnailSize - mNextBtnIcon.GetHeight() - THUMB_ARROW_MARGIN;
590         int w = mPrevBtnIcon.GetWidth();
591         int h = mPrevBtnIcon.GetHeight();
592
593         return wxRect(x - THUMB_ARROW_BUTTON_MARGIN,
594                       y - THUMB_ARROW_BUTTON_MARGIN,
595                       w + 2 * THUMB_ARROW_BUTTON_MARGIN,
596                       h + 2 * THUMB_ARROW_BUTTON_MARGIN);
597 }
598
599 wxRect SequenceView::GetPrevArrowButtonRect(int thumbnail_index) const
600 {
601         int thumb_x = mThumbnailPositions[thumbnail_index].x;
602         int thumb_y = mThumbnailPositions[thumbnail_index].y;
603
604         int x = thumb_x + THUMB_ARROW_MARGIN;
605         int y = thumb_y + mThumbnailSize - mNextBtnIcon.GetHeight() - THUMB_ARROW_MARGIN;
606         int w = mPrevBtnIcon.GetWidth();
607         int h = mPrevBtnIcon.GetHeight();
608
609         return wxRect(x - THUMB_ARROW_BUTTON_MARGIN, 
610                       y - THUMB_ARROW_BUTTON_MARGIN,
611                       w + 2 * THUMB_ARROW_BUTTON_MARGIN,
612                       h + 2 * THUMB_ARROW_BUTTON_MARGIN);
613 }