svgui  1.9
ImageLayer.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006 Chris Cannam.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "ImageLayer.h"
00017 
00018 #include "data/model/Model.h"
00019 #include "base/RealTime.h"
00020 #include "base/Profiler.h"
00021 #include "view/View.h"
00022 
00023 #include "data/model/ImageModel.h"
00024 #include "data/fileio/FileSource.h"
00025 
00026 #include "widgets/ImageDialog.h"
00027 #include "widgets/ProgressDialog.h"
00028 
00029 #include <QPainter>
00030 #include <QMouseEvent>
00031 #include <QInputDialog>
00032 #include <QMutexLocker>
00033 #include <QTextStream>
00034 #include <QMessageBox>
00035 
00036 #include <iostream>
00037 #include <cmath>
00038 
00039 ImageLayer::ImageMap
00040 ImageLayer::m_images;
00041 
00042 QMutex
00043 ImageLayer::m_imageMapMutex;
00044 
00045 ImageLayer::ImageLayer() :
00046     Layer(),
00047     m_model(0),
00048     m_editing(false),
00049     m_originalPoint(0, "", ""),
00050     m_editingPoint(0, "", ""),
00051     m_editingCommand(0)
00052 {
00053 }
00054 
00055 ImageLayer::~ImageLayer()
00056 {
00057     for (FileSourceMap::iterator i = m_fileSources.begin();
00058          i != m_fileSources.end(); ++i) {
00059         delete i->second;
00060     }
00061 }
00062 
00063 void
00064 ImageLayer::setModel(ImageModel *model)
00065 {
00066     if (m_model == model) return;
00067     m_model = model;
00068 
00069     connectSignals(m_model);
00070 
00071     emit modelReplaced();
00072 }
00073 
00074 Layer::PropertyList
00075 ImageLayer::getProperties() const
00076 {
00077     return Layer::getProperties();
00078 }
00079 
00080 QString
00081 ImageLayer::getPropertyLabel(const PropertyName &) const
00082 {
00083     return "";
00084 }
00085 
00086 Layer::PropertyType
00087 ImageLayer::getPropertyType(const PropertyName &name) const
00088 {
00089     return Layer::getPropertyType(name);
00090 }
00091 
00092 int
00093 ImageLayer::getPropertyRangeAndValue(const PropertyName &name,
00094                                     int *min, int *max, int *deflt) const
00095 {
00096     return Layer::getPropertyRangeAndValue(name, min, max, deflt);
00097 }
00098 
00099 QString
00100 ImageLayer::getPropertyValueLabel(const PropertyName &name,
00101                                  int value) const
00102 {
00103     return Layer::getPropertyValueLabel(name, value);
00104 }
00105 
00106 void
00107 ImageLayer::setProperty(const PropertyName &name, int value)
00108 {
00109     Layer::setProperty(name, value);
00110 }
00111 
00112 bool
00113 ImageLayer::getValueExtents(float &, float &, bool &, QString &) const
00114 {
00115     return false;
00116 }
00117 
00118 bool
00119 ImageLayer::isLayerScrollable(const View *) const
00120 {
00121     return true;
00122 }
00123 
00124 
00125 ImageModel::PointList
00126 ImageLayer::getLocalPoints(View *v, int x, int ) const
00127 {
00128     if (!m_model) return ImageModel::PointList();
00129 
00130 //    SVDEBUG << "ImageLayer::getLocalPoints(" << x << "," << y << "):";
00131     const ImageModel::PointList &points(m_model->getPoints());
00132 
00133     ImageModel::PointList rv;
00134 
00135     for (ImageModel::PointList::const_iterator i = points.begin();
00136          i != points.end(); ) {
00137 
00138         const ImageModel::Point &p(*i);
00139         int px = v->getXForFrame(p.frame);
00140         if (px > x) break;
00141 
00142         ++i;
00143         if (i != points.end()) {
00144             int nx = v->getXForFrame((*i).frame);
00145             if (nx < x) {
00146                 // as we aim not to overlap the images, if the following
00147                 // image begins to the left of a point then the current
00148                 // one may be assumed to end to the left of it as well.
00149                 continue;
00150             }
00151         }
00152 
00153         // this image is a candidate, test it properly
00154 
00155         int width = 32;
00156         if (m_scaled[v].find(p.image) != m_scaled[v].end()) {
00157             width = m_scaled[v][p.image].width();
00158 //            SVDEBUG << "scaled width = " << width << endl;
00159         }
00160 
00161         if (x >= px && x < px + width) {
00162             rv.insert(p);
00163         }
00164     }
00165 
00166 //    cerr << rv.size() << " point(s)" << endl;
00167 
00168     return rv;
00169 }
00170 
00171 QString
00172 ImageLayer::getFeatureDescription(View *v, QPoint &pos) const
00173 {
00174     int x = pos.x();
00175 
00176     if (!m_model || !m_model->getSampleRate()) return "";
00177 
00178     ImageModel::PointList points = getLocalPoints(v, x, pos.y());
00179 
00180     if (points.empty()) {
00181         if (!m_model->isReady()) {
00182             return tr("In progress");
00183         } else {
00184             return "";
00185         }
00186     }
00187 
00188 //    int useFrame = points.begin()->frame;
00189 
00190 //    RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
00191 
00192     QString text;
00193 /*    
00194     if (points.begin()->label == "") {
00195         text = QString(tr("Time:\t%1\nHeight:\t%2\nLabel:\t%3"))
00196             .arg(rt.toText(true).c_str())
00197             .arg(points.begin()->height)
00198             .arg(points.begin()->label);
00199     }
00200 
00201     pos = QPoint(v->getXForFrame(useFrame),
00202                  getYForHeight(v, points.begin()->height));
00203 */
00204     return text;
00205 }
00206 
00207 
00209 
00210 bool
00211 ImageLayer::snapToFeatureFrame(View *v, int &frame,
00212                               int &resolution,
00213                               SnapType snap) const
00214 {
00215     if (!m_model) {
00216         return Layer::snapToFeatureFrame(v, frame, resolution, snap);
00217     }
00218 
00219     resolution = m_model->getResolution();
00220     ImageModel::PointList points;
00221 
00222     if (snap == SnapNeighbouring) {
00223         
00224         points = getLocalPoints(v, v->getXForFrame(frame), -1);
00225         if (points.empty()) return false;
00226         frame = points.begin()->frame;
00227         return true;
00228     }    
00229 
00230     points = m_model->getPoints(frame, frame);
00231     int snapped = frame;
00232     bool found = false;
00233 
00234     for (ImageModel::PointList::const_iterator i = points.begin();
00235          i != points.end(); ++i) {
00236 
00237         if (snap == SnapRight) {
00238 
00239             if (i->frame > frame) {
00240                 snapped = i->frame;
00241                 found = true;
00242                 break;
00243             }
00244 
00245         } else if (snap == SnapLeft) {
00246 
00247             if (i->frame <= frame) {
00248                 snapped = i->frame;
00249                 found = true; // don't break, as the next may be better
00250             } else {
00251                 break;
00252             }
00253 
00254         } else { // nearest
00255 
00256             ImageModel::PointList::const_iterator j = i;
00257             ++j;
00258 
00259             if (j == points.end()) {
00260 
00261                 snapped = i->frame;
00262                 found = true;
00263                 break;
00264 
00265             } else if (j->frame >= frame) {
00266 
00267                 if (j->frame - frame < frame - i->frame) {
00268                     snapped = j->frame;
00269                 } else {
00270                     snapped = i->frame;
00271                 }
00272                 found = true;
00273                 break;
00274             }
00275         }
00276     }
00277 
00278     frame = snapped;
00279     return found;
00280 }
00281 
00282 void
00283 ImageLayer::paint(View *v, QPainter &paint, QRect rect) const
00284 {
00285     if (!m_model || !m_model->isOK()) return;
00286 
00287     int sampleRate = m_model->getSampleRate();
00288     if (!sampleRate) return;
00289 
00290 //    Profiler profiler("ImageLayer::paint", true);
00291 
00292 //    int x0 = rect.left(), x1 = rect.right();
00293     int x0 = 0, x1 = v->width();
00294 
00295     int frame0 = v->getFrameForX(x0);
00296     int frame1 = v->getFrameForX(x1);
00297 
00298     ImageModel::PointList points(m_model->getPoints(frame0, frame1));
00299     if (points.empty()) return;
00300 
00301     paint.save();
00302     paint.setClipRect(rect.x(), 0, rect.width(), v->height());
00303 
00304     QColor penColour;
00305     penColour = v->getForeground();
00306 
00307     QColor brushColour;
00308     brushColour = v->getBackground();
00309 
00310     int h, s, val;
00311     brushColour.getHsv(&h, &s, &val);
00312     brushColour.setHsv(h, s, 255, 240);
00313 
00314     paint.setPen(penColour);
00315     paint.setBrush(brushColour);
00316     paint.setRenderHint(QPainter::Antialiasing, true);
00317 
00318     for (ImageModel::PointList::const_iterator i = points.begin();
00319          i != points.end(); ++i) {
00320 
00321         const ImageModel::Point &p(*i);
00322 
00323         int x = v->getXForFrame(p.frame);
00324 
00325         int nx = x + 2000;
00326         ImageModel::PointList::const_iterator j = i;
00327         ++j;
00328         if (j != points.end()) {
00329             int jx = v->getXForFrame(j->frame);
00330             if (jx < nx) nx = jx;
00331         }
00332 
00333         drawImage(v, paint, p, x, nx);
00334     }
00335 
00336     paint.setRenderHint(QPainter::Antialiasing, false);
00337     paint.restore();
00338 }
00339 
00340 void
00341 ImageLayer::drawImage(View *v, QPainter &paint, const ImageModel::Point &p,
00342                       int x, int nx) const
00343 {
00344     QString label = p.label;
00345     QString imageName = p.image;
00346 
00347     QImage image;
00348     QString additionalText;
00349 
00350     QSize imageSize;
00351     if (!getImageOriginalSize(imageName, imageSize)) {
00352         image = QImage(":icons/emptypage.png");
00353         imageSize = image.size();
00354         additionalText = imageName;
00355     }
00356 
00357     int topMargin = 10;
00358     int bottomMargin = 10;
00359     int spacing = 5;
00360 
00361     if (v->height() < 100) {
00362         topMargin = 5;
00363         bottomMargin = 5;
00364     }
00365 
00366     int maxBoxHeight = v->height() - topMargin - bottomMargin;
00367 
00368     int availableWidth = nx - x - 3;
00369     if (availableWidth < 20) availableWidth = 20;
00370 
00371     QRect labelRect;
00372 
00373     if (label != "") {
00374 
00375         int likelyHeight = v->height() / 4;
00376 
00377         int likelyWidth = // available height times image aspect
00378             ((maxBoxHeight - likelyHeight) * imageSize.width())
00379             / imageSize.height();
00380 
00381         if (likelyWidth > imageSize.width()) {
00382             likelyWidth = imageSize.width();
00383         }
00384 
00385         if (likelyWidth > availableWidth) {
00386             likelyWidth = availableWidth;
00387         }
00388 
00389         int singleWidth = paint.fontMetrics().width(label);
00390         if (singleWidth < availableWidth && singleWidth < likelyWidth * 2) {
00391             likelyWidth = singleWidth + 4;
00392         }
00393 
00394         labelRect = paint.fontMetrics().boundingRect
00395             (QRect(0, 0, likelyWidth, likelyHeight),
00396              Qt::AlignCenter | Qt::TextWordWrap, label);
00397 
00398         labelRect.setWidth(labelRect.width() + 6);
00399     }
00400 
00401     if (image.isNull()) {
00402         image = getImage(v, imageName,
00403                          QSize(availableWidth,
00404                                maxBoxHeight - labelRect.height()));
00405     }
00406 
00407     int boxWidth = image.width();
00408     if (boxWidth < labelRect.width()) {
00409         boxWidth = labelRect.width();
00410     }
00411 
00412     int boxHeight = image.height();
00413     if (label != "") {
00414         boxHeight += labelRect.height() + spacing;
00415     }
00416 
00417     int division = image.height();
00418 
00419     if (additionalText != "") {
00420 
00421         paint.save();
00422 
00423         QFont font(paint.font());
00424         font.setItalic(true);
00425         paint.setFont(font);
00426 
00427         int tw = paint.fontMetrics().width(additionalText);
00428         if (tw > availableWidth) {
00429             tw = availableWidth;
00430         }
00431         if (boxWidth < tw) {
00432             boxWidth = tw;
00433         }
00434         boxHeight += paint.fontMetrics().height();
00435         division += paint.fontMetrics().height();
00436     }                
00437 
00438     bottomMargin = v->height() - topMargin - boxHeight;
00439     if (bottomMargin > topMargin + v->height()/7) {
00440         topMargin += v->height()/8;
00441         bottomMargin -= v->height()/8;
00442     }
00443 
00444     paint.drawRect(x - 1,
00445                    topMargin - 1,
00446                    boxWidth + 2,
00447                    boxHeight + 2);
00448 
00449     int imageY;
00450     if (label != "") {
00451         imageY = topMargin + labelRect.height() + spacing;
00452     } else {
00453         imageY = topMargin;
00454     }
00455 
00456     paint.drawImage(x + (boxWidth - image.width())/2,
00457                     imageY,
00458                     image);
00459 
00460     if (additionalText != "") {
00461         paint.drawText(x,
00462                        imageY + image.height() + paint.fontMetrics().ascent(),
00463                        additionalText);
00464         paint.restore();
00465     }
00466 
00467     if (label != "") {
00468         paint.drawLine(x,
00469                        topMargin + labelRect.height() + spacing,
00470                        x + boxWidth, 
00471                        topMargin + labelRect.height() + spacing);
00472 
00473         paint.drawText(QRect(x,
00474                              topMargin,
00475                              boxWidth,
00476                              labelRect.height()),
00477                        Qt::AlignCenter | Qt::TextWordWrap,
00478                        label);
00479     }
00480 }
00481 
00482 void
00483 ImageLayer::setLayerDormant(const View *v, bool dormant)
00484 {
00485     if (dormant) {
00486         // Delete the images named in the view's scaled map from the
00487         // general image map as well.  They can always be re-loaded
00488         // if it turns out another view still needs them.
00489         QMutexLocker locker(&m_imageMapMutex);
00490         for (ImageMap::iterator i = m_scaled[v].begin();
00491              i != m_scaled[v].end(); ++i) {
00492             m_images.erase(i->first);
00493         }
00494         m_scaled.erase(v);
00495     }
00496 }
00497 
00499 
00500 bool
00501 ImageLayer::getImageOriginalSize(QString name, QSize &size) const
00502 {
00503 //    cerr << "getImageOriginalSize: \"" << name << "\"" << endl;
00504 
00505     QMutexLocker locker(&m_imageMapMutex);
00506     if (m_images.find(name) == m_images.end()) {
00507 //        cerr << "don't have, trying to open local" << endl;
00508         m_images[name] = QImage(getLocalFilename(name));
00509     }
00510     if (m_images[name].isNull()) {
00511 //        cerr << "null image" << endl;
00512         return false;
00513     } else {
00514         size = m_images[name].size();
00515         return true;
00516     }
00517 }
00518 
00519 QImage 
00520 ImageLayer::getImage(View *v, QString name, QSize maxSize) const
00521 {
00522 //    SVDEBUG << "ImageLayer::getImage(" << v << ", " << name << ", ("
00523 //              << maxSize.width() << "x" << maxSize.height() << "))" << endl;
00524 
00525     if (!m_scaled[v][name].isNull()  &&
00526         ((m_scaled[v][name].width()  == maxSize.width() &&
00527           m_scaled[v][name].height() <= maxSize.height()) ||
00528          (m_scaled[v][name].width()  <= maxSize.width() &&
00529           m_scaled[v][name].height() == maxSize.height()))) {
00530 //        cerr << "cache hit" << endl;
00531         return m_scaled[v][name];
00532     }
00533 
00534     QMutexLocker locker(&m_imageMapMutex);
00535 
00536     if (m_images.find(name) == m_images.end()) {
00537         m_images[name] = QImage(getLocalFilename(name));
00538     }
00539 
00540     if (m_images[name].isNull()) {
00541 //        cerr << "null image" << endl;
00542         m_scaled[v][name] = QImage();
00543     } else if (m_images[name].width() <= maxSize.width() &&
00544                m_images[name].height() <= maxSize.height()) {
00545         m_scaled[v][name] = m_images[name];
00546     } else {
00547         m_scaled[v][name] =
00548             m_images[name].scaled(maxSize,
00549                                   Qt::KeepAspectRatio,
00550                                   Qt::SmoothTransformation);
00551     }
00552 
00553     return m_scaled[v][name];
00554 }
00555 
00556 void
00557 ImageLayer::drawStart(View *v, QMouseEvent *e)
00558 {
00559 //    SVDEBUG << "ImageLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
00560 
00561     if (!m_model) {
00562         SVDEBUG << "ImageLayer::drawStart: no model" << endl;
00563         return;
00564     }
00565 
00566     int frame = v->getFrameForX(e->x());
00567     if (frame < 0) frame = 0;
00568     frame = frame / m_model->getResolution() * m_model->getResolution();
00569 
00570     m_editingPoint = ImageModel::Point(frame, "", "");
00571     m_originalPoint = m_editingPoint;
00572 
00573     if (m_editingCommand) finish(m_editingCommand);
00574     m_editingCommand = new ImageModel::EditCommand(m_model, "Add Image");
00575     m_editingCommand->addPoint(m_editingPoint);
00576 
00577     m_editing = true;
00578 }
00579 
00580 void
00581 ImageLayer::drawDrag(View *v, QMouseEvent *e)
00582 {
00583 //    SVDEBUG << "ImageLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
00584 
00585     if (!m_model || !m_editing) return;
00586 
00587     int frame = v->getFrameForX(e->x());
00588     if (frame < 0) frame = 0;
00589     frame = frame / m_model->getResolution() * m_model->getResolution();
00590 
00591     m_editingCommand->deletePoint(m_editingPoint);
00592     m_editingPoint.frame = frame;
00593     m_editingCommand->addPoint(m_editingPoint);
00594 }
00595 
00596 void
00597 ImageLayer::drawEnd(View *, QMouseEvent *)
00598 {
00599 //    SVDEBUG << "ImageLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl;
00600     if (!m_model || !m_editing) return;
00601 
00602     ImageDialog dialog(tr("Select image"), "", "");
00603 
00604     if (dialog.exec() == QDialog::Accepted) {
00605 
00606         checkAddSource(dialog.getImage());
00607 
00608         ImageModel::ChangeImageCommand *command =
00609             new ImageModel::ChangeImageCommand
00610             (m_model, m_editingPoint, dialog.getImage(), dialog.getLabel());
00611         m_editingCommand->addCommand(command);
00612     } else {
00613         m_editingCommand->deletePoint(m_editingPoint);
00614     }
00615 
00616     finish(m_editingCommand);
00617     m_editingCommand = 0;
00618     m_editing = false;
00619 }
00620 
00621 bool
00622 ImageLayer::addImage(int frame, QString url)
00623 {
00624     QImage image(getLocalFilename(url));
00625     if (image.isNull()) {
00626         cerr << "Failed to open image from url \"" << url << "\" (local filename \"" << getLocalFilename(url) << "\"" << endl;
00627         delete m_fileSources[url];
00628         m_fileSources.erase(url);
00629         return false;
00630     }
00631 
00632     ImageModel::Point point(frame, url, "");
00633     ImageModel::EditCommand *command =
00634         new ImageModel::EditCommand(m_model, "Add Image");
00635     command->addPoint(point);
00636     finish(command);
00637     return true;
00638 }
00639 
00640 void
00641 ImageLayer::editStart(View *v, QMouseEvent *e)
00642 {
00643 //    SVDEBUG << "ImageLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
00644 
00645     if (!m_model) return;
00646 
00647     ImageModel::PointList points = getLocalPoints(v, e->x(), e->y());
00648     if (points.empty()) return;
00649 
00650     m_editOrigin = e->pos();
00651     m_editingPoint = *points.begin();
00652     m_originalPoint = m_editingPoint;
00653 
00654     if (m_editingCommand) {
00655         finish(m_editingCommand);
00656         m_editingCommand = 0;
00657     }
00658 
00659     m_editing = true;
00660 }
00661 
00662 void
00663 ImageLayer::editDrag(View *v, QMouseEvent *e)
00664 {
00665     if (!m_model || !m_editing) return;
00666 
00667     int frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x());
00668     int frame = m_originalPoint.frame + frameDiff;
00669 
00670     if (frame < 0) frame = 0;
00671     frame = (frame / m_model->getResolution()) * m_model->getResolution();
00672 
00673     if (!m_editingCommand) {
00674         m_editingCommand = new ImageModel::EditCommand(m_model, tr("Move Image"));
00675     }
00676 
00677     m_editingCommand->deletePoint(m_editingPoint);
00678     m_editingPoint.frame = frame;
00679     m_editingCommand->addPoint(m_editingPoint);
00680 }
00681 
00682 void
00683 ImageLayer::editEnd(View *, QMouseEvent *)
00684 {
00685 //    SVDEBUG << "ImageLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl;
00686     if (!m_model || !m_editing) return;
00687 
00688     if (m_editingCommand) {
00689         finish(m_editingCommand);
00690     }
00691     
00692     m_editingCommand = 0;
00693     m_editing = false;
00694 }
00695 
00696 bool
00697 ImageLayer::editOpen(View *v, QMouseEvent *e)
00698 {
00699     if (!m_model) return false;
00700 
00701     ImageModel::PointList points = getLocalPoints(v, e->x(), e->y());
00702     if (points.empty()) return false;
00703 
00704     QString image = points.begin()->image;
00705     QString label = points.begin()->label;
00706 
00707     ImageDialog dialog(tr("Select image"),
00708                        image,
00709                        label);
00710 
00711     if (dialog.exec() == QDialog::Accepted) {
00712 
00713         checkAddSource(dialog.getImage());
00714 
00715         ImageModel::ChangeImageCommand *command =
00716             new ImageModel::ChangeImageCommand
00717             (m_model, *points.begin(), dialog.getImage(), dialog.getLabel());
00718 
00719         CommandHistory::getInstance()->addCommand(command);
00720     }
00721 
00722     return true;
00723 }    
00724 
00725 void
00726 ImageLayer::moveSelection(Selection s, int newStartFrame)
00727 {
00728     if (!m_model) return;
00729 
00730     ImageModel::EditCommand *command =
00731         new ImageModel::EditCommand(m_model, tr("Drag Selection"));
00732 
00733     ImageModel::PointList points =
00734         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
00735 
00736     for (ImageModel::PointList::iterator i = points.begin();
00737          i != points.end(); ++i) {
00738 
00739         if (s.contains(i->frame)) {
00740             ImageModel::Point newPoint(*i);
00741             newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
00742             command->deletePoint(*i);
00743             command->addPoint(newPoint);
00744         }
00745     }
00746 
00747     finish(command);
00748 }
00749 
00750 void
00751 ImageLayer::resizeSelection(Selection s, Selection newSize)
00752 {
00753     if (!m_model) return;
00754 
00755     ImageModel::EditCommand *command =
00756         new ImageModel::EditCommand(m_model, tr("Resize Selection"));
00757 
00758     ImageModel::PointList points =
00759         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
00760 
00761     double ratio =
00762         double(newSize.getEndFrame() - newSize.getStartFrame()) /
00763         double(s.getEndFrame() - s.getStartFrame());
00764 
00765     for (ImageModel::PointList::iterator i = points.begin();
00766          i != points.end(); ++i) {
00767 
00768         if (s.contains(i->frame)) {
00769 
00770             double target = i->frame;
00771             target = newSize.getStartFrame() + 
00772                 double(target - s.getStartFrame()) * ratio;
00773 
00774             ImageModel::Point newPoint(*i);
00775             newPoint.frame = lrint(target);
00776             command->deletePoint(*i);
00777             command->addPoint(newPoint);
00778         }
00779     }
00780 
00781     finish(command);
00782 }
00783 
00784 void
00785 ImageLayer::deleteSelection(Selection s)
00786 {
00787     if (!m_model) return;
00788 
00789     ImageModel::EditCommand *command =
00790         new ImageModel::EditCommand(m_model, tr("Delete Selection"));
00791 
00792     ImageModel::PointList points =
00793         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
00794 
00795     for (ImageModel::PointList::iterator i = points.begin();
00796          i != points.end(); ++i) {
00797         if (s.contains(i->frame)) command->deletePoint(*i);
00798     }
00799 
00800     finish(command);
00801 }
00802 
00803 void
00804 ImageLayer::copy(View *v, Selection s, Clipboard &to)
00805 {
00806     if (!m_model) return;
00807 
00808     ImageModel::PointList points =
00809         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
00810 
00811     for (ImageModel::PointList::iterator i = points.begin();
00812          i != points.end(); ++i) {
00813         if (s.contains(i->frame)) {
00814             Clipboard::Point point(i->frame, i->label);
00815             point.setReferenceFrame(alignToReference(v, i->frame));
00816             to.addPoint(point);
00817         }
00818     }
00819 }
00820 
00821 bool
00822 ImageLayer::paste(View *v, const Clipboard &from, int /* frameOffset */, bool /* interactive */)
00823 {
00824     if (!m_model) return false;
00825 
00826     const Clipboard::PointList &points = from.getPoints();
00827 
00828     bool realign = false;
00829 
00830     if (clipboardHasDifferentAlignment(v, from)) {
00831 
00832         QMessageBox::StandardButton button =
00833             QMessageBox::question(v, tr("Re-align pasted items?"),
00834                                   tr("The items you are pasting came from a layer with different source material from this one.  Do you want to re-align them in time, to match the source material for this layer?"),
00835                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
00836                                   QMessageBox::Yes);
00837 
00838         if (button == QMessageBox::Cancel) {
00839             return false;
00840         }
00841 
00842         if (button == QMessageBox::Yes) {
00843             realign = true;
00844         }
00845     }
00846 
00847     ImageModel::EditCommand *command =
00848         new ImageModel::EditCommand(m_model, tr("Paste"));
00849 
00850     for (Clipboard::PointList::const_iterator i = points.begin();
00851          i != points.end(); ++i) {
00852         
00853         if (!i->haveFrame()) continue;
00854 
00855         int frame = 0;
00856 
00857         if (!realign) {
00858             
00859             frame = i->getFrame();
00860 
00861         } else {
00862 
00863             if (i->haveReferenceFrame()) {
00864                 frame = i->getReferenceFrame();
00865                 frame = alignFromReference(v, frame);
00866             } else {
00867                 frame = i->getFrame();
00868             }
00869         }
00870 
00871         ImageModel::Point newPoint(frame);
00872 
00874         
00875         if (i->haveLabel()) {
00876             newPoint.label = i->getLabel();
00877         } else if (i->haveValue()) {
00878             newPoint.label = QString("%1").arg(i->getValue());
00879         } else {
00880             newPoint.label = tr("New Point");
00881         }
00882         
00883         command->addPoint(newPoint);
00884     }
00885 
00886     finish(command);
00887     return true;
00888 }
00889 
00890 QString
00891 ImageLayer::getLocalFilename(QString img) const
00892 {
00893     if (m_fileSources.find(img) == m_fileSources.end()) {
00894         checkAddSource(img);
00895         if (m_fileSources.find(img) == m_fileSources.end()) {
00896             return img;
00897         }
00898     }
00899     return m_fileSources[img]->getLocalFilename();
00900 }
00901 
00902 void
00903 ImageLayer::checkAddSource(QString img) const
00904 {
00905     SVDEBUG << "ImageLayer::checkAddSource(" << img << "): yes, trying..." << endl;
00906 
00907     if (m_fileSources.find(img) != m_fileSources.end()) {
00908         return;
00909     }
00910 
00911     ProgressDialog dialog(tr("Opening image URL..."), true, 2000);
00912     FileSource *rf = new FileSource(img, &dialog);
00913     if (rf->isOK()) {
00914         cerr << "ok, adding it (local filename = " << rf->getLocalFilename() << ")" << endl;
00915         m_fileSources[img] = rf;
00916         connect(rf, SIGNAL(ready()), this, SLOT(fileSourceReady()));
00917     } else {
00918         delete rf;
00919     }
00920 }
00921 
00922 void
00923 ImageLayer::checkAddSources()
00924 {
00925     const ImageModel::PointList &points(m_model->getPoints());
00926 
00927     for (ImageModel::PointList::const_iterator i = points.begin();
00928          i != points.end(); ++i) {
00929         
00930         checkAddSource((*i).image);
00931     }
00932 }
00933 
00934 void
00935 ImageLayer::fileSourceReady()
00936 {
00937 //    SVDEBUG << "ImageLayer::fileSourceReady" << endl;
00938 
00939     FileSource *rf = dynamic_cast<FileSource *>(sender());
00940     if (!rf) return;
00941 
00942     QString img;
00943     for (FileSourceMap::const_iterator i = m_fileSources.begin();
00944          i != m_fileSources.end(); ++i) {
00945         if (i->second == rf) {
00946             img = i->first;
00947 //            cerr << "it's image \"" << img << "\"" << endl;
00948             break;
00949         }
00950     }
00951     if (img == "") return;
00952 
00953     QMutexLocker locker(&m_imageMapMutex);
00954     m_images.erase(img);
00955     for (ViewImageMap::iterator i = m_scaled.begin(); i != m_scaled.end(); ++i) {
00956         i->second.erase(img);
00957         emit modelChanged();
00958     }
00959 }
00960 
00961 void
00962 ImageLayer::toXml(QTextStream &stream,
00963                   QString indent, QString extraAttributes) const
00964 {
00965     Layer::toXml(stream, indent, extraAttributes);
00966 }
00967 
00968 void
00969 ImageLayer::setProperties(const QXmlAttributes &)
00970 {
00971 }
00972