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

Generated on 28 Jun 2016 for Hugintrunk by  doxygen 1.4.7