CPImageCtrl.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00027 // standard wx include
00028 #include <config.h>
00029 #include "panoinc_WX.h"
00030 
00031 // standard hugin include
00032 #include "panoinc.h"
00033 #include "base_wx/platform.h"
00034 
00035 #include <vigra/inspectimage.hxx>
00036 #include <vigra/transformimage.hxx>
00037 #include <vigra/basicimageview.hxx>
00038 #include <functional>  // std::bind 
00039 
00040 #include "hugin/config_defaults.h"
00041 #include "hugin/CPImageCtrl.h"
00042 #include "base_wx/wxImageCache.h"
00043 #include "hugin/CPEditorPanel.h"
00044 #include "hugin/MainFrame.h"
00045 #include "hugin/huginApp.h"
00046 #include "base_wx/wxcms.h"
00047 
00048 #include "vigra_ext/ImageTransforms.h"
00049 
00050 #if !wxCHECK_VERSION(3,0,0)
00051 #define wxPENSTYLE_SOLID wxSOLID
00052 #define wxPENSTYLE_DOT wxDOT
00053 #define wxBRUSHSTYLE_TRANSPARENT wxTRANSPARENT
00054 #define wxBRUSHSTYLE_SOLID wxSOLID
00055 #endif
00056 
00057 // definition of the control point event
00058 
00059 IMPLEMENT_DYNAMIC_CLASS( CPEvent, wxEvent )
00060 #if defined _WIN32 && defined Hugin_shared
00061 DEFINE_LOCAL_EVENT_TYPE( EVT_CPEVENT )
00062 #else
00063 DEFINE_EVENT_TYPE( EVT_CPEVENT )
00064 #endif
00065 
00066 CPEvent::CPEvent( )
00067 {
00068     SetEventType( EVT_CPEVENT );
00069     SetEventObject( (wxWindow *) NULL );
00070     mode = NONE;
00071 }
00072 
00073 CPEvent::CPEvent(wxWindow* win, hugin_utils::FDiff2D & p)
00074 {
00075     SetEventType( EVT_CPEVENT );
00076     SetEventObject( win );
00077     mode = NEW_POINT_CHANGED;
00078     point = p;
00079 }
00080 
00081 CPEvent::CPEvent(wxWindow *win, unsigned int cpNr)
00082 {
00083     SetEventType( EVT_CPEVENT );
00084     SetEventObject( win );
00085     mode = POINT_SELECTED;
00086     pointNr = cpNr;
00087 }
00088 
00089 CPEvent::CPEvent(wxWindow* win, unsigned int cpNr, const hugin_utils::FDiff2D & p)
00090 {
00091     SetEventType( EVT_CPEVENT );
00092     SetEventObject( win );
00093     mode = POINT_CHANGED;
00094     pointNr = cpNr;
00095     point = p;
00096 }
00097 
00098 CPEvent::CPEvent(wxWindow* win, const hugin_utils::FDiff2D & p1, const hugin_utils::FDiff2D & p2)
00099 {
00100     SetEventType(EVT_CPEVENT);
00101     SetEventObject(win);
00102     mode=DELETE_REGION_SELECTED;
00103     region=wxRect(hugin_utils::roundi(std::min(p1.x,p2.x)), hugin_utils::roundi(std::min(p1.y,p2.y)),
00104         abs(hugin_utils::roundi(p2.x-p1.x)),abs(hugin_utils::roundi(p2.y-p1.y)));
00105 };
00106 
00107 CPEvent::CPEvent(wxWindow* win, CPEventMode evt_mode, const hugin_utils::FDiff2D & p)
00108 {
00109     SetEventType(EVT_CPEVENT);
00110     SetEventObject(win);
00111     mode = evt_mode;
00112     point = p;
00113 }
00114 
00115 CPEvent::CPEvent(wxWindow* win, CPEventMode evt_mode, const HuginBase::ControlPoint cp)
00116 {
00117     SetEventType(EVT_CPEVENT);
00118     SetEventObject(win);
00119     mode=evt_mode;
00120     m_cp=cp;
00121 };
00122 
00123 CPEvent::CPEvent(wxWindow* win, CPEventMode evt_mode, size_t cpNr, const HuginBase::ControlPoint cp)
00124 {
00125     SetEventType(EVT_CPEVENT);
00126     SetEventObject(win);
00127     mode=evt_mode;
00128     pointNr=cpNr;
00129     m_cp=cp;
00130 };
00131 
00132 wxEvent * CPEvent::Clone() const
00133 {
00134     return new CPEvent(*this);
00135 }
00136 
00137 DisplayedControlPoint::DisplayedControlPoint(const HuginBase::ControlPoint& cp, CPImageCtrl* control, bool mirrored)
00138 {
00139     m_cp=cp;
00140     m_control=control;
00141     m_mirrored=mirrored;
00142     m_line=(m_cp.mode!=HuginBase::ControlPoint::X_Y) && (m_cp.image1Nr==m_cp.image2Nr);
00143 };
00144 
00145 void DisplayedControlPoint::SetColour(wxColour pointColour, wxColour textColour)
00146 {
00147     m_pointColour=pointColour;
00148     m_textColour=textColour;
00149 };
00150 
00151 void DisplayedControlPoint::SetLabel(wxString newLabel)
00152 {
00153     m_label=newLabel;
00154 };
00155 
00156 void DisplayedControlPoint::SetControl(CPImageCtrl* control)
00157 {
00158     m_control=control;
00159 };
00160 
00161 void DrawCross(wxDC& dc, wxPoint p, int l)
00162 {
00163     dc.DrawLine(p + wxPoint(-l, 0),
00164                 p + wxPoint(-1, 0));
00165     dc.DrawLine(p + wxPoint(2, 0),
00166                 p + wxPoint(l+1, 0));
00167     dc.DrawLine(p + wxPoint(0, -l),
00168                 p + wxPoint(0, -1));
00169     dc.DrawLine(p + wxPoint(0, 2),
00170                 p + wxPoint(0, l+1));
00171 };
00172 
00173 void DisplayedControlPoint::Draw(wxDC& dc, bool selected, bool newPoint)
00174 {
00175     if(m_control==NULL)
00176     {
00177         return;
00178     };
00179     // select color
00180     wxColour bgColor = m_pointColour;
00181     wxColour textColor = m_textColour;
00182     bool drawMag = false;
00183     if (selected)
00184     {
00185         bgColor = wxTheColourDatabase->Find(wxT("RED"));
00186         textColor = wxTheColourDatabase->Find(wxT("WHITE"));
00187         drawMag = !m_control->GetMouseInWindow() || m_control->GetForceMagnifier();
00188     }
00189     if (newPoint)
00190     {
00191         bgColor = wxTheColourDatabase->Find(wxT("YELLOW"));
00192         textColor = wxTheColourDatabase->Find(wxT("BLACK"));
00193         drawMag = true;
00194     }
00195 
00196     dc.SetPen(wxPen(wxT("WHITE"), 1, wxPENSTYLE_SOLID));
00197     dc.SetBrush(wxBrush(wxT("BLACK"),wxBRUSHSTYLE_TRANSPARENT));
00198 
00199     hugin_utils::FDiff2D pointInput = m_mirrored ? hugin_utils::FDiff2D(m_cp.x2, m_cp.y2) : hugin_utils::FDiff2D(m_cp.x1, m_cp.y1);
00200     hugin_utils::FDiff2D point = m_control->applyRot(pointInput);
00201     wxPoint p = m_control->roundP(m_control->scale(point));
00202     hugin_utils::FDiff2D pointInput2;
00203     wxPoint p2;
00204     if(m_line)
00205     {
00206         pointInput2=m_mirrored ? hugin_utils::FDiff2D(m_cp.x1, m_cp.y1) : hugin_utils::FDiff2D(m_cp.x2, m_cp.y2);
00207         hugin_utils::FDiff2D point2 = m_control->applyRot(pointInput2);
00208         p2 = m_control->roundP(m_control->scale(point2));
00209     };
00210     int l = 6;
00211     // draw cursor line, choose white or black
00212     vigra::Rect2D box;
00213     if(m_line)
00214     {
00215         box.setUpperLeft(vigra::Point2D(hugin_utils::roundi(std::min(m_cp.x1, m_cp.x2)) - l, hugin_utils::roundi(std::min(m_cp.y1, m_cp.y2)) - l));
00216         box.setSize(hugin_utils::roundi(std::abs(m_cp.x1 - m_cp.x2) + 2.0*l), hugin_utils::roundi(std::abs(m_cp.y1 - m_cp.y2) + 2.0*l));
00217     }
00218     else
00219     {
00220         box.setUpperLeft(vigra::Point2D(hugin_utils::roundi(pointInput.x - l), hugin_utils::roundi(pointInput.y - l)));
00221         box.setSize(2*l, 2*l);
00222     };                
00223     // only use part inside.
00224     box &= vigra::Rect2D(m_control->GetImg()->size());
00225     if(box.width()<=0 || box.height()<=0)
00226     {
00227         return;
00228     };
00229     // calculate mean "luminance value"
00230     vigra::FindAverage<vigra::UInt8> average;   // init functor
00231     vigra::RGBToGrayAccessor<vigra::RGBValue<vigra::UInt8> > lumac;
00232     vigra::inspectImage(m_control->GetImg()->upperLeft()+ box.upperLeft(),
00233                         m_control->GetImg()->upperLeft()+ box.lowerRight(),
00234                         lumac, average);
00235     if (average() < 150)
00236     {
00237         dc.SetPen(wxPen(wxT("WHITE"), 1, wxPENSTYLE_SOLID));
00238     }
00239     else
00240     {
00241         dc.SetPen(wxPen(wxT("BLACK"), 1, wxPENSTYLE_SOLID));
00242     }
00243 
00244     if(m_line)
00245     {
00246         DrawLine(dc);
00247         DrawCross(dc, p, l);
00248         DrawCross(dc, p2, l);
00249     }
00250     else
00251     {
00252         if (m_cp.mode != HuginBase::ControlPoint::X_Y)
00253         {
00254             DrawLineSegment(dc);
00255         };
00256         DrawCross(dc, p, l);
00257     };
00258     // calculate distance to the image boundaries,
00259     // decide where to put the label and magnifier
00260     m_labelPos=DrawTextMag(dc, p, pointInput, drawMag, textColor, bgColor);
00261     if(m_line)
00262     {
00263         m_labelPos2=DrawTextMag(dc, p2, pointInput2, drawMag, textColor, bgColor);
00264     };
00265 }
00266 
00267 wxRect DisplayedControlPoint::DrawTextMag(wxDC& dc, wxPoint p, hugin_utils::FDiff2D pointInput, bool drawMag, wxColour textColour, wxColour bgColour)
00268 {
00269     wxRect labelPos;
00270     int l = 6;
00271     wxSize clientSize = m_control->GetClientSize();
00272     int vx0, vy0;
00273     m_control->GetViewStart(&vx0, &vy0);
00274     wxFont font(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_LIGHT);
00275     dc.SetFont(font);
00276     wxPoint pClient(p.x - vx0, p.y - vy0);
00277     // space in upper left, upper right, lower left, lower right
00278     int maxDistUR = std::min(clientSize.x - pClient.x, pClient.y);
00279     int maxDistLL = std::min(pClient.x, clientSize.y - pClient.y);
00280     int maxDistLR = std::min(clientSize.x - pClient.x, clientSize.y - pClient.y);
00281 
00282     // text and magnifier offset
00283     int toff = l-1;
00284     // default to lower right
00285     wxPoint tul = p + wxPoint(toff,toff);
00286 
00287     // calculate text position and extend
00288     // width of border around text label
00289     int tB = 2;
00290     wxCoord tw, th;
00291     dc.GetTextExtent(m_label, &tw, &th);
00292 
00293     if (drawMag)
00294     {
00295         wxBitmap magBitmap = m_control->generateMagBitmap(pointInput, p);
00296         // TODO: select position depending on visible part of canvas
00297         wxPoint ulMag = tul;
00298         // choose placement of the magnifier
00299         int w = toff  + magBitmap.GetWidth()+3;
00300         int db = 5;
00301         if ( maxDistLR > w + db  )
00302         {
00303             ulMag = p + wxPoint(toff,toff);
00304         }
00305         else
00306         {
00307             if (maxDistLL > w + db)
00308             {
00309                 ulMag = p + wxPoint(-w, toff);
00310             }
00311             else
00312             {
00313                 if (maxDistUR > w + db)
00314                 {
00315                     ulMag = p + wxPoint(toff, -w);
00316                 }
00317                 else
00318                 {
00319                     ulMag = p + wxPoint(-w, -w);
00320                 }
00321             }
00322         };
00323 
00324         dc.DrawBitmap(magBitmap, ulMag);
00325         dc.SetPen(wxPen(wxT("BLACK"), 1, wxPENSTYLE_SOLID));
00326         dc.SetBrush(wxBrush(wxT("WHITE"),wxBRUSHSTYLE_TRANSPARENT));
00327 
00328         // draw Bevel
00329         int bw = magBitmap.GetWidth();
00330         int bh = magBitmap.GetHeight();
00331         dc.DrawLine(ulMag.x-1, ulMag.y+bh, 
00332                     ulMag.x+bw+1, ulMag.y+bh);
00333         dc.DrawLine(ulMag.x+bw, ulMag.y+bh, 
00334                     ulMag.x+bw, ulMag.y-2);
00335         dc.SetPen(wxPen(wxT("WHITE"), 1, wxPENSTYLE_SOLID));
00336         dc.DrawLine(ulMag.x-1, ulMag.y-1, 
00337                     ulMag.x+bw+1, ulMag.y-1);
00338         dc.DrawLine(ulMag.x-1, ulMag.y+bh, 
00339                     ulMag.x-1, ulMag.y-2);
00340     }
00341     // choose placement of text.
00342     int db = 5;
00343     int w = toff+tw+2*tB;
00344     if ( maxDistLR > w + db && (!drawMag) )
00345     {
00346         tul = p + wxPoint(toff,toff);
00347     }
00348     else
00349     {
00350         if (maxDistLL > w + db)
00351         {
00352             tul = p + wxPoint(-w, toff);
00353         }
00354         else
00355         {
00356             if (maxDistUR > w + db)
00357             {
00358                 tul = p + wxPoint(toff, -(toff) - (th+2*tB));
00359             }
00360             else
00361             {
00362                 tul = p + wxPoint(-w, -(toff) - (th+2*tB));
00363             };
00364         };
00365     };
00366 
00367     // draw background
00368     dc.SetPen(wxPen(textColour, 1, wxPENSTYLE_SOLID));
00369     dc.SetBrush(wxBrush(bgColour, wxBRUSHSTYLE_SOLID));
00370     dc.DrawRectangle(tul.x, tul.y, tw+2*tB+1, th+2*tB);
00371     labelPos.SetLeft(tul.x);
00372     labelPos.SetTop(tul.y);
00373     labelPos.SetWidth(tw+2*tB+1);
00374     labelPos.SetHeight(th+2*tB);
00375     // draw number
00376     dc.SetTextForeground(textColour);
00377     dc.DrawText(m_label, tul + wxPoint(tB,tB));
00378     return labelPos;
00379 };
00380 
00381 void DisplayedControlPoint::DrawLine(wxDC& dc)
00382 {
00383     if(m_control==NULL)
00384     {
00385         return;
00386     };
00387     hugin_utils::FDiff2D p1, p2, dp;
00388     // transform end points to pano space
00389     if(!m_control->getFirstInvTrans()->transformImgCoord(p1, hugin_utils::FDiff2D(m_cp.x1, m_cp.y1)))
00390     {
00391         return;
00392     };
00393     if(!m_control->getFirstInvTrans()->transformImgCoord(p2, hugin_utils::FDiff2D(m_cp.x2, m_cp.y2)))
00394     {
00395         return;
00396     };
00397     dp=p2-p1;
00398     int len = hugin_utils::roundi(sqrt((m_cp.x1 - m_cp.x2)*(m_cp.x1 - m_cp.x2) + (m_cp.y1 - m_cp.y2)*(m_cp.y1 - m_cp.y2))) + 1;
00399     if(len<5)
00400     {
00401         //very short line, draw straight line
00402         dc.DrawLine(m_control->roundP(m_control->scale(m_control->applyRot(hugin_utils::FDiff2D(m_cp.x1, m_cp.y1)))),
00403             m_control->roundP(m_control->scale(m_control->applyRot(hugin_utils::FDiff2D(m_cp.x2, m_cp.y2)))));
00404     }
00405     else
00406     {
00407         //longer line, draw correct line, taking output projection into account
00408         wxPoint* points=new wxPoint[len+1];
00409         for(size_t i=0; i<len+1; i++)
00410         {
00411             hugin_utils::FDiff2D p = p1 + dp*((double)i / len);
00412             // transform line coordinates back to image space
00413             if(!m_control->getFirstTrans()->transformImgCoord(p2, p))
00414             {
00415                 delete []points;
00416                 //fall through, draw direct line, not exact, but better than no line
00417                 dc.DrawLine(m_control->roundP(m_control->scale(m_control->applyRot(hugin_utils::FDiff2D(m_cp.x1, m_cp.y1)))),
00418                     m_control->roundP(m_control->scale(m_control->applyRot(hugin_utils::FDiff2D(m_cp.x2, m_cp.y2)))));
00419                 return;
00420             };
00421             points[i]=m_control->roundP(m_control->scale(m_control->applyRot(p2)));
00422         };
00423         dc.DrawLines(len+1, points);
00424         delete []points;
00425     };
00426 };
00427 
00428 void DisplayedControlPoint::DrawLineSegment(wxDC& dc)
00429 {
00430     if(m_control==NULL)
00431     {
00432         return;
00433     };
00434     // calculate line equation
00435     hugin_utils::FDiff2D p1_image = m_mirrored ? hugin_utils::FDiff2D(m_cp.x2, m_cp.y2) : hugin_utils::FDiff2D(m_cp.x1, m_cp.y1);
00436     hugin_utils::FDiff2D p2_image = m_mirrored ? hugin_utils::FDiff2D(m_cp.x1, m_cp.y1) : hugin_utils::FDiff2D(m_cp.x2, m_cp.y2);
00437     hugin_utils::FDiff2D p1, p2, dp;
00438     if(!m_control->getFirstInvTrans()->transformImgCoord(p1, p1_image))
00439     {
00440         return;
00441     };
00442     if(!m_control->getSecondInvTrans()->transformImgCoord(p2, p2_image))
00443     {
00444         return;
00445     };
00446     dp=p2-p1;
00447     // now find the parameter to draw an appropriate long line segment
00448     double f=1.0;
00449     int image_width=m_control->GetRealImageSize().GetWidth();
00450     int image_height=m_control->GetRealImageSize().GetHeight();
00451     int image_dimension=std::min(image_width, image_height);
00452     int line_length=-1;
00453     while(f>1e-4)
00454     {
00455         p2=p1+dp*f;
00456         if(m_control->getFirstTrans()->transformImgCoord(p2_image, p2))
00457         {
00458             double length=sqrt(p1_image.squareDistance(p2_image));
00459             if(length > 0.05f * image_dimension && length < 0.75f * image_dimension)
00460             {
00461                 line_length=hugin_utils::roundi(length);
00462                 break;
00463             };
00464         };
00465         f*=0.9;
00466     };
00467     // found no suitable length, don't draw line
00468     if(line_length<1)
00469     {
00470         return;
00471     };
00472     // now calc line positions
00473     wxPoint* points=new wxPoint[2*line_length+1];
00474     for(size_t i=0; i<2*line_length+1; i++)
00475     {
00476         hugin_utils::FDiff2D p = p1 + dp*f*(((double)i - line_length) / (2.0f*line_length));
00477         if(!m_control->getFirstTrans()->transformImgCoord(p2, p))
00478         {
00479             delete []points;
00480             return;
00481         };
00482         points[i]=m_control->roundP(m_control->scale(m_control->applyRot(p2)));
00483     };
00484     //and finally draw line segment
00485     dc.SetClippingRegion(wxPoint(0,0), m_control->GetBitmapSize());
00486     dc.DrawLines(2*line_length+1, points);
00487     dc.DestroyClippingRegion();
00488     delete []points;
00489 };
00490 
00491 const bool DisplayedControlPoint::isOccupiedLabel(const wxPoint mousePos) const
00492 {
00493     if(m_line)
00494     {
00495         return m_labelPos.Contains(mousePos) || m_labelPos2.Contains(mousePos);
00496     }
00497     else
00498     {
00499         return m_labelPos.Contains(mousePos);
00500     };
00501 };
00502 
00503 const bool DisplayedControlPoint::isOccupiedPos(const hugin_utils::FDiff2D &p) const
00504 {
00505     double d=m_control->invScale(3.0);
00506     if(m_line)
00507     {
00508         return (p.x < m_cp.x1 + d && p.x > m_cp.x1 - d && p.y < m_cp.y1 + d && p.y > m_cp.y1 - d) ||
00509                (p.x < m_cp.x2 + d && p.x > m_cp.x2 - d && p.y < m_cp.y2 + d && p.y > m_cp.y2 - d);
00510     }
00511     else
00512     {
00513         if(m_mirrored)
00514         {
00515             return (p.x < m_cp.x2 + d && p.x > m_cp.x2 - d && p.y < m_cp.y2 + d && p.y > m_cp.y2 - d);
00516         }
00517         else
00518         {
00519             return (p.x < m_cp.x1 + d && p.x > m_cp.x1 - d && p.y < m_cp.y1 + d && p.y > m_cp.y1 - d);
00520         };
00521     };
00522 };
00523 
00524 void DisplayedControlPoint::CheckSelection(const wxPoint mousePos, const hugin_utils::FDiff2D& p)
00525 {
00526     if(!m_line)
00527     {
00528         return;
00529     };
00530     double d=m_control->invScale(3.0);
00531     m_mirrored=m_labelPos2.Contains(mousePos) || 
00532         (p.x < m_cp.x2 + d && p.x > m_cp.x2 - d && p.y < m_cp.y2 + d && p.y > m_cp.y2 - d);
00533 };
00534 
00535 void DisplayedControlPoint::UpdateControlPointX(double x)
00536 {
00537     if(m_mirrored)
00538     {
00539         m_cp.x2=x;
00540     }
00541     else
00542     {
00543         m_cp.x1=x;
00544     };
00545 };
00546 
00547 void DisplayedControlPoint::UpdateControlPointY(double y)
00548 {
00549     if(m_mirrored)
00550     {
00551         m_cp.y2=y;
00552     }
00553     else
00554     {
00555         m_cp.y1=y;
00556     };
00557 };
00558 
00559 void DisplayedControlPoint::UpdateControlPoint(hugin_utils::FDiff2D newPoint)
00560 {
00561     if(m_mirrored)
00562     {
00563         m_cp.x2=newPoint.x;
00564         m_cp.y2=newPoint.y;
00565     }
00566     else
00567     {
00568         m_cp.x1=newPoint.x;
00569         m_cp.y1=newPoint.y;
00570     };
00571 };
00572 
00573 void DisplayedControlPoint::ShiftControlPoint(hugin_utils::FDiff2D shift)
00574 {
00575     if(m_mirrored)
00576     {
00577         m_cp.x2+=shift.x;
00578         m_cp.y2+=shift.y;
00579     }
00580     else
00581     {
00582         m_cp.x1+=shift.x;
00583         m_cp.y1+=shift.y;
00584     };
00585 };
00586 
00587 void DisplayedControlPoint::StartLineControlPoint(hugin_utils::FDiff2D newPoint)
00588 {
00589     //start a new line control point
00590     m_line=true;
00591     m_mirrored=true;
00592     m_label=_("new");
00593     m_cp.image1Nr=UINT_MAX;
00594     m_cp.x1=newPoint.x;
00595     m_cp.y1=newPoint.y;
00596     m_cp.image2Nr=m_cp.image1Nr;
00597     m_cp.x2=m_cp.x1;
00598     m_cp.y2=m_cp.y1;
00599     m_cp.mode = HuginBase::ControlPoint::X;
00600 };
00601 
00602 hugin_utils::FDiff2D DisplayedControlPoint::GetPos() const
00603 {
00604     return m_mirrored ? hugin_utils::FDiff2D(m_cp.x2, m_cp.y2) : hugin_utils::FDiff2D(m_cp.x1, m_cp.y1);
00605 };
00606 
00607 bool DisplayedControlPoint::operator==(const DisplayedControlPoint other)
00608 {
00609     return m_cp==other.GetControlPoint() && m_mirrored == other.IsMirrored() && m_label == other.GetLabel();
00610 };
00611 
00612 // our image control
00613 BEGIN_EVENT_TABLE(CPImageCtrl, wxScrolledWindow)
00614     EVT_SIZE(CPImageCtrl::OnSize)
00615     EVT_CHAR(CPImageCtrl::OnKey)
00616 //    EVT_KEY_UP(CPImageCtrl::OnKeyUp)
00617     EVT_KEY_DOWN(CPImageCtrl::OnKeyDown)
00618     EVT_LEAVE_WINDOW(CPImageCtrl::OnMouseLeave)
00619     EVT_ENTER_WINDOW(CPImageCtrl::OnMouseEnter)
00620     EVT_MOTION(CPImageCtrl::mouseMoveEvent)
00621     EVT_LEFT_DOWN(CPImageCtrl::mousePressLMBEvent)
00622     EVT_LEFT_UP(CPImageCtrl::mouseReleaseLMBEvent)
00623     EVT_RIGHT_DOWN(CPImageCtrl::mousePressRMBEvent)
00624     EVT_RIGHT_UP(CPImageCtrl::mouseReleaseRMBEvent)
00625     EVT_MIDDLE_DOWN(CPImageCtrl::mousePressMMBEvent)
00626     EVT_MIDDLE_UP(CPImageCtrl::mouseReleaseMMBEvent)
00627     EVT_TIMER(-1, CPImageCtrl::OnTimer)
00628 END_EVENT_TABLE()
00629 
00630 bool CPImageCtrl::Create(wxWindow * parent, wxWindowID id,
00631                          const wxPoint& pos,
00632                          const wxSize& size,
00633                          long style,
00634                          const wxString& name)
00635 {
00636     wxScrolledWindow::Create(parent, id, pos, size, style, name);
00637     selectedPointNr = 0;
00638     editState = NO_IMAGE;
00639     scaleFactor = 1;
00640     fitToWindow = false;
00641     m_showSearchArea = false;
00642     m_searchRectWidth = 0;
00643     m_showTemplateArea = false;
00644     m_templateRectWidth = 0;
00645     m_editPanel = 0;
00646     m_imgRotation = ROT0;
00647     m_sameImage = false;
00648 
00649     wxString filename;
00650 
00651 #if defined(__WXMSW__) 
00652     wxString cursorPath = huginApp::Get()->GetXRCPath() + wxT("/data/cursor_cp_pick.cur");
00653     m_CPSelectCursor = new wxCursor(cursorPath, wxBITMAP_TYPE_CUR);
00654 #else
00655     m_CPSelectCursor = new wxCursor(wxCURSOR_CROSS);
00656 #endif
00657     SetCursor(*m_CPSelectCursor);
00658 
00659     // TODO: define custom, light background colors.
00660     pointColors.push_back(wxTheColourDatabase->Find(wxT("BLUE")));
00661     textColours.push_back(wxTheColourDatabase->Find(wxT("WHITE")));
00662 
00663     pointColors.push_back(wxTheColourDatabase->Find(wxT("GREEN")));
00664     textColours.push_back(wxTheColourDatabase->Find(wxT("WHITE")));
00665 
00666     pointColors.push_back(wxTheColourDatabase->Find(wxT("CYAN")));
00667     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00668     pointColors.push_back(wxTheColourDatabase->Find(wxT("GOLD")));
00669     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00670 
00671     pointColors.push_back(wxTheColourDatabase->Find(wxT("NAVY")));
00672     textColours.push_back(wxTheColourDatabase->Find(wxT("WHITE")));
00673 
00674     pointColors.push_back(wxTheColourDatabase->Find(wxT("DARK TURQUOISE")));
00675     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00676 
00677     pointColors.push_back(wxTheColourDatabase->Find(wxT("SALMON")));
00678     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00679 
00680     pointColors.push_back(wxTheColourDatabase->Find(wxT("MAROON")));
00681     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00682 
00683     pointColors.push_back(wxTheColourDatabase->Find(wxT("KHAKI")));
00684     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00685 
00686     m_searchRectWidth = 120;
00687     m_mouseInWindow = false;
00688     m_forceMagnifier = false;
00689     m_timer.SetOwner(this);
00690 
00691     return true;
00692 }
00693 
00694 void CPImageCtrl::Init(CPEditorPanel * parent)
00695 {
00696     m_editPanel = parent;
00697     m_sameImage = false;
00698 }
00699 
00700 CPImageCtrl::~CPImageCtrl()
00701 {
00702     DEBUG_TRACE("dtor");
00703     this->SetCursor(wxNullCursor);
00704     delete m_CPSelectCursor;
00705     DEBUG_TRACE("dtor end");
00706 }
00707 
00708 void CPImageCtrl::OnDraw(wxDC & dc)
00709 {
00710     wxSize vSize = GetClientSize();
00711     // draw image (FIXME, redraw only visible regions.)
00712     if (editState != NO_IMAGE && m_img.get()) {
00713                 //clear the blank rectangle to the left of the image
00714         if (bitmap.GetWidth() < vSize.GetWidth()) {
00715             dc.SetPen(wxPen(GetBackgroundColour(), 1, wxPENSTYLE_SOLID));
00716             dc.SetBrush(wxBrush(GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
00717             dc.DrawRectangle(bitmap.GetWidth(), 0,
00718                              vSize.GetWidth() - bitmap.GetWidth(),vSize.GetHeight());
00719         }
00720                 //clear the blank rectangle below the image
00721         if (bitmap.GetHeight() < vSize.GetHeight()) {
00722             dc.SetPen(wxPen(GetBackgroundColour(), 1, wxPENSTYLE_SOLID));
00723             dc.SetBrush(wxBrush(GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
00724                         dc.DrawRectangle(0, bitmap.GetHeight(),
00725                              vSize.GetWidth(), vSize.GetHeight() - bitmap.GetHeight());
00726         }
00727         dc.DrawBitmap(bitmap,0,0);
00728         } else {
00729                 // clear the rectangle and exit
00730         dc.SetPen(wxPen(GetBackgroundColour(), 1, wxPENSTYLE_SOLID));
00731         dc.SetBrush(wxBrush(GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
00732         dc.Clear();
00733                 return;
00734         }
00735 
00736     // draw known points.
00737     for(size_t i=0; i<m_points.size(); i++)
00738     {
00739         if (!(editState == KNOWN_POINT_SELECTED && i==selectedPointNr))
00740         {
00741             m_points[i].Draw(dc, false);
00742         };
00743     }
00744 
00745     switch(editState) {
00746     case NEW_POINT_SELECTED:
00747         // Boundary check
00748         if ((newPoint.x < 0) || (newPoint.y < 0)) {
00749             // Tried to create a point outside of the canvas.  Ignore it.
00750             break;
00751         } 
00752         {
00753             DisplayedControlPoint dsp(HuginBase::ControlPoint(0, newPoint.x, newPoint.y, 0, 0, 0), this, false);
00754             dsp.SetLabel(_("new"));
00755             dsp.Draw(dc, false, true);
00756         }
00757         if (m_showTemplateArea) {
00758             dc.SetLogicalFunction(wxINVERT);
00759             dc.SetPen(wxPen(wxT("RED"), 1, wxPENSTYLE_SOLID));
00760             dc.SetBrush(wxBrush(wxT("WHITE"), wxBRUSHSTYLE_TRANSPARENT));
00761             wxPoint upperLeft = applyRot(roundP(newPoint));
00762             upperLeft = scale(upperLeft);
00763 
00764             int width = scale(m_templateRectWidth);
00765 
00766             dc.DrawRectangle(upperLeft.x-width, upperLeft.y-width, 2*width, 2*width);
00767             dc.SetLogicalFunction(wxCOPY);
00768         }
00769 
00770         break;
00771     case NEW_LINE_CREATING:
00772         m_selectedPoint.Draw(dc, false, true);
00773         break;
00774     case KNOWN_POINT_SELECTED:
00775         m_points[selectedPointNr].Draw(dc, true);
00776         break;
00777     case SELECT_DELETE_REGION:
00778     case NO_SELECTION:
00779     case NO_IMAGE:
00780         break;
00781     }
00782 
00783     if (m_showSearchArea && m_mousePos.x != -1){
00784         dc.SetLogicalFunction(wxINVERT);
00785         dc.SetPen(wxPen(wxT("WHITE"), 1, wxPENSTYLE_SOLID));
00786         dc.SetBrush(wxBrush(wxT("WHITE"), wxBRUSHSTYLE_TRANSPARENT));
00787 
00788         hugin_utils::FDiff2D upperLeft = applyRot(m_mousePos);
00789         upperLeft = scale(upperLeft);
00790         int width = scale(m_searchRectWidth);
00791         DEBUG_DEBUG("drawing rect " << upperLeft << " with width " << 2*width << " orig: " << m_searchRectWidth*2  << " scale factor: " << getScaleFactor());
00792 
00793         dc.DrawRectangle(hugin_utils::roundi(upperLeft.x - width), hugin_utils::roundi(upperLeft.y - width), 2 * width, 2 * width);
00794         dc.SetLogicalFunction(wxCOPY);
00795     }
00796 }
00797 
00798 class ScalingTransform
00799 {
00800 public:
00801     explicit ScalingTransform(double scale)
00802     : m_scale(scale) {};
00803 
00804     bool transformImgCoord(double & sx, double & sy, double x, double y)
00805     {
00806         sx = m_scale*x;
00807         sy = m_scale*y;
00808         return true;
00809     }
00810 
00811     double m_scale;
00812 };
00813 
00814 wxBitmap CPImageCtrl::generateMagBitmap(hugin_utils::FDiff2D point, wxPoint canvasPos) const
00815 {
00816     typedef vigra::RGBValue<vigra::UInt8> VT;
00817     DEBUG_TRACE("")
00818 
00819     // draw magnified image (TODO: warp!)
00820     double magScale = 3.0;
00821     wxConfigBase::Get()->Read(wxT("/CPEditorPanel/MagnifierScale"), &magScale);
00822     // width (and height) of magnifier region (output), should be odd
00823     int magWidth = wxConfigBase::Get()->Read(wxT("/CPEditorPanel/MagnifierWidth"),61l);
00824     int hw = magWidth/2;
00825     magWidth = hw*2+1;
00826 
00827     // setup simple scaling transformation function.
00828     ScalingTransform transform(1.0/magScale);
00829     ScalingTransform invTransform(magScale);
00830     wxImage img(magWidth, magWidth);
00831     vigra::BasicImageView<VT> magImg((VT*)img.GetData(), magWidth,magWidth);
00832     vigra::BImage maskImg(magWidth, magWidth);
00833     vigra_ext::PassThroughFunctor<vigra::UInt8> ptf;
00834 
00835 
00836     // middle pixel
00837     double mx, my;
00838     invTransform.transformImgCoord(mx, my, point.x, point.y);
00839 
00840     // apply the transform
00841     AppBase::DummyProgressDisplay progDisp;
00842     vigra_ext::transformImageIntern(vigra::srcImageRange(*(m_img->image8)),
00843                          vigra::destImageRange(magImg),
00844                          vigra::destImage(maskImg),
00845                          transform,
00846                          ptf,
00847                          vigra::Diff2D(hugin_utils::roundi(mx - hw),
00848                                        hugin_utils::roundi(my - hw)),
00849                          vigra_ext::interp_cubic(),
00850                          false,
00851                          &progDisp,
00852                          false);
00853 
00854     // TODO: contrast enhancement
00855     vigra::FindMinMax<vigra::UInt8> minmax;
00856     vigra::inspectImage(vigra::srcImageRange(magImg), minmax);
00857 
00858     // transform to range 0...255
00859     vigra::transformImage(vigra::srcImageRange(magImg), vigra::destImage(magImg),
00860                           vigra::linearRangeMapping(
00861                             VT(minmax.min), VT(minmax.max),               // src range
00862                             VT(0, 0, 0), VT(255, 255, 255)) // dest range
00863                           );
00864 //    vigra::transformImage(srcImageRange(magImg), destImage(magImg),
00865 //       vigra::BrightnessContrastFunctor<float>(brightness, contrast, minmax.min, minmax.max));
00866 
00867     // draw cursor
00868     for(int x=0; x < magWidth; x++) {
00869         VT p =magImg(x,hw+1);
00870         vigra::UInt8 v = 0.3/255*p.red() + 0.6/255*p.green() + 0.1/255*p.blue() < 0.5 ? 255 : 0;
00871         p[0] = v;
00872         p[1] = v;
00873         p[2] = v;
00874         magImg(x,hw+1) = p;
00875         p = magImg(hw+1, x);
00876         v = 0.3/255*p.red() + 0.6/255*p.green() + 0.1/255*p.blue() < 0.5 ? 255 : 0;
00877         p[0] = v;
00878         p[1] = v;
00879         p[2] = v;
00880         magImg(hw+1, x) = p;
00881     }
00882 
00883     // rotate image according to current display
00884     switch(m_imgRotation) {
00885         case ROT90:
00886             img = img.Rotate90(true);
00887             break;
00888         case ROT180:
00889             // this is slower than it needs to be...
00890             img = img.Rotate90(true);
00891             img = img.Rotate90(true);
00892             break;
00893         case ROT270:
00894             img = img.Rotate90(false);
00895             break;
00896         default:
00897             break;
00898     }
00899     return wxBitmap (img);
00900 }
00901 
00902 wxSize CPImageCtrl::DoGetBestSize() const
00903 {
00904     return wxSize(imageSize.GetWidth(),imageSize.GetHeight());
00905 }
00906 
00907 
00908 void CPImageCtrl::setImage(const std::string & file, ImageRotation imgRot)
00909 {
00910     DEBUG_TRACE("setting Image " << file);
00911     imageFilename = file;
00912     m_sameImage=false;
00913     wxString fn(imageFilename.c_str(),HUGIN_CONV_FILENAME);
00914     if (wxFileName::FileExists(fn)) {
00915         m_imgRotation = imgRot;
00916         m_img = ImageCache::getInstance().getImageIfAvailable(imageFilename);
00917         editState = NO_SELECTION;
00918         if (m_img.get()) {
00919             rescaleImage();
00920         } else {
00921             // load the image in the background.
00922             m_imgRequest = ImageCache::getInstance().requestAsyncImage(imageFilename);
00923             m_imgRequest->ready.push_back(
00924                 std::bind(&CPImageCtrl::OnImageLoaded, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)
00925                 );
00926             // With m_img.get() 0, everything will act as normal except drawing.
00927         }
00928     } else {
00929         editState = NO_IMAGE;
00930         bitmap = wxBitmap();
00931         // delete the image (release shared_ptr)
00932         // create an empty image.
00933         m_img = ImageCache::EntryPtr(new ImageCache::Entry);
00934     }
00935 }
00936 
00937 void CPImageCtrl::setTransforms(HuginBase::PTools::Transform* firstTrans, HuginBase::PTools::Transform* firstInvTrans, HuginBase::PTools::Transform* secondInvTrans)
00938 {
00939     m_firstTrans=firstTrans;
00940     m_firstInvTrans=firstInvTrans;
00941     m_secondInvTrans=secondInvTrans;
00942 };
00943 
00944 void CPImageCtrl::setSameImage(bool sameImage)
00945 {
00946     m_sameImage=sameImage;
00947 };
00948 
00949 void CPImageCtrl::OnImageLoaded(ImageCache::EntryPtr entry, std::string filename, bool load_small)
00950 {
00951     // check we are still displaying this image
00952     if (imageFilename == filename)
00953     {
00954         m_img = entry;
00955         rescaleImage();
00956     }
00957 }
00958 
00959 void CPImageCtrl::rescaleImage()
00960 {
00961     if (editState == NO_IMAGE || !m_img.get()) {
00962         return;
00963     }
00964     wxImage img = imageCacheEntry2wxImage(m_img);
00965     if (img.GetWidth() == 0) {
00966         return;
00967     }
00968     imageSize = wxSize(img.GetWidth(), img.GetHeight());
00969     m_realSize = imageSize;
00970     if (fitToWindow) {
00971         scaleFactor = calcAutoScaleFactor(imageSize);
00972     }
00973     DEBUG_DEBUG("src image size "
00974                 << imageSize.GetHeight() << "x" << imageSize.GetWidth());
00975     if (getScaleFactor() == 1.0) {
00976         //the icc correction would work on the original cached image file
00977         //therefore we need to create a copy to work on it
00978         img = img.Copy();
00979     }
00980     else
00981     {
00982         // rescale image
00983         imageSize.SetWidth(scale(imageSize.GetWidth()));
00984         imageSize.SetHeight(scale(imageSize.GetHeight()));
00985         DEBUG_DEBUG("rescaling to " << imageSize.GetWidth() << "x"
00986             << imageSize.GetHeight());
00987         img = img.Scale(imageSize.GetWidth(), imageSize.GetHeight());
00988     };
00989     // need to rotate full image. warning. this can be very memory intensive
00990     if (m_imgRotation != ROT0)
00991     {
00992         switch (m_imgRotation)
00993         {
00994             case ROT90:
00995                 img = img.Rotate90(true);
00996                 break;
00997             case ROT180:
00998                 // this is slower than it needs to be...
00999                 img = img.Rotate90(true);
01000                 img = img.Rotate90(true);
01001                 break;
01002             case ROT270:
01003                 img = img.Rotate90(false);
01004                 break;
01005             default:
01006                 break;
01007         };
01008     };
01009     // do color correction only if input image has icc profile or if we found a monitor profile
01010     if (!m_img->iccProfile->empty() || huginApp::Get()->HasMonitorProfile())
01011     {
01012         HuginBase::Color::CorrectImage(img, *(m_img->iccProfile), huginApp::Get()->GetMonitorProfile());
01013     };
01014     bitmap = wxBitmap(img);
01015 
01016     if (m_imgRotation == ROT90 || m_imgRotation == ROT270) {
01017         SetVirtualSize(imageSize.GetHeight(), imageSize.GetWidth());
01018     } else {
01019         SetVirtualSize(imageSize.GetWidth(), imageSize.GetHeight());
01020     }
01021     SetScrollRate(1,1);
01022     Refresh(FALSE);
01023 }
01024 
01025 void CPImageCtrl::setCtrlPoint(const HuginBase::ControlPoint& cp, const bool mirrored)
01026 {
01027     DisplayedControlPoint dcp(cp, this, mirrored);
01028     dcp.SetColour(pointColors[m_points.size() % pointColors.size()], textColours[m_points.size() % textColours.size()]);
01029     dcp.SetLabel(wxString::Format(wxT("%lu"), (unsigned long int)m_points.size()));
01030     m_points.push_back(dcp);
01031 }
01032 
01033 void CPImageCtrl::clearCtrlPointList()
01034 {
01035     m_points.clear();
01036     if(editState == KNOWN_POINT_SELECTED)
01037     {
01038         editState = NO_SELECTION;
01039     };
01040     selectedPointNr = UINT_MAX;
01041 };
01042 
01043 void CPImageCtrl::clearNewPoint()
01044 {
01045     DEBUG_TRACE("clearNewPoint");
01046     if (editState != NO_IMAGE) {
01047         editState = NO_SELECTION;
01048     }
01049 }
01050 
01051 
01052 void CPImageCtrl::selectPoint(unsigned int nr)
01053 {
01054     DEBUG_TRACE("nr: " << nr);
01055     if (nr < m_points.size()) {
01056         selectedPointNr = nr;
01057         editState = KNOWN_POINT_SELECTED;
01058         showPosition(m_points[nr].GetPos());
01059         update();
01060     } else {
01061         DEBUG_DEBUG("trying to select invalid point nr: " << nr << ". Nr of points: " << m_points.size());
01062     }
01063 }
01064 
01065 void CPImageCtrl::deselect()
01066 {
01067     DEBUG_TRACE("deselecting points");
01068     if (editState == KNOWN_POINT_SELECTED) {
01069         editState = NO_SELECTION;
01070     }
01071     // update view
01072     update();
01073 }
01074 
01075 void CPImageCtrl::showPosition(hugin_utils::FDiff2D point, bool warpPointer)
01076 {
01077     DEBUG_DEBUG("x: " << point.x  << " y: " << point.y);
01078     // transform and scale the co-ordinate to the screen.
01079     point = applyRot(point);
01080     point = scale(point);
01081     int x = hugin_utils::roundi(point.x);
01082     int y = hugin_utils::roundi(point.y);
01083 
01084     wxSize sz = GetClientSize();
01085     int scrollx = x - sz.GetWidth()/2;
01086     int scrolly = y - sz.GetHeight()/2;
01087     Scroll(scrollx, scrolly);
01088     if (warpPointer) {
01089         int sx,sy;
01090         GetViewStart(&sx, &sy);
01091         DEBUG_DEBUG("relative coordinages: " << x-sx << "," << y-sy);
01092         WarpPointer(x-sx,y-sy);
01093     }
01094 }
01095 
01096 CPImageCtrl::EditorState CPImageCtrl::isOccupied(wxPoint mousePos, const hugin_utils::FDiff2D &p, unsigned int & pointNr) const
01097 {
01098     // check if mouse is hovering over a label
01099     if(m_points.size()>0)
01100     {
01101         for(int i=m_points.size()-1; i>=0; i--)
01102         {
01103             if(m_points[i].isOccupiedLabel(mousePos))
01104             {
01105                 pointNr = i;
01106                 return KNOWN_POINT_SELECTED;
01107             }
01108         };
01109         // check if mouse is over a known point
01110         for(std::vector<DisplayedControlPoint>::const_iterator it=m_points.begin(); it!=m_points.end(); ++it)
01111         {
01112             if(it->isOccupiedPos(p))
01113             {
01114                 pointNr = it - m_points.begin();
01115                 return KNOWN_POINT_SELECTED;
01116             }
01117         };
01118     };
01119 
01120     return NEW_POINT_SELECTED;
01121 }
01122 
01123 void CPImageCtrl::DrawSelectionRectangle(hugin_utils::FDiff2D pos1,hugin_utils::FDiff2D pos2)
01124 {
01125     wxClientDC dc(this);
01126     PrepareDC(dc);
01127     dc.SetLogicalFunction(wxINVERT);
01128     dc.SetPen(wxPen(*wxWHITE,1, wxPENSTYLE_DOT));
01129     dc.SetBrush(*wxTRANSPARENT_BRUSH);
01130     wxPoint p1=roundP(scale(applyRot(pos1)));
01131     wxPoint p2=roundP(scale(applyRot(pos2)));
01132     dc.DrawRectangle(p1.x,p1.y,p2.x-p1.x,p2.y-p1.y);
01133 };
01134 
01135 void CPImageCtrl::mouseMoveEvent(wxMouseEvent& mouse)
01136 {
01137     if (!m_img.get()) return; // ignore events if no image loaded.
01138     wxPoint unScrolledMousePos;
01139     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
01140                            &unScrolledMousePos.x, & unScrolledMousePos.y);
01141     hugin_utils::FDiff2D mpos(unScrolledMousePos.x, unScrolledMousePos.y);
01142     bool doUpdate = false;
01143     mpos = applyRotInv(invScale(mpos));
01144     // if mouseclick is out of image, ignore
01145     if ((mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight()) && editState!=SELECT_DELETE_REGION)
01146     {
01147         return;
01148     }
01149 
01150 //    DEBUG_DEBUG(" pos:" << mpos.x << ", " << mpos.y);
01151     // only if the shift key is not pressed.
01152     if (mouse.LeftIsDown() && ! mouse.ShiftDown()) {
01153         switch(editState) {
01154         case NO_SELECTION:
01155             DEBUG_DEBUG("mouse down movement without selection, in NO_SELECTION state!");
01156             break;
01157         case KNOWN_POINT_SELECTED:
01158             if (mpos.x >= 0 && mpos.x <= m_realSize.GetWidth()){
01159                 m_points[selectedPointNr].UpdateControlPointX(mpos.x);
01160             } else if (mpos.x < 0) {
01161                 m_points[selectedPointNr].UpdateControlPointX(0);
01162             } else if (mpos.x > m_realSize.GetWidth()) {
01163                 m_points[selectedPointNr].UpdateControlPointX(m_realSize.GetWidth());
01164             }
01165 
01166             if (mpos.y >= 0 && mpos.y <= m_realSize.GetHeight()){
01167                 m_points[selectedPointNr].UpdateControlPointY(mpos.y);
01168             } else if (mpos.y < 0) {
01169                 m_points[selectedPointNr].UpdateControlPointY(0);
01170             } else if (mpos.y > m_realSize.GetHeight()) {
01171                 m_points[selectedPointNr].UpdateControlPointY(m_realSize.GetHeight());
01172             }
01173             // emit a notify event here.
01174             //
01175             //emit(pointMoved(selectedPointNr, points[selectedPointNr]));
01176             // do more intelligent updating here?
01177             doUpdate = true;
01178             break;
01179             // not possible.
01180         case NEW_POINT_SELECTED:
01181             DEBUG_DEBUG("WARNING: mouse move in new point state")
01182             newPoint = mpos;
01183             doUpdate = true;
01184             break;
01185         case NEW_LINE_CREATING:
01186             m_selectedPoint.UpdateControlPoint(mpos);
01187             doUpdate = true;
01188             break;
01189         case SELECT_DELETE_REGION:
01190         case NO_IMAGE:
01191             break;
01192         }
01193     }
01194 
01195     if ((mouse.MiddleIsDown() || mouse.ShiftDown() || mouse.m_controlDown ) && editState!=SELECT_DELETE_REGION) {
01196         // scrolling with the mouse
01197         if (m_mouseScrollPos !=mouse.GetPosition()) {
01198             wxPoint delta_ = mouse.GetPosition() - m_mouseScrollPos;
01199             double speed = (double)GetVirtualSize().GetHeight() / GetClientSize().GetHeight();
01200 //          int speed = wxConfigBase::Get()->Read(wxT("/CPEditorPanel/scrollSpeed"),5);
01201             wxPoint delta;
01202             delta.x = hugin_utils::roundi(delta_.x * speed);
01203             delta.y =  hugin_utils::roundi(delta_.y * speed);
01204             // scrolling is done later
01205             if (mouse.ShiftDown()) {
01206                 // emit scroll event, so that other window can be scrolled
01207                 // as well.
01208                 CPEvent e(this, CPEvent::SCROLLED, hugin_utils::FDiff2D(delta.x, delta.y));
01209                 emit(e);
01210             } else {
01211                 // scroll only our window
01212                 ScrollDelta(delta);
01213             }
01214             m_mouseScrollPos = mouse.GetPosition();
01215         }
01216     }
01217 
01218     if(mouse.RightIsDown() && editState==SELECT_DELETE_REGION)
01219     {
01220         //update selection rectangle
01221         DrawSelectionRectangle(rectStartPos,m_mousePos);
01222         DrawSelectionRectangle(rectStartPos,mpos);
01223     }
01224 //    DEBUG_DEBUG("ImageDisplay: mouse move, state: " << editState);
01225 
01226     // draw a rectangle
01227     if (m_showSearchArea) {
01228         doUpdate = true;
01229     }
01230 
01231     unsigned int selPointNr;
01232     if (isOccupied(unScrolledMousePos, mpos, selPointNr) == KNOWN_POINT_SELECTED &&
01233         (! (editState == KNOWN_POINT_SELECTED && selectedPointNr == selPointNr) ) ) {
01234         SetCursor(wxCursor(wxCURSOR_ARROW));
01235     } else {
01236         SetCursor(*m_CPSelectCursor);
01237     }
01238 
01239     m_mousePos = mpos;
01240     // repaint
01241     if (doUpdate) {
01242         update();
01243     }
01244 }
01245 
01246 
01247 void CPImageCtrl::mousePressLMBEvent(wxMouseEvent& mouse)
01248 {
01249     DEBUG_DEBUG("LEFT MOUSE DOWN");
01250     if (!m_img.get()) return; // ignore events if no image loaded.
01251     //ignore left mouse button if selecting region with right mouse button
01252     if(editState==SELECT_DELETE_REGION) 
01253         return;
01254     wxPoint unScrolledMousePos;
01255     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
01256                            &unScrolledMousePos.x, & unScrolledMousePos.y);
01257     hugin_utils::FDiff2D mpos(unScrolledMousePos.x, unScrolledMousePos.y);
01258     mpos = applyRotInv(invScale(mpos));
01259     DEBUG_DEBUG("mousePressEvent, pos:" << mpos.x
01260                 << ", " << mpos.y);
01261     // if mouseclick is out of image, ignore
01262     if (mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight()) {
01263         return;
01264     }
01265     unsigned int selPointNr = 0;
01266     EditorState clickState = isOccupied(unScrolledMousePos, mpos, selPointNr);
01267     if (mouse.LeftDown() && editState != NO_IMAGE
01268         && mpos.x < m_realSize.x && mpos.y < m_realSize.y)
01269     {
01270         // we can always select a new point
01271         if (clickState == KNOWN_POINT_SELECTED) {
01272             DEBUG_DEBUG("click on point: " << selPointNr);
01273             selectedPointNr = selPointNr;
01274             m_points[selectedPointNr].CheckSelection(unScrolledMousePos, mpos);
01275             m_selectedPoint = m_points[selectedPointNr];
01276             editState = clickState;
01277             CPEvent e( this, selectedPointNr);
01278             m_forceMagnifier = true;
01279             emit(e);
01280         } else if (clickState == NEW_POINT_SELECTED) {
01281             DEBUG_DEBUG("click on new space, select new point");
01282             if(m_sameImage && mouse.AltDown())
01283             {
01284                 editState = NEW_LINE_CREATING;
01285                 m_selectedPoint.StartLineControlPoint(mpos);
01286                 m_selectedPoint.SetControl(this);
01287             }
01288             else
01289             {
01290                 editState = NEW_POINT_SELECTED;
01291             };
01292             newPoint = mpos;
01293         } else {
01294             DEBUG_ERROR("invalid state " << clickState << " on mouse down");
01295         }
01296 //        DEBUG_DEBUG("ImageDisplay: mouse down, state change: " << oldstate
01297 //                    << " -> " << editState);
01298     }
01299     m_mousePos = mpos;
01300 }
01301 
01302 void CPImageCtrl::OnTimer(wxTimerEvent & e)
01303 {
01304     if (!m_img.get()) return; // ignore events if no image loaded.
01305     m_forceMagnifier = false;
01306     update();
01307 }
01308 
01309 void CPImageCtrl::mouseReleaseLMBEvent(wxMouseEvent& mouse)
01310 {
01311     DEBUG_DEBUG("LEFT MOUSE UP");
01312     if (!m_img.get()) return; // ignore events if no image loaded.
01313     //ignore left mouse button if selecting region with right mouse button
01314     if(editState==SELECT_DELETE_REGION) 
01315         return;
01316 
01317     m_timer.Start(2000, true);
01318 
01319     wxPoint mpos_;
01320     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
01321                            &mpos_.x, & mpos_.y);
01322     hugin_utils::FDiff2D mpos(mpos_.x, mpos_.y);
01323     mpos = applyRotInv(invScale(mpos));
01324     DEBUG_DEBUG("mouseReleaseEvent, pos:" << mpos.x
01325                 << ", " << mpos.y);
01326     // if mouseclick is out of image, ignore
01327     if (mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight()) {
01328         return;
01329     }
01330 //    EditorState oldState = editState;
01331     if (mouse.LeftUp()) {
01332         switch(editState) {
01333         case NO_SELECTION:
01334             DEBUG_DEBUG("mouse release without selection");
01335             break;
01336         case KNOWN_POINT_SELECTED:
01337         {
01338             DEBUG_DEBUG("mouse release with known point " << selectedPointNr);
01339             if (! (m_selectedPoint == m_points[selectedPointNr]) ) {
01340                 CPEvent e( this, CPEvent::POINT_CHANGED, selectedPointNr, m_points[selectedPointNr].GetControlPoint());
01341                 emit(e);
01342             }
01343             break;
01344         }
01345         case NEW_POINT_SELECTED:
01346         {
01347             DEBUG_DEBUG("new Point changed (event fire): x:" << mpos.x << " y:" << mpos.y);
01348             // fire the wxWin event
01349             CPEvent e( this, newPoint);
01350             emit(e);
01351             break;
01352         }
01353         case NEW_LINE_CREATING:
01354         {
01355             //notify parent
01356             CPEvent e(this, CPEvent::NEW_LINE_ADDED, m_selectedPoint.GetControlPoint());
01357             emit(e);
01358             break;
01359         }
01360         case SELECT_DELETE_REGION:
01361         case NO_IMAGE:
01362             break;
01363 
01364         }
01365 //        DEBUG_DEBUG("ImageDisplay: mouse release, state change: " << oldState
01366 //                    << " -> " << editState);
01367     }
01368 
01369 }
01370 
01371 
01372 void CPImageCtrl::mouseReleaseMMBEvent(wxMouseEvent& mouse)
01373 {
01374     DEBUG_DEBUG("middle mouse button released, leaving scroll mode")
01375 //    SetCursor(wxCursor(wxCURSOR_BULLSEYE));
01376 }
01377 
01378 
01379 void CPImageCtrl::mousePressMMBEvent(wxMouseEvent& mouse)
01380 {
01381     DEBUG_DEBUG("middle mouse button pressed, entering scroll mode")
01382     if (!m_img.get()) return; // ignore events if no image loaded.
01383     m_mouseScrollPos = mouse.GetPosition();
01384 //    SetCursor(wxCursor(wxCURSOR_HAND));
01385 }
01386 
01387 void CPImageCtrl::mousePressRMBEvent(wxMouseEvent& mouse)
01388 {
01389     //ignore event if no image loaded
01390     if(!m_img.get()) 
01391         return;
01392     wxPoint mpos_;
01393     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y, &mpos_.x, & mpos_.y);
01394     hugin_utils::FDiff2D mpos(mpos_.x, mpos_.y);
01395     mpos = applyRotInv(invScale(mpos));
01396     // if mouseclick is out of image, ignore
01397     if (mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight())
01398     {
01399         return;
01400     }
01401     if(mouse.CmdDown() && (editState==NO_SELECTION || editState==KNOWN_POINT_SELECTED || editState==NEW_POINT_SELECTED))
01402     {
01403         rectStartPos=mpos;
01404         editState=SELECT_DELETE_REGION;
01405         DrawSelectionRectangle(mpos,mpos);
01406     };
01407 };
01408 
01409 void CPImageCtrl::mouseReleaseRMBEvent(wxMouseEvent& mouse)
01410 {
01411     if (!m_img.get()) return; // ignore events if no image loaded.
01412     wxPoint mpos_;
01413     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
01414                            &mpos_.x, & mpos_.y);
01415     hugin_utils::FDiff2D mpos(mpos_.x, mpos_.y);
01416     mpos = applyRotInv(invScale(mpos));
01417     DEBUG_DEBUG("mouseReleaseEvent, pos:" << mpos.x
01418                 << ", " << mpos.y);
01419 
01420     if (mouse.RightUp())
01421     {
01422         if(editState==SELECT_DELETE_REGION)
01423         {
01424             DrawSelectionRectangle(rectStartPos,mpos);
01425             editState=NO_SELECTION;
01426             CPEvent e(this,rectStartPos,mpos);
01427             emit(e);
01428         }
01429         else
01430         {
01431             // if mouseclick is out of image, ignore
01432             if (mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight()) {
01433                 return;
01434             }
01435             // set right up event
01436             DEBUG_DEBUG("Emitting right click (rmb release)");
01437             CPEvent e(this, CPEvent::RIGHT_CLICK, mpos);
01438             emit(e);
01439         }
01440     }
01441 }
01442 
01443 void CPImageCtrl::update()
01444 {
01445     DEBUG_TRACE("edit state:" << editState);
01446     wxClientDC dc(this);
01447     PrepareDC(dc);
01448     OnDraw(dc);
01449 }
01450 
01451 bool CPImageCtrl::emit(CPEvent & ev)
01452 {
01453     if ( ProcessEvent( ev ) == FALSE ) {
01454         wxLogWarning( _("Could not process event!") );
01455         return false;
01456     } else {
01457         return true;
01458     }
01459 }
01460 
01461 void CPImageCtrl::setScale(double factor)
01462 {
01463     if (factor == 0) {
01464         fitToWindow = true;
01465         factor = calcAutoScaleFactor(imageSize);
01466     } else {
01467         fitToWindow = false;
01468     }
01469     DEBUG_DEBUG("new scale factor:" << factor);
01470     // update if factor changed
01471     if (factor != scaleFactor) {
01472         scaleFactor = factor;
01473         // keep existing scale focussed.
01474         rescaleImage();
01475     }
01476 }
01477 
01478 double CPImageCtrl::calcAutoScaleFactor(wxSize size)
01479 {
01480     // TODO correctly autoscale rotated iamges
01481     int w = size.GetWidth();
01482     int h = size.GetHeight();
01483     if (m_imgRotation ==  ROT90 || m_imgRotation == ROT270) {
01484         int t = w;
01485         w = h;
01486         h = t;
01487     }
01488 
01489 //    wxSize csize = GetClientSize();
01490     wxSize csize = GetSize();
01491     DEBUG_DEBUG("csize: " << csize.GetWidth() << "x" << csize.GetHeight() << "image: " << w << "x" << h);
01492     double s1 = (double)csize.GetWidth()/w;
01493     double s2 = (double)csize.GetHeight()/h;
01494     DEBUG_DEBUG("s1: " << s1 << "  s2:" << s2);
01495     return s1 < s2 ? s1 : s2;
01496 }
01497 
01498 double CPImageCtrl::getScaleFactor() const
01499 {
01500     return scaleFactor;
01501 }
01502 
01503 void CPImageCtrl::OnSize(wxSizeEvent &e)
01504 {
01505     DEBUG_TRACE("size: " << e.GetSize().GetWidth() << "x" << e.GetSize().GetHeight());
01506     // rescale bitmap if needed.
01507     if (imageFilename != "") {
01508         if (fitToWindow) {
01509             setScale(0);
01510         }
01511     }
01512 }
01513 
01514 void CPImageCtrl::OnKey(wxKeyEvent & e)
01515 {
01516     if (!m_img.get()) return; // ignore events if no image loaded.
01517     DEBUG_TRACE(" OnKey, key:" << e.m_keyCode);
01518     wxPoint delta(0,0);
01519     // check for cursor keys, if control is not pressed
01520     if ((!e.CmdDown()) && e.GetKeyCode() == WXK_LEFT ) delta.x = -1;
01521     if ((!e.CmdDown()) && e.GetKeyCode() == WXK_RIGHT ) delta.x = 1;
01522     if ((!e.CmdDown()) && e.GetKeyCode() == WXK_UP ) delta.y = -1;
01523     if ((!e.CmdDown()) && e.GetKeyCode() == WXK_DOWN ) delta.y = 1;
01524     if ( (delta.x != 0 || delta.y != 0 ) && (e.ShiftDown() || e.CmdDown())) {
01525         // move to the left
01526         double speed = (double) GetClientSize().GetWidth()/10;
01527         delta.x = (int) (delta.x * speed);
01528         delta.y = (int) (delta.y * speed);
01529         if (e.ShiftDown()) {
01530             // emit scroll event, so that other window can be scrolled
01531             // as well.
01532             CPEvent e(this, CPEvent::SCROLLED, hugin_utils::FDiff2D(delta.x, delta.y));
01533             emit(e);
01534         } else if (e.CmdDown()) {
01535             ScrollDelta(delta);
01536         }
01537     } else if (delta.x != 0 || delta.y != 0 ) {
01538 
01539         hugin_utils::FDiff2D shift(delta.x/3.0, delta.y/3.0);
01540         // rotate shift according to current display
01541         double t;
01542         switch (m_imgRotation) {
01543             case ROT90:
01544                 t = shift.x;
01545                 shift.x = shift.y;
01546                 shift.y = -t;
01547                 break;
01548             case ROT180:
01549                 shift.x = -shift.x;
01550                 shift.y = -shift.y;
01551                 break;
01552             case ROT270:
01553                 t = shift.x;
01554                 shift.x = -shift.y;
01555                 shift.y = t;
01556             default:
01557                 break;
01558         }
01559         // move control point by half a pixel, if a point is selected
01560         if (editState == KNOWN_POINT_SELECTED ) {
01561             DisplayedControlPoint updatedCp=m_points[selectedPointNr];
01562             updatedCp.ShiftControlPoint(shift);
01563             CPEvent e( this, CPEvent::POINT_CHANGED, selectedPointNr, updatedCp.GetControlPoint());
01564             emit(e);
01565             m_forceMagnifier = true;
01566             m_timer.Stop();
01567             m_timer.Start(2000, true);
01568         } else if (editState == NEW_POINT_SELECTED) {
01569             newPoint = newPoint + shift;
01570             // update display.
01571             update();
01572         }
01573 
01574     } else if (e.m_keyCode == 'a') {
01575         DEBUG_DEBUG("adding point with a key, faking right click");
01576         // faking right mouse button with "a"
01577         // set right up event
01578         CPEvent ev(this, CPEvent::RIGHT_CLICK, hugin_utils::FDiff2D(0,0));
01579         emit(ev);
01580     } else {
01581         // forward some keys...
01582         bool forward = false;
01583         switch (e.GetKeyCode())
01584         {
01585             case 'g':
01586             case '0':
01587             case '1':
01588             case '2':
01589             case 'f':
01590             case WXK_RIGHT:
01591             case WXK_LEFT:
01592             case WXK_UP:
01593             case WXK_DOWN:
01594             case WXK_DELETE:
01595                 forward = true;
01596                 break;
01597             default:
01598                 break;
01599         }
01600 
01601         if (forward) {
01602             // dangelo: I don't understand why some keys are forwarded and others are not..
01603             // Delete is forwared under wxGTK, and g not..
01604             // wxWidgets 2.6.1 using gtk 2 doesn't set the event object
01605             // properly.. do it here by hand
01606             e.SetEventObject(this);
01607             DEBUG_DEBUG("forwarding key " << e.GetKeyCode()
01608                         << " origin: id:" << e.GetId() << " obj: "
01609                         << e.GetEventObject());
01610             // forward all keys to our parent
01611             //GetParent()->GetEventHandler()->ProcessEvent(e);
01612             m_editPanel->GetEventHandler()->ProcessEvent(e);
01613         } else {
01614             e.Skip();
01615         }
01616     }
01617 }
01618 
01619 void CPImageCtrl::OnKeyDown(wxKeyEvent & e)
01620 {
01621     DEBUG_TRACE("key:" << e.m_keyCode);
01622     if (!m_img.get()) return; // ignore events if no image loaded.
01623     if (e.m_keyCode == WXK_SHIFT || e.m_keyCode == WXK_CONTROL) {
01624         DEBUG_DEBUG("shift or control down, reseting scoll position");
01625         m_mouseScrollPos = e.GetPosition();
01626     }
01627     e.Skip();
01628 }
01629 
01630 void CPImageCtrl::OnMouseLeave(wxMouseEvent & e)
01631 {
01632     DEBUG_TRACE("MOUSE LEAVE");
01633     m_mousePos = hugin_utils::FDiff2D(-1,-1);
01634     m_mouseInWindow = false;
01635     update();
01636 }
01637 
01638 void CPImageCtrl::OnMouseEnter(wxMouseEvent & e)
01639 {
01640     DEBUG_TRACE("MOUSE Enter, setting focus");
01641     m_mouseInWindow = true;
01642     SetFocus();
01643     update();
01644 }
01645 
01646 hugin_utils::FDiff2D CPImageCtrl::getNewPoint()
01647 {
01648     // only possible if a new point is actually selected
01649     // DEBUG_ASSERT(editState == NEW_POINT_SELECTED);
01650     return newPoint;
01651 }
01652 
01653 void CPImageCtrl::setNewPoint(const hugin_utils::FDiff2D & p)
01654 {
01655     DEBUG_DEBUG("setting new point " << p.x << "," << p.y);
01656     // should we need to check for some precondition?
01657     newPoint = p;
01658     editState = NEW_POINT_SELECTED;
01659 
01660     // show new point.
01661     showPosition(p);
01662 
01663     // we do not send an event, since CPEditorPanel
01664     // caused the change.. so it doesn't need to filter
01665     // out its own change messages.
01666 }
01667 
01668 void CPImageCtrl::showSearchArea(bool show)
01669 {
01670     m_showSearchArea = show;
01671     if (show)
01672     {
01673         int templSearchAreaPercent = wxConfigBase::Get()->Read(wxT("/Finetune/SearchAreaPercent"), HUGIN_FT_SEARCH_AREA_PERCENT);
01674         m_searchRectWidth = (m_realSize.GetWidth() * templSearchAreaPercent) / 200;
01675         DEBUG_DEBUG("Setting new search area: w in %:" << templSearchAreaPercent << " bitmap width: " << bitmap.GetWidth() << "  resulting size: " << m_searchRectWidth);
01676         m_mousePos = hugin_utils::FDiff2D(-1,-1);
01677     }
01678 }
01679 
01680 void CPImageCtrl::showTemplateArea(bool show)
01681 {
01682     m_showTemplateArea = show;
01683     if (show)
01684     {
01685         m_templateRectWidth = wxConfigBase::Get()->Read(wxT("/Finetune/TemplateSize"),HUGIN_FT_TEMPLATE_SIZE) / 2;
01686     }
01687 }
01688 
01689 wxPoint CPImageCtrl::MaxScrollDelta(wxPoint delta)
01690 {
01691     int x,y;
01692     GetViewStart( &x, &y );
01693 
01694     wxSize winSize = GetClientSize();
01695     wxSize imgSize;
01696     imgSize.x = bitmap.GetWidth();
01697     imgSize.y = bitmap.GetHeight();
01698     // check for top and left border
01699     if (x + delta.x < 0) {
01700         delta.x = -x;
01701     }
01702     if (y + delta.y < 0) {
01703         delta.y = -y;
01704     }
01705     // check for right and bottom border
01706     int right = x + delta.x + winSize.x ;
01707     if (right > imgSize.x) {
01708         delta.x = imgSize.x - right;
01709         if (delta.x < 0) {
01710             delta.x = 0;
01711         }
01712     }
01713     int bottom = y + delta.y + winSize.y ;
01714     if (bottom > imgSize.y) {
01715         delta.y = imgSize.y - bottom;
01716         if (delta.y < 0) {
01717             delta.y = 0;
01718         }
01719     }
01720     return delta;
01721 }
01722 
01723 void CPImageCtrl::ScrollDelta(const wxPoint & delta)
01724 {
01725     // TODO: adjust
01726     if (delta.x == 0 && delta.y == 0) {
01727         return;
01728     }
01729     int x,y;
01730     GetViewStart( &x, &y );
01731     x = x + delta.x;
01732     y = y + delta.y;
01733     if (x<0) x = 0;
01734     if (y<0) y = 0;
01735     Scroll( x, y);
01736 }
01737 
01738 const wxSize CPImageCtrl::GetBitmapSize() const
01739 {
01740 #if wxCHECK_VERSION(2,9,0)
01741     return bitmap.GetSize();
01742 #else
01743     return wxSize(bitmap.GetWidth(), bitmap.GetHeight());
01744 #endif
01745 };
01746 
01747 IMPLEMENT_DYNAMIC_CLASS(CPImageCtrl, wxScrolledWindow)
01748 
01749 CPImageCtrlXmlHandler::CPImageCtrlXmlHandler()
01750                 : wxXmlResourceHandler()
01751 {
01752     AddWindowStyles();
01753 }
01754 
01755 wxObject *CPImageCtrlXmlHandler::DoCreateResource()
01756 {
01757     XRC_MAKE_INSTANCE(cp, CPImageCtrl)
01758 
01759     cp->Create(m_parentAsWindow,
01760                    GetID(),
01761                    GetPosition(), GetSize(),
01762                    GetStyle(wxT("style")),
01763                    GetName());
01764 
01765     SetupWindow( cp);
01766 
01767     return cp;
01768 }
01769 
01770 bool CPImageCtrlXmlHandler::CanHandle(wxXmlNode *node)
01771 {
01772     return IsOfClass(node, wxT("CPImageCtrl"));
01773 }
01774 
01775 IMPLEMENT_DYNAMIC_CLASS(CPImageCtrlXmlHandler, wxXmlResourceHandler)

Generated on 27 May 2016 for Hugintrunk by  doxygen 1.4.7