diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmyth/mythdialogs.cpp release.20115.0305gridedit/mythtv/libs/libmyth/mythdialogs.cpp
--- mythtv/libs/libmyth/mythdialogs.cpp	2009-03-06 14:07:22.000000000 -0600
+++ mythtv/libs/libmyth/mythdialogs.cpp	2009-03-11 20:34:21.000000000 -0500
@@ -1258,7 +1258,7 @@
         rect_to_update = this->geometry();
     }
 
-    redrawRect = redrawRect.unite(r);
+    redrawRect = redrawRect.unite(rect_to_update);
 
     update(redrawRect);
 }
@@ -1600,6 +1600,16 @@
     return GetUIType<UIBlackHoleType>(this, name);
 }
 
+UIGridEditImageType* MythThemedDialog::getUIGridEditImageType(const QString &name)
+{
+    return GetUIType<UIGridEditImageType>(this, name);
+}
+
+UIGridEditSliderType* MythThemedDialog::getUIGridEditSliderType(const QString &name)
+{
+    return GetUIType<UIGridEditSliderType>(this, name);
+}
+
 UIImageType* MythThemedDialog::getUIImageType(const QString &name)
 {
     return GetUIType<UIImageType>(this, name);
diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmyth/mythdialogs.h release.20115.0305gridedit/mythtv/libs/libmyth/mythdialogs.h
--- mythtv/libs/libmyth/mythdialogs.h	2009-03-06 14:07:22.000000000 -0600
+++ mythtv/libs/libmyth/mythdialogs.h	2009-03-11 20:34:21.000000000 -0500
@@ -33,6 +33,8 @@
 class UICheckBoxType;
 class UISelectorType;
 class UIBlackHoleType;
+class UIGridEditImageType;
+class UIGridEditSliderType;
 class UIImageType;
 class UIImageGridType;
 class UIStatusBarType;
@@ -373,6 +375,8 @@
     UICheckBoxType *getUICheckBoxType(const QString &name);
     UISelectorType *getUISelectorType(const QString &name);
     UIBlackHoleType *getUIBlackHoleType(const QString &name);
+    UIGridEditImageType *getUIGridEditImageType(const QString &name);
+    UIGridEditSliderType *getUIGridEditSliderType(const QString &name);
     UIImageGridType *getUIImageGridType(const QString &name);
     UIImageType *getUIImageType(const QString &name);
     UIStatusBarType *getUIStatusBarType(const QString &name);
diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmyth/uitypes.cpp release.20115.0305gridedit/mythtv/libs/libmyth/uitypes.cpp
--- mythtv/libs/libmyth/uitypes.cpp	2009-03-06 14:07:22.000000000 -0600
+++ mythtv/libs/libmyth/uitypes.cpp	2009-03-11 20:34:21.000000000 -0500
@@ -5338,6 +5338,324 @@
 
 // ********************************************************************
 
+UIGridEditImageType::UIGridEditImageType(const QString &name)
+                     : UIType(name)
+{
+    cutStatus = 0;
+    highlightFrame = false; // Used to highlight frame #0
+    framenumber = -1;
+}
+
+void UIGridEditImageType::calculateScreenArea()
+{
+    QRect r = area;
+    r.moveBy(m_parent->GetAreaRect().left(),
+             m_parent->GetAreaRect().top());
+    screen_area = r;
+
+    inner_border.setLeft(screen_area.left()+1);
+    inner_border.setRight(screen_area.right()-1);
+    inner_border.setTop(screen_area.top()+1);
+    inner_border.setBottom(screen_area.bottom()-1);
+
+    outer_border = screen_area;
+}
+
+void UIGridEditImageType::Draw(QPainter *p, int drawlayer, int context)
+{
+    if (pmap.isNull())
+    {
+        QColor drawcolor = QColor( 0, 0, 0);
+        p->setBrush(QBrush(Qt::SolidPattern));
+        p->setPen(QPen(drawcolor, 2));
+        p->drawRect(screen_area);
+    }
+    else
+    {
+        p->drawPixmap(screen_area, pmap);
+
+        p->setBrush(QBrush(Qt::NoBrush));
+        // Draw the frame outline
+        // This may be covered by the cut indicator
+        // Or the highlight
+  
+        p->setPen(QPen(colorSet[0], 3));
+        p->drawRect(outer_border);
+
+        if (cutStatus > 0)
+        {
+            p->setPen(QPen(colorSet[cutStatus], 3));
+            p->drawRect(inner_border);
+            p->drawLine(inner_border.topLeft(), inner_border.bottomRight());
+            p->drawLine(inner_border.bottomLeft(), inner_border.topRight());
+        }
+        if (highlightFrame)
+        {
+            // Overlay the image frame with the standard frame color
+            // This is used to highlight small frame #0
+            p->setPen(QPen(highlightColor, 3));
+            p->drawRect(outer_border);
+        }
+    }
+}
+
+void UIGridEditImageType::setPixmap(QPixmap *new_pmap, long long frame, int new_cutStatus)
+{
+    // We can get a null new_pmap at the start or end of the video
+    if (! new_pmap)
+    {
+        clearPixmap();
+        return;
+    }
+
+    if (frame == framenumber)
+    {
+        // No change in pixmap (?)
+        setCutStatus(new_cutStatus);
+        return;
+    }
+
+    pmap = *new_pmap;
+    framenumber = frame;
+
+    if (new_cutStatus >= 0 && new_cutStatus <= 3)
+        cutStatus = new_cutStatus;
+    else
+        cutStatus = 0;
+
+    refresh();
+}
+
+void UIGridEditImageType::clearPixmap(bool dorefresh)
+{
+    if (! pmap.isNull())
+    {
+        pmap = QPixmap();
+
+        cutStatus = 0;
+        framenumber = -1;
+        if (dorefresh)
+            refresh();
+    }
+}
+
+void UIGridEditImageType::setCutStatus(int new_cutStatus)
+{
+    if (new_cutStatus == cutStatus)
+        return;
+
+    if (new_cutStatus >= 0 && new_cutStatus <= 3)
+        cutStatus = new_cutStatus;
+    else
+        cutStatus = 0;
+
+    refresh();
+}
+
+// ********************************************************************
+
+UIGridEditSliderType::UIGridEditSliderType(const QString &name)
+                     : UIType(name)
+{
+    m_drawMap = NULL;
+    m_position=0;
+}
+
+void UIGridEditSliderType::calculateScreenArea()
+{
+    QRect r = area;
+    r.moveBy(m_parent->GetAreaRect().left(),
+             m_parent->GetAreaRect().top());
+    screen_area = r;
+
+    if (m_drawMap)
+        delete [] m_drawMap;
+
+    m_drawWidth = area.width();
+    m_drawMap = new unsigned char[m_drawWidth];
+    for (int i = 0; i < m_drawWidth; i++)
+        m_drawMap[i] = 0;
+}
+
+void UIGridEditSliderType::ClearAll()
+{
+    for (int i = 0; i < m_drawWidth; i++)
+        m_drawMap[i] = 0;
+    refresh();
+}
+
+void UIGridEditSliderType::SetRange(
+        long long fstart, long long fend, long long fcount)
+{
+    if (fcount <= 0)
+    {
+        VERBOSE(VB_IMPORTANT, QString("Invalid frame count: %1")
+                .arg(fcount));
+        return;
+    }
+    if (fstart < 0)
+    {
+        VERBOSE(VB_IMPORTANT, QString("Invalid starting frame: %1")
+                .arg(fstart));
+        return;
+    }
+    if (fend < 0)
+    {
+        VERBOSE(VB_IMPORTANT, QString("Invalid ending frame: %1")
+                .arg(fend));
+        return;
+    }
+
+    if (fstart > fcount) fstart = fcount;
+    if (fend > fcount) fend = fcount;
+
+    int start = (int)((1.0 * fstart * m_drawWidth) / fcount);
+    int end   = (int)((1.0 * fend   * m_drawWidth) / fcount);
+
+    if (start < 0)
+        start = 0;
+    if (start >= m_drawWidth)
+        start = m_drawWidth - 1;
+    if (end < 0)
+        end = 0;
+    if (end >= m_drawWidth)
+        end = m_drawWidth - 1;
+
+    if (end < start)
+    {
+        int tmp = start;
+        start = end;
+        end = tmp;
+    }
+
+    for (int i = start; i < end; i++)
+        if (m_drawMap[i] < 1)
+            m_drawMap[i] = 1;
+
+    // Mark endpoints
+    m_drawMap[start] = 2;
+    m_drawMap[end]   = 2;
+
+    VERBOSE(VB_GENERAL, QString("Range = %1 - %2 (%3 - %4)")
+            .arg(start).arg(end)
+            .arg(fstart).arg(fend));
+    refresh();
+}
+
+void UIGridEditSliderType::SetPosition(
+        long long fposition, long long fcount)
+{
+    if (fcount <= 0)
+    {
+        VERBOSE(VB_IMPORTANT, QString("Invalid frame count: %1")
+                .arg(fcount));
+        return;
+    }
+
+    if (fposition < 0)
+    {
+        VERBOSE(VB_IMPORTANT, QString("Invalid position frame: %1")
+                .arg(fposition));
+        return;
+    }
+
+    if (fposition > fcount) fposition = fcount;
+
+    int new_position = (int)(1.0 * fposition * m_drawWidth) / fcount;
+
+    if (new_position < 0)
+        new_position = 0;
+    if (new_position >= m_drawWidth)
+       new_position = m_drawWidth - 1;
+
+    if (new_position != m_position)
+    {
+        m_position = new_position;
+        refresh();
+    }
+}
+
+void UIGridEditSliderType::Draw(QPainter *p, int drawlayer, int context)
+{
+    // Draw Background
+    p->setPen(QPen(colorSet[0], 2));
+    p->setBrush(colorSet[0]);
+    p->drawRect(screen_area);
+
+    // Draw bars
+
+//    VERBOSE(VB_GENERAL, QString("Starting"));
+    int i = 0;
+    do {
+        int start = 0;
+        int end = 0;
+
+        while (i < m_drawWidth && m_drawMap[i] == 0) i++;
+        if (i == m_drawWidth) break;
+        start = i;
+
+        i++;
+        
+
+        while (i < m_drawWidth && m_drawMap[i] == 1) i++;
+        end = i;
+        if (end == m_drawWidth) end--;
+
+        // If the next map value is not a normal internal cutpoint
+        // increment i so we handle it properly
+        if (end+1 < m_drawWidth && m_drawMap[end+1] != 1)
+            i++;
+
+        // start == starting point
+        //   end == endingpoint
+        {
+            QRect r = screen_area;
+            r.setLeft(r.left() + start);
+            r.setWidth(end - start);
+
+//            VERBOSE(VB_GENERAL, QString("Cut from (%1, %2) - (%3, %4)")
+//                    .arg(r.left()).arg(r.top())
+//                    .arg(r.right()).arg(r.bottom()));
+
+//            VERBOSE(VB_GENERAL, QString("start = %1, m_position = %2, end = %3")
+//                    .arg(start)
+//                    .arg(m_position)
+//                    .arg(end));
+            
+            if (start <= m_position && m_position <= end)
+            {
+                p->setPen(QPen(colorSet[4], 2));
+                p->setBrush(colorSet[4]);
+            }
+            else
+            {
+                p->setPen(QPen(colorSet[1], 2));
+                p->setBrush(colorSet[1]);
+            }
+            p->drawRect(r);
+
+            p->setPen(QPen(colorSet[2], 2));
+            p->setBrush(colorSet[2]);
+            if (m_drawMap[start] == 2)
+                p->drawLine(r.topLeft(), r.bottomLeft()); 
+            if (m_drawMap[end] == 2)
+                p->drawLine(r.topRight(), r.bottomRight()); 
+
+        }
+    } while (i < m_drawWidth);
+
+    // Draw Current Position Mark
+
+    QPoint ptop(screen_area.left() + m_position, screen_area.top());
+    QPoint pbot(screen_area.left() + m_position, screen_area.bottom());
+
+    p->setPen(QPen(colorSet[3], 2));
+    p->setBrush(colorSet[3]);
+    p->drawLine(ptop, pbot);
+}
+
+// ********************************************************************
+
 UIKeyType::UIKeyType(const QString &name)
          : UIType(name)
 {
diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmyth/uitypes.h release.20115.0305gridedit/mythtv/libs/libmyth/uitypes.h
--- mythtv/libs/libmyth/uitypes.h	2009-03-06 14:07:22.000000000 -0600
+++ mythtv/libs/libmyth/uitypes.h	2009-03-11 20:34:21.000000000 -0500
@@ -1385,6 +1385,78 @@
     QRect area;
 };
 
+class MPUBLIC UIGridEditImageType : public UIType
+{
+    Q_OBJECT
+
+  public:
+
+    UIGridEditImageType(const QString &name);
+    void calculateScreenArea();
+    void setArea(QRect an_area)     { area = an_area; };
+
+    void setOutlineColor(QColor c)  { colorSet[0] = c; };
+    void setCutColor(QColor c)      { colorSet[1] = c; };
+    void setCutPointColor(QColor c)
+    {
+         colorSet[2] = c; // Cut before
+         colorSet[3] = c; // Cut after
+    };
+    void setHighlightFrame(bool high) { highlightFrame = high; };
+    void setHighlightColor(QColor c)  { highlightColor = c; };
+
+    virtual void Draw(QPainter *, int, int);
+    void setPixmap(QPixmap *new_pmap, long long frame, int new_cutStatus);
+ 
+    void clearPixmap(bool dorefresh=true);
+    void setCutStatus(int new_cutStatus);
+
+    QRect getOuterBorder() { return outer_border; };
+  protected:
+
+    QRect    area;
+    QRect    inner_border;
+    QRect    outer_border;
+    QPixmap  pmap;
+    long long framenumber; // for consistency checking
+    int      cutStatus;
+    QColor   colorSet[4];
+    bool     highlightFrame;
+    QColor   highlightColor;
+};
+
+class MPUBLIC UIGridEditSliderType : public UIType
+{
+    Q_OBJECT
+
+  public:
+
+    UIGridEditSliderType(const QString &name);
+    void calculateScreenArea();
+    void setArea(QRect an_area)     { area = an_area; };
+
+    void setFillColor(QColor c)     { colorSet[0] = c; };
+    void setCutColor(QColor c)      { colorSet[1] = c; };
+    void setCutPointColor(QColor c) { colorSet[2] = c; };
+    void setPositionColor(QColor c) { colorSet[3] = c; };
+    void setInCutColor(QColor c)    { colorSet[4] = c; };
+
+    void ClearAll();
+    void SetRange(long long fstart, long long fend, long long fcount);
+    void SetPosition(long long fposition, long long fcount);
+
+    virtual void Draw(QPainter *, int, int);
+  protected:
+
+    QRect          area;
+    unsigned char *m_drawMap;
+    int            m_drawWidth;
+    int            m_position;
+
+    QColor         colorSet[5];
+};
+
+
 class MPUBLIC UIKeyType : public UIType
 {
     Q_OBJECT
diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmyth/xmlparse.cpp release.20115.0305gridedit/mythtv/libs/libmyth/xmlparse.cpp
--- mythtv/libs/libmyth/xmlparse.cpp	2009-03-06 14:07:22.000000000 -0600
+++ mythtv/libs/libmyth/xmlparse.cpp	2009-03-11 20:34:21.000000000 -0500
@@ -13,6 +13,10 @@
 #undef LoadImage
 #endif
 
+#define LOC QString("XMLParse: ")
+#define LOC_WARN QString("XMLParse, Warning: ")
+#define LOC_ERR QString("XMLParse, Error: ")
+
 MPUBLIC QMap<QString, fontProp> globalFontMap;
 
 XMLParse::XMLParse(void)
@@ -1442,6 +1446,14 @@
             {
                 parseBlackHole(container, info);
             }
+            else if (info.tagName() == "grideditimage")
+            {
+                parseGridEditImage(container, info);
+            }
+            else if (info.tagName() == "grideditslider")
+            {
+                parseGridEditSlider(container, info);
+            }
             else if (info.tagName() == "area")
             {
                 area = parseRect(getFirstText(info));
@@ -3445,6 +3457,238 @@
     container->AddType(bh);
 }
 
+
+void XMLParse::parseGridEditImage(LayerSet *container, QDomElement &element)
+{
+    QRect area;
+    QColor outlineColor;
+    QColor cutColor;
+    QColor cutPointColor;
+    QColor highlightColor;
+    bool   haveOutlineColor = false;
+    bool   haveCutColor = false;
+    bool   haveCutPointColor = false;
+    bool   haveHighlightColor = false;
+    bool   highlightFrame = false;
+
+    QString name = element.attribute("name", "");
+    if (name.isNull() || name.isEmpty())
+    {
+        VERBOSE(VB_IMPORTANT, LOC_WARN + "GridEditImage needs a name");
+        return;
+    }
+    QString highlight = element.attribute("highlightframe", "");
+    if (highlight == "true")
+        highlightFrame = true;
+
+    for (QDomNode child = element.firstChild(); !child.isNull();
+         child = child.nextSibling())
+    {
+        QDomElement info = child.toElement();
+        if (!info.isNull())
+        {
+            if (info.tagName() == "area")
+            {
+                area = parseRect(getFirstText(info));
+                normalizeRect(area);
+            }
+            else if (info.tagName() == "outlinecolor")
+            {
+                haveOutlineColor = true;
+                outlineColor = getFirstText(info);
+            }
+            else if (info.tagName() == "cutcolor")
+            {
+                haveCutColor = true;
+                cutColor = getFirstText(info);
+            }
+            else if (info.tagName() == "cutpointcolor")
+            {
+                haveCutPointColor = true;
+                cutPointColor = getFirstText(info);
+            }
+            else if (info.tagName() == "highlightcolor")
+            {
+                haveHighlightColor = true;
+                highlightColor = getFirstText(info);
+            }
+            else
+            {
+                VERBOSE(VB_IMPORTANT, LOC_WARN +
+                        QString("Unknown tag '%1' in grideditimage '%2'")
+                        .arg(info.tagName()).arg(name));
+                return;
+            }
+        }
+    }
+
+    if (!haveOutlineColor)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_WARN +
+                QString("Missing tag 'outlinecolor' in grideditimage '%1'")
+                .arg(name));
+        return;
+    }
+
+    if (!haveCutColor)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_WARN +
+                QString("Missing tag 'cutcolor' in grideditimage '%1'")
+                .arg(name));
+        return;
+    }
+
+    if (!haveCutPointColor)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_WARN +
+                QString("Missing tag 'cutpointcolor' in grideditimage '%1'")
+                .arg(name));
+        return;
+    }
+
+    if (highlightFrame && !haveHighlightColor)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_WARN +
+                QString("highlightframe specified but missing tag 'hightlightcolor' in grideditimage '%1'")
+                .arg(name));
+        return;
+    }
+
+
+    UIGridEditImageType *gei = new UIGridEditImageType(name);
+    gei->SetScreen(wmult, hmult);
+    gei->setArea(area);
+    gei->setOutlineColor(outlineColor);
+    gei->setCutColor(cutColor);
+    gei->setCutPointColor(cutPointColor);
+    gei->setHighlightColor(highlightColor);
+    gei->setHighlightFrame(highlightFrame);
+    gei->SetParent(container);
+    gei->calculateScreenArea();
+    gei->clearPixmap(false);
+    container->AddType(gei);
+}
+
+void XMLParse::parseGridEditSlider(LayerSet *container, QDomElement &element)
+{
+    QRect area;
+    QColor fillColor;
+    QColor cutColor;
+    QColor inCutColor;
+    QColor cutPointColor;
+    QColor positionColor;
+    bool   haveFillColor = false;
+    bool   haveCutColor = false;
+    bool   haveInCutColor = false;
+    bool   haveCutPointColor = false;
+    bool   havePositionColor = false;
+
+    QString name = element.attribute("name", "");
+    if (name.isNull() || name.isEmpty())
+    {
+        VERBOSE(VB_IMPORTANT, LOC_WARN + "GridEditSlider needs a name");
+        return;
+    }
+
+    for (QDomNode child = element.firstChild(); !child.isNull();
+         child = child.nextSibling())
+    {
+        QDomElement info = child.toElement();
+        if (!info.isNull())
+        {
+            if (info.tagName() == "area")
+            {
+                area = parseRect(getFirstText(info));
+                normalizeRect(area);
+            }
+            else if (info.tagName() == "fillcolor")
+            {
+                haveFillColor = true;
+                fillColor = getFirstText(info);
+            }
+            else if (info.tagName() == "cutcolor")
+            {
+                haveCutColor = true;
+                cutColor = getFirstText(info);
+            }
+            else if (info.tagName() == "incutcolor")
+            {
+                haveInCutColor = true;
+                inCutColor = getFirstText(info);
+            }
+            else if (info.tagName() == "cutpointcolor")
+            {
+                haveCutPointColor = true;
+                cutPointColor = getFirstText(info);
+            }
+            else if (info.tagName() == "positioncolor")
+            {
+                havePositionColor = true;
+                positionColor = getFirstText(info);
+            }
+            else
+            {
+                VERBOSE(VB_IMPORTANT, LOC_WARN +
+                        QString("Unknown tag '%1' in grideditslider '%2'")
+                        .arg(info.tagName()).arg(name));
+                return;
+            }
+        }
+    }
+
+    if (!haveFillColor)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_WARN +
+                QString("Missing tag 'fillcolor' in grideditslider '%1'")
+                .arg(name));
+        return;
+    }
+
+    if (!haveCutColor)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_WARN +
+                QString("Missing tag 'cutcolor' in grideditslider '%1'")
+                .arg(name));
+        return;
+    }
+
+    if (!haveInCutColor)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_WARN +
+                QString("Missing tag 'incutcolor' in grideditslider '%1'")
+                .arg(name));
+        return;
+    }
+
+    if (!haveCutPointColor)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_WARN +
+                QString("Missing tag 'cutpointcolor' in grideditslider '%1'")
+                .arg(name));
+        return;
+    }
+
+    if (!havePositionColor)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_WARN +
+                QString("Missing tag 'positioncolor' in grideditslider '%1'")
+                .arg(name));
+        return;
+    }
+
+    UIGridEditSliderType *ges = new UIGridEditSliderType(name);
+    ges->SetScreen(wmult, hmult);
+    ges->setArea(area);
+    ges->setFillColor(fillColor);
+    ges->setCutColor(cutColor);
+    ges->setInCutColor(inCutColor);
+    ges->setCutPointColor(cutPointColor);
+    ges->setPositionColor(positionColor);
+    ges->SetParent(container);
+    ges->calculateScreenArea();
+    container->AddType(ges);
+}
+
 void XMLParse::parseListBtnArea(LayerSet *container, QDomElement &element)
 {
     int context = -1;
diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmyth/xmlparse.h release.20115.0305gridedit/mythtv/libs/libmyth/xmlparse.h
--- mythtv/libs/libmyth/xmlparse.h	2009-03-06 14:07:22.000000000 -0600
+++ mythtv/libs/libmyth/xmlparse.h	2009-03-11 20:34:21.000000000 -0500
@@ -49,6 +49,8 @@
     void parseCheckBox(LayerSet *, QDomElement &);
     void parseSelector(LayerSet *, QDomElement &);
     void parseBlackHole(LayerSet *, QDomElement &);
+    void parseGridEditImage(LayerSet *, QDomElement &);
+    void parseGridEditSlider(LayerSet *, QDomElement &);
     void parseListBtnArea(LayerSet *, QDomElement &); 
     void parseListTreeArea(LayerSet *, QDomElement &);
     void parseKeyboard(LayerSet *, QDomElement &);
diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmythtv/NuppelVideoPlayer.cpp release.20115.0305gridedit/mythtv/libs/libmythtv/NuppelVideoPlayer.cpp
--- mythtv/libs/libmythtv/NuppelVideoPlayer.cpp	2009-03-06 14:07:22.000000000 -0600
+++ mythtv/libs/libmythtv/NuppelVideoPlayer.cpp	2009-03-11 20:34:21.000000000 -0500
@@ -53,6 +53,8 @@
 #include "interactivetv.h"
 #include "util-osx-cocoa.h"
 
+#include "grideditcutpoints.h"
+
 extern "C" {
 #include "vbitext/vbi.h"
 #include "vsync.h"
@@ -157,6 +159,7 @@
       decoder_thread_alive(true),   killplayer(false),
       killvideo(false),             livetv(false),
       watchingrecording(false),     editmode(false),
+      hideedits(false),
       resetvideo(false),            using_null_videoout(false),
       no_audio_in(false),           no_audio_out(false),
       transcoding(false),
@@ -166,7 +169,9 @@
       bookmarkseek(0),              previewFromBookmark(false),
       // Seek
       fftime(0),                    seekamountpos(4),
-      seekamount(30),               exactseeks(false),
+      allow_pagesize(false),        half_page(0),
+      seekamount(30),               seekamounttext("30 Frames"),
+      exactseeks(false),
       // Playback misc.
       videobuf_retries(0),          framesPlayed(0),
       totalFrames(0),               totalLength(0),
@@ -218,6 +223,13 @@
       yuv_need_copy(false),         yuv_desired_size(0,0),
       yuv_scaler(NULL),             yuv_frame_scaled(NULL),
       yuv_scaler_in_size(0,0),      yuv_scaler_out_size(0,0),
+      // Grid Editing
+      grid_edit_image_scaler(NULL), grid_edit_image_small_scaler(NULL),
+      grid_edit_image_buffer_yuv(NULL), grid_edit_image_small_buffer_yuv(NULL),
+      grid_edit_image_buffer_rgb(NULL), grid_edit_image_small_buffer_rgb(NULL),
+      grid_edit_image_buffer_length(0), grid_edit_image_small_buffer_length(0),
+      grid_edit_image_in_size(-1, -1),
+
       // Filters
       videoFiltersForProgram(""),   videoFiltersOverride(""),
       postfilt_width(0),            postfilt_height(0),
@@ -388,6 +400,8 @@
         output_jmeter = NULL;
     }
 
+    ClearScreenGrab();
+
     ShutdownYUVResize();
 }
 
@@ -1526,6 +1540,94 @@
     return retval;
 }
 
+
+bool NuppelVideoPlayer::GetScreenGrabsOfCurrentFrame(QImage &normal, QImage &small)
+{
+
+    unsigned char *data      = NULL;
+    VideoFrame    *frame     = NULL;
+    AVPicture      orig;
+    AVPicture      scaled;
+    AVPicture      scaled_small;
+    bzero(&orig,         sizeof(AVPicture));
+    bzero(&scaled,       sizeof(AVPicture));
+    bzero(&scaled_small, sizeof(AVPicture));
+
+    int vw, vh;
+    if (!(frame = GetCurrentFrame(vw, vh)))
+    {
+        return false;
+    }
+
+    if (!(data = frame->buf))
+    {
+        ReleaseCurrentFrame(frame);
+        return false;;
+    }
+
+    // Check to see if the screen dimensions have changed
+    // Probably shouldn't happen in normal use.
+    if (grid_edit_image_in_size != video_dim)
+        SetupScreenGrab();
+
+    avpicture_fill(&orig, data, PIX_FMT_YUV420P,
+                   video_dim.width(), video_dim.height());
+
+    avpicture_deinterlace(&orig, &orig, PIX_FMT_YUV420P,
+                          video_dim.width(), video_dim.height());
+
+    // Rescale to the normal size
+    avpicture_fill(&scaled, grid_edit_image_buffer_yuv, PIX_FMT_YUV420P,
+                   grid_edit_image_size.width(),
+                   grid_edit_image_size.height());
+
+    img_resample(grid_edit_image_scaler, &scaled, &orig);
+
+    // Don't need the current frame anymore so release it
+    ReleaseCurrentFrame(frame);
+
+    // Rescale to the small size
+    avpicture_fill(&scaled_small, grid_edit_image_small_buffer_yuv, PIX_FMT_YUV420P,
+                   grid_edit_image_small_size.width(),
+                   grid_edit_image_small_size.height());
+
+    img_resample(grid_edit_image_small_scaler, &scaled_small, &scaled);
+
+    // Convert scaled and scaled_small from YUV to RGB
+
+    // scaled first
+    uint w = grid_edit_image_size.width();
+    uint h = grid_edit_image_size.height();
+    unsigned char *yuv_buf = grid_edit_image_buffer_yuv;
+    unsigned char *rgb_buf = grid_edit_image_buffer_rgb;
+    yuv2argb_conv(rgb_buf,
+                  yuv_buf, yuv_buf + (w * h), yuv_buf + (w * h * 5 / 4),
+                  w, h, w * 4, w, w / 2, 0);
+
+    // scaled_small next
+    w = grid_edit_image_small_size.width();
+    h = grid_edit_image_small_size.height();
+    yuv_buf = grid_edit_image_small_buffer_yuv;
+    rgb_buf = grid_edit_image_small_buffer_rgb;
+    yuv2argb_conv(rgb_buf,
+                  yuv_buf, yuv_buf + (w * h), yuv_buf + (w * h * 5 / 4),
+                  w, h, w * 4, w, w / 2, 0);
+
+#ifdef MMX
+#define _RGBSWAP .swapRGB()
+#endif
+
+    normal = QImage(grid_edit_image_buffer_rgb,
+                    grid_edit_image_size.width(), grid_edit_image_size.height(),
+                    32, NULL, 65536 * 65536, QImage::LittleEndian)_RGBSWAP;
+
+    small = QImage(grid_edit_image_small_buffer_rgb,
+                   grid_edit_image_small_size.width(), grid_edit_image_small_size.height(),
+                   32, NULL, 65536 * 65536, QImage::LittleEndian)_RGBSWAP;
+
+    return true;
+}
+
 void NuppelVideoPlayer::ReleaseCurrentFrame(VideoFrame *frame)
 {
     if (frame)
@@ -2926,6 +3028,103 @@
     }
 }
 
+void NuppelVideoPlayer::SetScreenGrabSizes(QSize normal, QSize small)
+{
+    grid_edit_image_size = normal;
+    grid_edit_image_small_size = small;
+
+    SetupScreenGrab();
+    VERBOSE(VB_GENERAL, QString("Main Image = (%1, %2) from (%3, %4)")
+                        .arg(grid_edit_image_size.width())
+                        .arg(grid_edit_image_size.height())
+                        .arg(normal.width()).arg(normal.height()));
+    VERBOSE(VB_GENERAL, QString("Small Image = (%1, %2) from (%3, %4)")
+                        .arg(grid_edit_image_small_size.width())
+                        .arg(grid_edit_image_small_size.height())
+                        .arg(small.width()).arg(small.height()));
+
+}
+
+void NuppelVideoPlayer::ClearScreenGrab()
+{
+    if (grid_edit_image_scaler)
+    {
+        img_resample_close(grid_edit_image_scaler);
+        grid_edit_image_scaler = NULL;
+    }
+
+    if (grid_edit_image_small_scaler)
+    {
+        img_resample_close(grid_edit_image_small_scaler);
+        grid_edit_image_small_scaler=NULL;
+    }
+
+    if (grid_edit_image_buffer_yuv)
+    {
+        delete grid_edit_image_buffer_yuv;
+        grid_edit_image_buffer_yuv=NULL;
+    }
+
+    if (grid_edit_image_small_buffer_yuv)
+    {
+        delete grid_edit_image_small_buffer_yuv;
+        grid_edit_image_small_buffer_yuv=NULL;
+    }
+
+    if (grid_edit_image_buffer_rgb)
+    {
+        delete grid_edit_image_buffer_rgb;
+        grid_edit_image_buffer_rgb=NULL;
+    }
+
+    if (grid_edit_image_small_buffer_rgb)
+    {
+        delete grid_edit_image_small_buffer_rgb;
+        grid_edit_image_small_buffer_rgb=NULL;
+    }
+}
+
+void NuppelVideoPlayer::SetupScreenGrab()
+{
+    ClearScreenGrab();
+    grid_edit_image_in_size = video_dim;
+
+    // Normalize the output sizes.
+    // This is necessary to preserve the aspect ratio
+
+    QSize tmpsize = grid_edit_image_size;
+    grid_edit_image_size =  video_dim;
+    grid_edit_image_size.scale(tmpsize, QSize::ScaleMin);
+
+    tmpsize = grid_edit_image_small_size;
+    grid_edit_image_small_size = video_dim;
+    grid_edit_image_small_size.scale(tmpsize, QSize::ScaleMin);
+
+    grid_edit_image_size = QSize(grid_edit_image_size.width() & ~0x7,
+                                 grid_edit_image_size.height() & ~0x7);
+    grid_edit_image_small_size = QSize(grid_edit_image_small_size.width() & ~0x7,
+                                       grid_edit_image_small_size.height() & ~0x7);
+
+    // Do normal first
+    uint sz = grid_edit_image_size.width() * grid_edit_image_size.height();
+    grid_edit_image_buffer_yuv = new unsigned char[(sz * 3 / 2) + 128];
+    grid_edit_image_buffer_rgb = new unsigned char[sz * 4 + 128];
+
+    grid_edit_image_scaler = img_resample_init(
+                grid_edit_image_size.width(), grid_edit_image_size.height(),
+                grid_edit_image_in_size.width(),  grid_edit_image_in_size.height());
+
+    // Then small
+    sz = grid_edit_image_small_size.width() * grid_edit_image_small_size.height();
+    grid_edit_image_small_buffer_yuv = new unsigned char[(sz * 3 / 2) + 128];
+    grid_edit_image_small_buffer_rgb = new unsigned char[sz * 4 + 128];
+
+    // Resize from normal to small
+    grid_edit_image_small_scaler = img_resample_init(
+                grid_edit_image_small_size.width(), grid_edit_image_small_size.height(),
+                grid_edit_image_size.width(),  grid_edit_image_size.height());
+}
+
 void NuppelVideoPlayer::DisplayNormalFrame(void)
 {
     SetVideoActuallyPaused(false);
@@ -4989,6 +5188,8 @@
 
     dialogname = "";
 
+    osd->HideAllExcept("editmode");
+
     QMap<QString, QString> infoMap;
     m_playbackinfo->ToMap(infoMap);
     osd->SetText("editmode", infoMap, -1);
@@ -5044,6 +5245,78 @@
     m_playbackinfo->SetEditing(false);
 }
 
+bool NuppelVideoPlayer::EditSeekToFrame(long long targetFrame)
+{
+    bool tmpexactseeks = exactseeks;
+    GetDecoder()->setExactSeeks(true);
+
+//    VERBOSE(VB_GENERAL, QString("Before current frame = %1, going to frame %2")
+//           .arg(GetFramesPlayed())
+//           .arg(targetFrame));
+
+    if (framesPlayed > targetFrame)
+    {
+        // seek back
+        rewindtime = framesPlayed - targetFrame;
+        while (rewindtime != 0)
+            usleep(1000);
+    }
+    else
+    {
+        // seek forward
+        fftime = targetFrame - framesPlayed;
+        while (fftime != 0)
+            usleep(1000);
+
+    }
+//    VERBOSE(VB_GENERAL, QString("After current frame = %1")
+//           .arg(GetFramesPlayed()));
+    GetDecoder()->setExactSeeks(tmpexactseeks);
+    return (targetFrame == framesPlayed);
+}
+
+void NuppelVideoPlayer::EditHandleClearMap()
+{
+    QMap<long long, int>::Iterator it;
+    for (it = deleteMap.begin(); it != deleteMap.end(); ++it)
+        osd->HideEditArrow(it.key(), it.data());
+
+    deleteMap.clear();
+    UpdateEditSlider();
+}
+
+void NuppelVideoPlayer::EditHandleInvertMap()
+{
+    QMap<long long, int>::Iterator it;
+    for (it = deleteMap.begin(); it != deleteMap.end(); ++it)
+        ReverseMark(it.key());
+
+    UpdateEditSlider();
+    UpdateTimeDisplay();
+}
+
+void NuppelVideoPlayer::EditHandleLoadCommSkip()
+{
+    if (hascommbreaktable)
+    {
+        commBreakMapLock.lock();
+        QMap<long long, int>::Iterator it;
+        for (it = commBreakMap.begin(); it != commBreakMap.end(); ++it)
+        {
+            if (!deleteMap.contains(it.key()))
+            {
+                if (it.data() == MARK_COMM_START)
+                    AddMark(it.key(), MARK_CUT_START);
+                else
+                    AddMark(it.key(), MARK_CUT_END);
+            }
+        }
+        commBreakMapLock.unlock();
+        UpdateEditSlider();
+        UpdateTimeDisplay();
+    }
+}
+
 bool NuppelVideoPlayer::DoKeypress(QKeyEvent *e)
 {
     bool handled = false;
@@ -5123,44 +5396,11 @@
             UpdateTimeDisplay();
         }
         else if (action == "CLEARMAP")
-        {
-            QMap<long long, int>::Iterator it;
-            for (it = deleteMap.begin(); it != deleteMap.end(); ++it)
-                osd->HideEditArrow(it.key(), it.data());
-
-            deleteMap.clear();
-            UpdateEditSlider();
-        }
+            EditHandleClearMap();
         else if (action == "INVERTMAP")
-        {
-            QMap<long long, int>::Iterator it;
-            for (it = deleteMap.begin(); it != deleteMap.end(); ++it)
-                ReverseMark(it.key());
-
-            UpdateEditSlider();
-            UpdateTimeDisplay();
-        }
+            EditHandleInvertMap();
         else if (action == "LOADCOMMSKIP")
-        {
-            if (hascommbreaktable)
-            {
-                commBreakMapLock.lock();
-                QMap<long long, int>::Iterator it;
-                for (it = commBreakMap.begin(); it != commBreakMap.end(); ++it)
-                {
-                    if (!deleteMap.contains(it.key()))
-                    {
-                        if (it.data() == MARK_COMM_START)
-                            AddMark(it.key(), MARK_CUT_START);
-                        else
-                            AddMark(it.key(), MARK_CUT_END);
-                    }
-                }
-                commBreakMapLock.unlock();
-                UpdateEditSlider();
-                UpdateTimeDisplay();
-            }
-        }
+            EditHandleLoadCommSkip();
         else if (action == "PREVCUT")
         {
             int old_seekamount = seekamount;
@@ -5206,8 +5446,9 @@
             UpdateEditSlider();
             UpdateTimeDisplay();
         }
-        else if (action == "ESCAPE" || action == "MENU" ||
-                 action == "TOGGLEEDIT")
+        else if (action == "TOGGLEEDIT" || action == "MENU")
+              m_tv->ShowEditRecordingGrid();
+        else if (action == "ESCAPE")
         {
             DisableEdit();
             retval = false;
@@ -5220,6 +5461,37 @@
     return retval;
 }
 
+void NuppelVideoPlayer::ShowEditRecordingGrid(void)
+{
+    // Completely hide the OSD
+    osd->HideAll();
+    hideedits = true;
+
+    GridEditCutpoints::Run(this);
+
+    ClearScreenGrab();
+
+    allow_pagesize = false;
+    hideedits = false;
+
+    // Show OSD
+
+    QMap<QString, QString> infoMap;
+    m_playbackinfo->ToMap(infoMap);
+    osd->SetText("editmode", infoMap, -1);
+
+    UpdateEditSlider();
+    UpdateTimeDisplay();
+    if (seekamountpos == 3 || seekamountpos == 4)
+        UpdateSeekAmount(true);
+    else
+        UpdateSeekAmountDisplay();
+
+    QMap<long long, int>::Iterator it;
+    for (it = deleteMap.begin(); it != deleteMap.end(); ++it)
+         AddMark(it.key(), it.data());
+}
+
 AspectOverrideMode NuppelVideoPlayer::GetAspectOverride(void) const
 {
     if (videoOutput)
@@ -5311,31 +5583,74 @@
 
 void NuppelVideoPlayer::UpdateSeekAmount(bool up)
 {
-    if (seekamountpos > 0 && !up)
-        seekamountpos--;
-    if (seekamountpos < 9 && up)
-        seekamountpos++;
+    if (up)
+    {
+        if (seekamountpos < 11)
+            seekamountpos++;
+        if (allow_pagesize)
+        {
+            if (seekamountpos == 1)
+                seekamountpos = 2;
+        }
+        else
+        {
+            if (seekamountpos == 3 || seekamountpos == 4)
+                seekamountpos = 5;
+        }
+    }
+    else
+    {
+        if (seekamountpos > 0)
+            seekamountpos--;
+        if (allow_pagesize)
+        {
+            if (seekamountpos == 1)
+                seekamountpos =0;
+        }
+        else
+        {
+            if (seekamountpos == 3 || seekamountpos == 4)
+                seekamountpos = 2;
+        }
+    }
 
-    QString text = "";
+    QString text;
 
     switch (seekamountpos)
     {
-        case 0: text = QObject::tr("cut point"); seekamount = -2; break;
-        case 1: text = QObject::tr("keyframe"); seekamount = -1; break;
-        case 2: text = QObject::tr("1 frame"); seekamount = 1; break;
-        case 3: text = QObject::tr("0.5 seconds"); seekamount = (int)roundf(video_frame_rate / 2); break;
-        case 4: text = QObject::tr("1 second"); seekamount = (int)roundf(video_frame_rate); break;
-        case 5: text = QObject::tr("5 seconds"); seekamount = (int)roundf(video_frame_rate * 5); break;
-        case 6: text = QObject::tr("20 seconds"); seekamount = (int)roundf(video_frame_rate * 20); break;
-        case 7: text = QObject::tr("1 minute"); seekamount = (int)roundf(video_frame_rate * 60); break;
-        case 8: text = QObject::tr("5 minutes"); seekamount = (int)roundf(video_frame_rate * 300); break;
-        case 9: text = QObject::tr("10 minutes"); seekamount = (int)roundf(video_frame_rate * 600); break;
-        default: text = QObject::tr("error"); seekamount = (int)roundf(video_frame_rate); break;
+        case  0: text = QObject::tr("cut point");   seekamount = -2; break;
+
+        // Only for non-edit grid
+        case  1: text = QObject::tr("keyframe");    seekamount = -1; break;
+        // Only for non-edit grid
+
+        case  2: text = QObject::tr("1 frame");     seekamount =  1; break;
+
+        // Case 3 & 4 are for the edit grid only
+        case  3: text = QObject::tr("1/2 Page");    seekamount =  half_page; break;
+        case  4: text = QObject::tr("Full Page");   seekamount =  2*half_page; break;
+        // Case 3 & 4 are for the edit grid only
+
+        case  5: text = QObject::tr("0.5 seconds"); seekamount = (int)roundf(video_frame_rate / 2); break;
+        case  6: text = QObject::tr("1 second");    seekamount = (int)roundf(video_frame_rate); break;
+        case  7: text = QObject::tr("5 seconds");   seekamount = (int)roundf(video_frame_rate * 5); break;
+        case  8: text = QObject::tr("20 seconds");  seekamount = (int)roundf(video_frame_rate * 20); break;
+        case  9: text = QObject::tr("1 minute");    seekamount = (int)roundf(video_frame_rate * 60); break;
+        case 10: text = QObject::tr("5 minutes");   seekamount = (int)roundf(video_frame_rate * 300); break;
+        case 11: text = QObject::tr("10 minutes");  seekamount = (int)roundf(video_frame_rate * 600); break;
+        default: text = QObject::tr("error");       seekamount = (int)roundf(video_frame_rate); break;
     }
 
+    seekamounttext = text;
+    UpdateSeekAmountDisplay();
+}
+
+void NuppelVideoPlayer::UpdateSeekAmountDisplay(void)
+{
     QMap<QString, QString> infoMap;
-    infoMap["seekamount"] = text;
-    osd->SetText("editmode", infoMap, -1);
+    infoMap["seekamount"] = seekamounttext;
+    if (!hideedits)
+        osd->SetText("editmode", infoMap, -1);
 }
 
 void NuppelVideoPlayer::UpdateTimeDisplay(void)
@@ -5365,50 +5680,56 @@
     infoMap["timedisplay"] = timestr;
     infoMap["framedisplay"] = framestr;
     infoMap["cutindicator"] = cutmarker;
-    osd->SetText("editmode", infoMap, -1);
+    if (!hideedits)
+        osd->SetText("editmode", infoMap, -1);
 }
 
-void NuppelVideoPlayer::HandleSelect(bool allowSelectNear)
+DeletePointInfo NuppelVideoPlayer::GetDeletePointInfo(long long frame, bool allowSelectNear)
 {
-    bool deletepoint = false;
-    bool cut_after = false;
-    int direction = 0;
+    DeletePointInfo retval;
 
     if(!deleteMap.isEmpty())
     {
         QMap<long long, int>::ConstIterator iter = deleteMap.begin();
 
-        while((iter != deleteMap.end()) && (iter.key() < framesPlayed))
+        while((iter != deleteMap.end()) && (iter.key() < frame))
             ++iter;
 
         if (iter == deleteMap.end())
         {
             --iter;
-            cut_after = !iter.data();
+            retval.cut_after = !iter.data();
         }
-        else if((iter != deleteMap.begin()) && (iter.key() != framesPlayed))
+        else if((iter != deleteMap.begin()) && (iter.key() != frame))
         {
             long long value = iter.key();
-            if((framesPlayed - (--iter).key()) > (value - framesPlayed))
+            if((frame - (--iter).key()) > (value - frame))
             {
-                cut_after = !iter.data();
+                retval.cut_after = !iter.data();
                 ++iter;
             }
             else
-                cut_after = !iter.data();
+                retval.cut_after = !iter.data();
         }
 
-        direction = iter.data();
-        deleteframe = iter.key();
+        retval.direction = iter.data();
+        retval.deleteframe = iter.key();
 
-        if ((absLongLong(deleteframe - framesPlayed) <
+        if ((absLongLong(retval.deleteframe - frame) <
                    (int)ceil(20 * video_frame_rate)) && !allowSelectNear)
         {
-            deletepoint = true;
+            retval.deletepoint = true;
         }
     }
+    return retval;
+}
 
-    if (deletepoint)
+void NuppelVideoPlayer::HandleSelect(bool allowSelectNear)
+{
+    DeletePointInfo dpi = GetDeletePointInfo(framesPlayed, allowSelectNear);
+    deleteframe = dpi.deleteframe;
+
+    if (dpi.deletepoint)
     {
         QString message = QObject::tr("You are close to an existing cut point. "
                                       "Would you like to:");
@@ -5416,7 +5737,7 @@
         QString option2 = QObject::tr("Move this cut point to the current "
                                       "position");
         QString option3 = QObject::tr("Flip directions - delete to the ");
-        if (direction == 0)
+        if (dpi.direction == 0)
             option3 += QObject::tr("right");
         else
             option3 += QObject::tr("left");
@@ -5446,7 +5767,7 @@
         options += option1;
         options += option2;
 
-        osd->NewDialogBox(dialogname, message, options, -1, cut_after);
+        osd->NewDialogBox(dialogname, message, options, -1, dpi.cut_after);
     }
 }
 
@@ -5497,68 +5818,86 @@
 
 void NuppelVideoPlayer::UpdateEditSlider(void)
 {
-    osd->DoEditSlider(deleteMap, framesPlayed, totalFrames);
+    if (!hideedits)
+        osd->DoEditSlider(deleteMap, framesPlayed, totalFrames);
 }
 
 void NuppelVideoPlayer::AddMark(long long frames, int type)
 {
     deleteMap[frames] = type;
-    osd->ShowEditArrow(frames, totalFrames, type);
+    if (!hideedits)
+        osd->ShowEditArrow(frames, totalFrames, type);
 }
 
 void NuppelVideoPlayer::DeleteMark(long long frames)
 {
-    osd->HideEditArrow(frames, deleteMap[frames]);
+    if (!hideedits)
+        osd->HideEditArrow(frames, deleteMap[frames]);
     deleteMap.remove(frames);
 }
 
 void NuppelVideoPlayer::ReverseMark(long long frames)
 {
-    osd->HideEditArrow(frames, deleteMap[frames]);
+    if (!hideedits)
+        osd->HideEditArrow(frames, deleteMap[frames]);
 
     if (deleteMap[frames] == MARK_CUT_END)
         deleteMap[frames] = MARK_CUT_START;
     else
         deleteMap[frames] = MARK_CUT_END;
 
-    osd->ShowEditArrow(frames, totalFrames, deleteMap[frames]);
+    if (!hideedits)
+        osd->ShowEditArrow(frames, totalFrames, deleteMap[frames]);
+}
+
+long long NuppelVideoPlayer::CalcCutPointSeek(long long baseframe, bool right)
+{
+    QMap<long long, int>::Iterator i = deleteMap.begin();                                      
+    long long framenum = -1;
+    long long seekcount = 0;
+    if (right)                                                                                 
+    {
+        for (; i != deleteMap.end(); ++i)                                                      
+        {
+            if (i.key() > baseframe)                                                        
+            {
+                framenum = i.key();                                                            
+                break;                                                                          
+            }                                                                                  
+        }                                                                                      
+        if (framenum == -1)
+            framenum = totalFrames;
+        seekcount = framenum - baseframe;
+    }
+    else
+    {
+        for (; i != deleteMap.end(); ++i)
+        {
+            if (i.key() >= baseframe)
+                break;
+            framenum = i.key();
+        }
+        if (framenum == -1)
+            framenum = 0;
+        seekcount = baseframe - framenum;
+    }
+    return seekcount;
 }
 
 void NuppelVideoPlayer::HandleArbSeek(bool right)
 {
     if (seekamount == -2)
     {
-        QMap<long long, int>::Iterator i = deleteMap.begin();
-        long long framenum = -1;
+        long long seekcount = CalcCutPointSeek(framesPlayed, right);
         if (right)
         {
-            for (; i != deleteMap.end(); ++i)
-            {
-                if (i.key() > framesPlayed)
-                {
-                    framenum = i.key();
-                    break;
-                }
-            }
-            if (framenum == -1)
-                framenum = totalFrames;
-
-            fftime = framenum - framesPlayed;
+            fftime = seekcount;
             while (fftime > 0)
                 usleep(1000);
         }
         else
         {
-            for (; i != deleteMap.end(); ++i)
-            {
-                if (i.key() >= framesPlayed)
-                    break;
-                framenum = i.key();
-            }
-            if (framenum == -1)
-                framenum = 0;
-
-            rewindtime = framesPlayed - framenum;
+            rewindtime = seekcount;
             while (rewindtime > 0)
                 usleep(1000);
         }
@@ -5589,6 +5928,27 @@
     UpdateEditSlider();
 }
 
+int NuppelVideoPlayer::GetCutStatus(long long testframe) const
+{
+    int retval = 0;
+    QMap<long long, int>::const_iterator i;
+    i = deleteMap.find(testframe);
+    if (i == deleteMap.end()) {
+        // testframe is not an explicit cutpoint
+        // See if it is in a deleted area
+        if (IsInDelete(testframe))
+            retval = 1;
+    } else {
+        int direction = i.data();
+        if (direction == 0)
+            retval = 2;
+        else
+            retval = 3;
+    }
+
+    return retval;
+}
+
 bool NuppelVideoPlayer::IsInDelete(long long testframe) const
 {
     long long startpos = 0;
diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmythtv/NuppelVideoPlayer.h release.20115.0305gridedit/mythtv/libs/libmythtv/NuppelVideoPlayer.h
--- mythtv/libs/libmythtv/NuppelVideoPlayer.h	2009-03-06 14:07:22.000000000 -0600
+++ mythtv/libs/libmythtv/NuppelVideoPlayer.h	2009-03-11 20:34:21.000000000 -0500
@@ -100,8 +100,22 @@
     kDisplayTeletextMenu        = 0x40,
 };
 
+class DeletePointInfo
+{
+    public:
+        DeletePointInfo() :
+            deleteframe(0), deletepoint(false), cut_after(false), direction(0) {}
+       
+        long long deleteframe; 
+        bool deletepoint;
+        bool cut_after;
+        int direction;
+};
+
 class MPUBLIC NuppelVideoPlayer : public CC608Reader, public CC708Reader
 {
+ friend class GridEditCutpoints;
+ friend class TV;
  public:
     NuppelVideoPlayer(QString inUseID = "Unknown",
                       const ProgramInfo *info = NULL);
@@ -270,6 +284,7 @@
     bool EnableEdit(void);
     bool DoKeypress(QKeyEvent *e);
     bool GetEditMode(void) const { return editmode; }
+    bool GetHideEdits(void) const { return hideedits; }
 
     // Decoder stuff..
     VideoFrame *GetNextVideoFrame(bool allow_unsafe = true);
@@ -289,6 +304,17 @@
     void ShutdownYUVResize(void);
     void SaveScreenshot(void);
 
+    // Edit stuff
+    bool EditSeekToFrame(long long targetFrame); 
+    void SetScreenGrabSizes(QSize normal, QSize small);
+    bool GetScreenGrabsOfCurrentFrame(QImage & normal, QImage &small); // Get current frame
+
+    void       EditHandleClearMap();
+    void       EditHandleInvertMap();
+    void       EditHandleLoadCommSkip();
+
+    void ShowEditRecordingGrid();
+
     // Reinit
     void    ReinitOSD(void);
     void    ReinitVideo(void);
@@ -406,6 +432,17 @@
         hidedvdbutton = hide;
     }
 
+    // Stuf for GridEditCutpoints
+    long long CalcCutPointSeek(long long baseframe, bool right);
+    // returns 
+    // 0 - no cut
+    // 1 - is deleted
+    // 2 - cut left
+    // 3 - cut right
+    int  GetCutStatus(long long testframe) const;
+    long long GetSeekAmount() { return seekamount; }
+    QString GetSeekAmountText() { return seekamounttext; }
+
   protected:
     void DisplayPauseFrame(void);
     void DisplayNormalFrame(void);
@@ -487,8 +524,15 @@
     void HandleSelect(bool allowSelectNear = false);
     void HandleResponse(void);
 
+    DeletePointInfo GetDeletePointInfo(long long frame, bool allowSelectNear);
+    void SetupScreenGrab();
+    void ClearScreenGrab();
+
     void UpdateTimeDisplay(void);
+    int  GetSeekAmountPos() { return seekamountpos; }
     void UpdateSeekAmount(bool up);
+    void SetHalfPageSize(int hp) { allow_pagesize = true; half_page = hp; }
+    void UpdateSeekAmountDisplay(void);
     void UpdateEditSlider(void);
 
     // Private A/V Sync Stuff
@@ -560,6 +604,7 @@
     bool     livetv;
     bool     watchingrecording;
     bool     editmode;
+    bool     hideedits;
     bool     resetvideo;
     bool     using_null_videoout;
     bool     no_audio_in;
@@ -579,9 +624,13 @@
     long long fftime;
     /// 1..9 == keyframe..10 minutes. 0 == cut point
     int       seekamountpos;
+    ///  Used for Grid Edit logic
+    bool      allow_pagesize;
+    int       half_page;
     /// Seekable frame increment when not using exact seeks.
     /// Usually equal to keyframedist.
     int      seekamount;
+    QString  seekamounttext; // OSD seek units
     /// Iff true we ignore seek amount and try to seek to an
     /// exact frame ignoring key frame restrictions.
     bool     exactseeks;
@@ -714,6 +763,19 @@
     QMutex              yuv_lock;
     QWaitCondition      yuv_wait;
 
+    // EditGrid still image capture
+    ImgReSampleContext *grid_edit_image_scaler;
+    ImgReSampleContext *grid_edit_image_small_scaler;
+    unsigned char      *grid_edit_image_buffer_yuv;
+    unsigned char      *grid_edit_image_small_buffer_yuv;
+    unsigned char      *grid_edit_image_buffer_rgb;
+    unsigned char      *grid_edit_image_small_buffer_rgb;
+    int                 grid_edit_image_buffer_length;
+    int                 grid_edit_image_small_buffer_length;
+    QSize               grid_edit_image_in_size;
+    QSize               grid_edit_image_size;
+    QSize               grid_edit_image_small_size;
+
     // Filters
     QMutex   videofiltersLock;
     QString  videoFiltersForProgram;
diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmythtv/grideditcutpoints.cpp release.20115.0305gridedit/mythtv/libs/libmythtv/grideditcutpoints.cpp
--- mythtv/libs/libmythtv/grideditcutpoints.cpp	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/grideditcutpoints.cpp	2009-03-12 13:38:08.000000000 -0500
@@ -0,0 +1,715 @@
+#include <qapplication.h>
+#include <qpainter.h>
+#include <qfont.h>
+#include <qsqldatabase.h>
+#include <qsqlquery.h>
+#include <math.h>
+#include <qcursor.h>
+#include <qapplication.h>
+#include <qimage.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qdatetime.h>
+#include <qvgroupbox.h>
+#include <qheader.h>
+#include <qrect.h>
+
+#include <unistd.h>
+#include <iostream>
+#include <algorithm>
+using namespace std;
+
+#include "mythcontext.h"
+#include "mythdbcon.h"
+#include "grideditcutpoints.h"
+#include "grideditimages.h"
+#include "NuppelVideoPlayer.h"
+
+void GridEditCutpoints::Run(NuppelVideoPlayer *player)
+{
+    VERBOSE(VB_GENERAL, "Starting");
+    gContext->addCurrentLocation("GridEditCutpoints");
+
+    GridEditCutpoints *er = new GridEditCutpoints(gContext->GetMainWindow(),
+                                  player, "editrecording");
+
+    if (er->isValid())
+    {
+        er->Show();
+        er->displayInitialFrame();
+        er->exec();
+    }
+
+    delete er;
+
+    gContext->removeCurrentLocation();
+    VERBOSE(VB_GENERAL, "Ending");
+}
+
+GridEditCutpoints::GridEditCutpoints(MythMainWindow *parent,
+                     NuppelVideoPlayer *player, const char *name)
+         : MythThemedDialog(parent, "grideditcutpoints", "", name)
+{
+    m_player = player;
+    m_images = new GridEditImages(this, player);
+
+    int i;
+    m_gridimagemain = NULL;
+    for (i = m_gridimages.minIndex(); i < m_gridimages.maxIndex(); i++)
+        m_gridimages[i] = NULL;
+
+    usedSubVideoCount=0;
+
+    QSize videoSizeMain, videoSizeSmall;
+
+    m_gridimagemain = getUIGridEditImageType("mainvideo");
+    if (m_gridimagemain) 
+        videoSizeMain = m_gridimagemain->getScreenArea().size();
+    else
+    {
+        VERBOSE(VB_IMPORTANT, "FATAL: Couldn't find mainedit:mainvideo");
+        return;
+    }
+
+    // Small version of main frame
+    m_gridimages[0] = getUIGridEditImageType("video0");
+
+    for (i = 1; i < m_gridimages.maxIndex(); i++)
+    {
+        QString p = QString("videop%1").arg(i);
+        QString m = QString("videom%1").arg(i);
+
+        // Minus frame i
+        m_gridimages[-i] = getUIGridEditImageType(m);
+        if (m_gridimages[-i])
+        {
+            QSize tmpVideoSizeSmall = m_gridimages[-i]->getScreenArea().size();
+            if (videoSizeSmall.isValid() && videoSizeSmall != tmpVideoSizeSmall)
+                VERBOSE(VB_IMPORTANT,
+                        QString("Multiple sizes found for edit videos (%1)").arg(m));
+            else
+                videoSizeSmall = tmpVideoSizeSmall;
+            if (i > usedSubVideoCount) usedSubVideoCount=i;
+        }
+
+        m_gridimages[i] = getUIGridEditImageType(p);
+        if (m_gridimages[i])
+        {
+            QSize tmpVideoSizeSmall = m_gridimages[i]->getScreenArea().size();
+            if (videoSizeSmall.isValid() && videoSizeSmall != tmpVideoSizeSmall)
+                VERBOSE(VB_IMPORTANT,
+                        QString("Multiple sizes found for edit videos (%1)").arg(p));
+            else
+                videoSizeSmall = tmpVideoSizeSmall;
+            if (i > usedSubVideoCount) usedSubVideoCount=i;
+        }
+    }
+
+    imageScreenArea = m_gridimages[-usedSubVideoCount]->getOuterBorder();
+    for (i = -usedSubVideoCount; i <= usedSubVideoCount; i++)
+        if (m_gridimages[i])
+            imageScreenArea = imageScreenArea.unite(m_gridimages[i]->getOuterBorder());
+
+    m_images->SetVideoInfo(usedSubVideoCount, videoSizeMain, videoSizeSmall);
+
+    m_player->SetHalfPageSize(usedSubVideoCount);
+    m_slider = getUIGridEditSliderType("positionbar");
+    if (!m_slider)
+        VERBOSE(VB_GENERAL, "Missing positionbar for GridEditCutpoints");
+
+    // Get Status boxes
+    {
+        m_framenum    = getUITextType("framenum");
+        m_time        = getUITextType("time");
+        m_cutind      = getUITextType("cutind");
+        m_jumpstyle   = getUITextType("jumpstyle");
+        m_updatingind = getUITextType("updatingind");
+    }
+
+    VERBOSE(VB_GENERAL, QString("main = (%1, %2) small = (%3, %4)")
+                       .arg(videoSizeMain.width()).arg(videoSizeMain.height())
+                       .arg(videoSizeSmall.width()).arg(videoSizeSmall.height()));
+
+    slowMotionDirection = 1; // start off play forward
+    slowMotionActive = false;
+    readyForNextFrame = false;
+    slowMotionTimer = new QTimer(this);
+    QObject::connect(slowMotionTimer, SIGNAL(timeout()),
+                     this,           SLOT(updateSlowMotion()));
+
+    movingCutpoint = false;
+    // Create blank pixmaps...
+ 
+    updateBackground();
+
+    updateStats();
+
+    setNoErase();
+    gContext->addListener(this);
+    
+    updateForeground();
+}
+
+GridEditCutpoints::~GridEditCutpoints()
+{
+    if (m_images)
+    {
+        delete m_images;
+        m_images = NULL;
+    }
+    gContext->removeListener(this);
+}
+
+void GridEditCutpoints::displayInitialFrame()
+{
+    refreshImages();
+    refreshCutList();
+    updateStats();
+    SetUpdating(false);
+}
+
+void GridEditCutpoints::updateSlowMotion()
+{
+    if (!slowMotionActive)
+        slowMotionTimer->stop();
+    else if (readyForNextFrame && slowMotionDirection != 0)
+    {
+        readyForNextFrame=false;
+
+        if (slowMotionDirection > 0)
+            EditHandleRight();
+        else if (slowMotionDirection < 0)
+            EditHandleLeft();
+    }
+}
+
+
+void GridEditCutpoints::setSlowMotionSpeed()
+{
+    // slowMotionDirection is max FPS
+
+    if (slowMotionDirection != 0)
+    {
+        int smd = slowMotionDirection;
+        if (smd < 0) smd = -smd;
+        int timeout = 1000 / smd;
+
+        slowMotionTimer->start(timeout);
+    }
+    SetUpdating(true, QString("%1 FPS max").arg(slowMotionDirection));
+}
+
+
+void GridEditCutpoints::keyPressEvent(QKeyEvent *e)
+{
+    // keyDown limits keyrepeats to prevent continued scrolling
+    // after key is released. Note: Qt's keycompress should handle
+    // this but will fail with fast key strokes and keyboard repeat
+    // enabled. Keys will not be marked as autorepeat and flood buffer.
+    // setFocusPolicy(QWidget::ClickFocus) in constructor is important 
+    // or keyRelease events will not be received after a refocus.
+    
+    bool handled = false;
+
+    QStringList actions;
+    gContext->GetMainWindow()->TranslateKeyPress("TV Editing", e, actions);
+
+    {
+        for (unsigned int i = 0; i < actions.size() && !handled; i++)
+        {
+            QString action = actions[i];
+            handled = true;
+
+            if (action == "SELECT")
+            {
+                if (slowMotionActive)
+                {
+                    slowMotionActive = false;
+                    slowMotionTimer->stop();
+                }
+                else if (movingCutpoint)
+                {
+                    // Move cutpoint
+                    m_player->DeleteMark(savedCutpoint);
+                    m_player->AddMark(m_images->GetCurrentFrameNumber(), savedCutType);
+                    movingCutpoint = false;
+                    refreshCutList();
+                    refreshImages();
+                }
+                else
+                    handleSelect();
+            }
+            else if (action == "PAUSE") 
+            {
+                if (movingCutpoint)
+                    MythPopupBox::showOkPopup(gContext->GetMainWindow(),
+                                              "Moving Cutpoint",
+                                              "Slow Motion Unavailable when moving cutpoint");
+                else if (slowMotionActive)
+                {
+                    slowMotionActive = false;
+                    slowMotionTimer->stop();
+                }
+            }
+            else if (action == "SLOWMO")
+            {
+                if (movingCutpoint)
+                    MythPopupBox::showOkPopup(gContext->GetMainWindow(),
+                                              "Moving Cutpoint",
+                                              "Slow Motion Unavailable when moving cutpoint");
+                else
+                {
+                    if (slowMotionActive)
+                    {
+                        slowMotionActive = false;
+                        slowMotionTimer->stop();
+                    }
+                    else
+                    {
+                        slowMotionActive = true;
+                        readyForNextFrame = true;
+                        slowMotionDirection = 1;
+
+                        // force to either 1 frame. 1/2 page or full page motion
+                        int i;
+                        // move to 1 frame if on cutpoint
+                        for (i = 0; m_player->GetSeekAmountPos() < 2 && i < 10; i++)
+                            m_player->UpdateSeekAmount(true);
+
+                        // move to fullpage if higher 
+                        for (i = 0; m_player->GetSeekAmountPos() > 4 && i < 10; i++)
+                            m_player->UpdateSeekAmount(false);
+
+                        setSlowMotionSpeed();
+                    }
+                }
+            }
+            else if (action == "LEFT")
+            {
+                if (slowMotionActive)
+                {
+                    slowMotionDirection--;
+                    setSlowMotionSpeed();
+                }
+                else
+                {
+                    SetUpdating(true);
+                    EditHandleLeft();
+                }
+            }
+            else if (action == "RIGHT" )
+            {
+                if (slowMotionActive)
+                {
+                    slowMotionDirection++;
+                    setSlowMotionSpeed();
+                }
+                else
+                {
+                    SetUpdating(true);
+                    EditHandleRight();
+                }
+            }
+            else if (action == "UP")
+            {
+                m_player->UpdateSeekAmount(true);
+                updateStats();
+            }
+            else if (action == "DOWN")
+            {
+                if ((movingCutpoint || slowMotionActive) && m_player->GetSeekAmountPos() == 2)
+                    MythPopupBox::showOkPopup(gContext->GetMainWindow(),
+                                              "Moving Cutpoint",
+                                              "CutPoint skip Unavailable");
+                else
+                {
+                    m_player->UpdateSeekAmount(false);
+                    updateStats();
+                }
+            }
+            else if (action == "CLEARMAP")
+            {
+                if (movingCutpoint || slowMotionActive)
+                    MythPopupBox::showOkPopup(gContext->GetMainWindow(),
+                                              "Moving Cutpoint",
+                                              "Clear Cut Map Unavailable");
+                else
+                {
+                    SetUpdating(true);
+                    m_player->EditHandleClearMap();
+                    refreshCutList();
+                    updateStats();
+                }
+            }
+            else if (action == "INVERTMAP")
+            {
+                if (movingCutpoint || slowMotionActive)
+                    MythPopupBox::showOkPopup(gContext->GetMainWindow(),
+                                              "Moving Cutpoint",
+                                              "Invert Cut Map Unavailable");
+                else
+                {
+                    SetUpdating(true);
+                    m_player->EditHandleInvertMap();
+                    refreshCutList();
+                    updateStats();
+                }
+            }
+            else if (action == "LOADCOMMSKIP")
+            {
+                if (movingCutpoint || slowMotionActive)
+                    MythPopupBox::showOkPopup(gContext->GetMainWindow(),
+                                              "Moving Cutpoint",
+                                              "Load Comm Skip Map Unavailable");
+                else
+                {
+                    SetUpdating(true);
+                    m_player->EditHandleLoadCommSkip();
+                    refreshCutList();
+                    updateStats();
+                }
+            }
+            else if (action == "PREVCUT")
+            {
+                if (movingCutpoint || slowMotionActive)
+                    MythPopupBox::showOkPopup(gContext->GetMainWindow(),
+                                              "Moving Cutpoint",
+                                              "Prev Cut Unavailable");
+                else
+                {
+                    SetUpdating(true);
+                    EditHandlePrevCut();
+                }
+            }
+            else if (action == "NEXTCUT")
+            {
+                if (movingCutpoint || slowMotionActive)
+                    MythPopupBox::showOkPopup(gContext->GetMainWindow(),
+                                              "Moving Cutpoint",
+                                              "Next Cut Unavailable");
+                else
+                {
+                    SetUpdating(true);
+                    EditHandleNextCut();
+                }
+            }
+            else if (action == "BIGJUMPREW")
+            {
+                SetUpdating(true);
+                EditHandleBigJumpRew();
+            }
+            else if (action == "BIGJUMPFWD")
+            {
+                SetUpdating(true);
+                EditHandleBigJumpFwd();
+            }
+            else if (action == "ESCAPE" && movingCutpoint)
+                movingCutpoint = false;
+            else if (action == "ESCAPE" || action == "TOGGLEEDIT" ||
+                     action == "MENU")
+                escape();
+            else
+                handled = false;
+
+            if (movingCutpoint)
+                SetUpdating(true, "Moving Cutpoint");
+            else if (!slowMotionActive)
+                SetUpdating(false);
+        }
+    }
+
+    if (!handled)
+        MythDialog::keyPressEvent(e);
+}
+
+void GridEditCutpoints::EditHandleLeft(int seektype)
+{
+    long long seekamount = m_player->GetSeekAmount();
+    bool cutpointseek = false;
+
+    if (seektype == -2 || seekamount == -2)
+        cutpointseek = true;
+    else
+    {
+        // seektype == 1 for normal, 10 for bigjump
+        seekamount *= seektype;
+    }
+
+    if (seekamount < 0) // Invalid -- keyframe
+        seekamount = 1;
+
+    m_images->SeekLeft(seekamount, cutpointseek);
+
+    refreshImages();
+    updateStats();
+}
+
+void GridEditCutpoints::EditHandleRight(int seektype)
+{
+    long long seekamount = m_player->GetSeekAmount();
+    bool cutpointseek=false;
+
+    if (seektype == -2 || seekamount == -2)
+        cutpointseek = true;
+    else
+    {
+        // seektype == 1 for normal, 10 for bigjump
+        seekamount *= seektype;
+    }
+
+    if (seekamount < 0) // Invalid -- keyframe
+        seekamount = 1;
+
+    m_images->SeekRight(seekamount, cutpointseek);
+
+    refreshImages();
+    updateStats();
+}
+
+void GridEditCutpoints::handleSelect(void)
+{
+    bool needupdate = false;
+    // add / update cutpoint
+    // -or-
+    // delete / flip / move cutpoint
+
+    // if no cut points on screen 
+    //    "Delete Before"
+    //    "Delete After"
+
+    // if on existing cutpoint
+    //    "Delete cutpoint"
+    //    "Flip directions"
+
+    // FIXME
+    // if a cutpoint exists on the screen but not on the current frame
+    //    "Move to current frame"
+    //    "Add new"
+
+    FrameStats fs = m_images->GetMainFrameStats();
+    DeletePointInfo dpi = m_player->GetDeletePointInfo(fs.frameNumber, true);
+
+    if (fs.cutInd >= 2)
+    {
+        QString title = "";
+        QString message = "Cutpoint exists:";
+        QStringList buttons;
+        buttons.append("Delete cutpoint?");
+        buttons.append("Move cutpoint?");
+        if (fs.cutInd == 2)
+            buttons.append("Flip directions (Cut After)?");
+        else
+            buttons.append("Flip directions (Cut Before)?");
+
+        DialogCode dc = MythPopupBox::ShowButtonPopup(gContext->GetMainWindow(),
+                                                      title, message, buttons, kDialogCodeButton0);
+
+
+        if (dc != kDialogCodeRejected)
+        {
+            needupdate = true;
+            if (dc == kDialogCodeButton0)
+                // Delete cutpoint
+                m_player->DeleteMark(fs.frameNumber);
+
+            else if (dc == kDialogCodeButton1)
+            {
+                // Move cutpoint
+                savedCutpoint = fs.frameNumber;
+                savedCutType = m_player->deleteMap[fs.frameNumber];
+                movingCutpoint = true;
+                // Ensure we're at least at 1 frame motion
+                int i;
+                for (i = 0; m_player->GetSeekAmountPos() < 2 && i < 10; i++)
+                    m_player->UpdateSeekAmount(true);
+                    
+            }
+            else if (dc == kDialogCodeButton2)
+                // Flip
+                m_player->ReverseMark(fs.frameNumber);
+        }
+    }
+    else
+    {
+        QString title = "";
+        QString message = "Insert New Cutpoint?";
+        QStringList buttons;
+        buttons.append("Delete before this frame");
+        buttons.append("Delete after this frame");
+        DialogCode default_cut = (dpi.cut_after ? kDialogCodeButton1: kDialogCodeButton0);
+        DialogCode dc = MythPopupBox::ShowButtonPopup(gContext->GetMainWindow(),
+                                                      title, message, buttons, default_cut);
+
+        if (dc != kDialogCodeRejected)
+        {
+            needupdate = true;
+            if (dc == kDialogCodeButton0)
+                // Delete left
+                m_player->AddMark(fs.frameNumber, MARK_CUT_END);
+
+            else if (dc == kDialogCodeButton1)
+                // Delete Right
+                m_player->AddMark(fs.frameNumber, MARK_CUT_START);
+        }
+    }
+
+    if (needupdate)
+    {
+        refreshCutList();
+        refreshImages();
+    }
+}
+
+void GridEditCutpoints::refreshImages()
+{
+    m_images->refreshImages(m_gridimagemain, m_gridimages, false);
+    repaint(m_gridimagemain->getOuterBorder(), false);
+    repaint(imageScreenArea, false);
+}
+
+void GridEditCutpoints::refreshCutList()
+{
+    m_images->refreshCutList(m_gridimagemain, m_gridimages);
+    refreshSlider();
+}
+
+void GridEditCutpoints::refreshSlider()
+{
+    if (!m_slider)
+        return;
+
+    m_slider->ClearAll();
+
+    const int CUT_LEFT = 0;
+    const int CUT_RIGHT = 1;
+
+    long long startpos = 0;
+    long long endpos = 0;
+    
+    int       lastdirection = CUT_LEFT;
+
+    QMap<long long, int> & deleteMap = m_player->deleteMap;
+    QMap<long long, int>::Iterator i = deleteMap.begin();
+    for (; i != deleteMap.end(); ++i)
+    {
+        long long frame = i.key();
+        int direction = i.data();
+
+        if (direction == CUT_LEFT)
+        {
+            endpos = frame;
+            m_slider->SetRange(startpos, endpos, m_images->GetMaxFrameNumber());
+
+            startpos = frame;
+            lastdirection = CUT_LEFT;
+        }
+        else if (direction == CUT_RIGHT)
+        {
+            if (lastdirection == CUT_RIGHT)
+            {
+                // continuing within a cutpoint
+                endpos = frame;
+                m_slider->SetRange(startpos, endpos, m_images->GetMaxFrameNumber());
+            }
+
+            startpos = frame;
+            lastdirection = CUT_RIGHT;
+        }
+    }
+
+    if (lastdirection == CUT_RIGHT)
+    {
+        // continuing within a cutpoint
+        endpos = m_images->GetMaxFrameNumber();
+        m_slider->SetRange(startpos, endpos, m_images->GetMaxFrameNumber());
+    }
+}
+
+void GridEditCutpoints::updateStats(bool forcerepaint)
+{
+    int secs, frames, ss, mm, hh;
+
+    FrameStats fs = m_images->GetMainFrameStats();
+
+    secs = (int)(fs.frameNumber / m_player->GetFrameRate());
+    frames = fs.frameNumber - (int)(secs * m_player->GetFrameRate());
+
+    ss = secs;
+    mm = ss / 60;
+    ss %= 60;
+    hh = mm / 60;
+    mm %= 60;
+
+    char timestr[128];
+    sprintf(timestr, "%d:%02d:%02d.%02d", hh, mm, ss, frames);
+
+    char framestr[128];
+    sprintf(framestr, "%lld", fs.frameNumber);
+
+    if (m_time)
+    {
+        m_time->SetText(timestr);
+        if (forcerepaint)
+            repaint(m_time->getScreenArea());
+    }
+    if (m_framenum)
+    {
+        m_framenum->SetText(framestr);
+        if (forcerepaint)
+            repaint(m_framenum->getScreenArea());
+    }
+    if (m_cutind)
+    {
+        switch (fs.cutInd) {
+            case 0: m_cutind->SetText("");           break;
+            case 1: m_cutind->SetText("Cut");        break;
+            case 2: m_cutind->SetText("Cut Before"); break;
+            case 3: m_cutind->SetText("Cut After");  break;
+        }
+        if (forcerepaint)
+            repaint(m_cutind->getScreenArea());
+    }
+
+    // Don't need to force update this
+    if (m_jumpstyle)
+        m_jumpstyle->SetText(m_player->GetSeekAmountText());
+
+    if (m_slider)
+        m_slider->SetPosition(fs.frameNumber, fs.maxFrameNumber);
+
+}
+
+void GridEditCutpoints::escape()
+{
+    // Make sure we're on the right frame when we go back to
+    // Normal edit mode
+    unsetCursor();
+    accept();
+}
+
+void GridEditCutpoints::SetUpdating(bool active, QString text)
+{
+    if (m_updatingind)
+    {
+        //VERBOSE(VB_GENERAL, QString("Updating to %1").arg(active));
+        if (active)
+        {
+            m_updatingind->show();
+            m_updatingind->SetText(text);
+        }
+        else
+            m_updatingind->hide();
+        repaint(m_updatingind->getScreenArea());
+    }
+}
+
+void GridEditCutpoints::cacheFramesAreReady()
+{
+    if (slowMotionActive)
+    {
+        readyForNextFrame=true;
+        if (!slowMotionTimer->isActive())
+            slowMotionTimer->start(0);
+    }
+}
+
diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmythtv/grideditcutpoints.h release.20115.0305gridedit/mythtv/libs/libmythtv/grideditcutpoints.h
--- mythtv/libs/libmythtv/grideditcutpoints.h	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/grideditcutpoints.h	2009-03-12 12:26:48.000000000 -0500
@@ -0,0 +1,92 @@
+// -*- Mode: c++ -*-
+#ifndef GRIDEDITCUTPOINTS_H_
+#define GRIDEDITCUTPOINTS_H_
+
+#include <qstring.h>
+
+#include "libmyth/mythwidgets.h"
+#include "uitypes.h"
+
+#include "grideditimages.h"
+
+using namespace std;
+
+class QTimer;
+class NuppelVideoPlayer;
+class GridEditImages;
+
+class MPUBLIC GridEditCutpoints : public MythThemedDialog
+{
+    Q_OBJECT
+
+  public:
+    // Use this function to instantiate an GridEditCutpoints instance.
+    static void Run(NuppelVideoPlayer            *player);
+
+    void refreshImages();
+    void cacheFramesAreReady();
+    bool isValid() { return ((usedSubVideoCount > 0) && (m_gridimagemain != NULL)); };
+
+  protected:
+    GridEditCutpoints(MythMainWindow *parent,
+              NuppelVideoPlayer *player, const char *name = "GridEditCutpoints");
+   ~GridEditCutpoints();
+
+    void displayInitialFrame();
+
+    void handleSelect();
+
+    void updateStats(bool forcerepaint = false);
+
+  protected slots:
+    void escape();
+    void updateSlowMotion();
+
+  private:
+    void keyPressEvent(QKeyEvent *e);
+
+    // seektype == -2 - cutpoint seek
+    // seektype ==  1 - normal seek
+    // seektype == 10 - large seek
+    void EditHandleLeft(int seektype = 1);
+    void EditHandleRight(int seektype = 1);
+    void EditHandlePrevCut()    { EditHandleLeft(-2); };
+    void EditHandleNextCut()    { EditHandleRight(-2); };
+    void EditHandleBigJumpRew() { EditHandleLeft(10); };
+    void EditHandleBigJumpFwd() { EditHandleRight(10); };
+    void setSlowMotionSpeed();
+    void refreshCutList();
+    void refreshSlider();
+
+    void SetUpdating(bool active, QString text = "Updating");
+
+    // Private Data
+
+    NuppelVideoPlayer *m_player;
+    GridEditImages *m_images;
+
+    int   usedSubVideoCount;
+    myArray<UIGridEditImageType*, MAX_SUB_VIDEOS> m_gridimages;
+    // The main Big image
+    UIGridEditImageType* m_gridimagemain;
+
+    long long savedCutpoint;
+    int       savedCutType;
+    bool      movingCutpoint;
+
+    UITextType   *m_framenum;
+    UITextType   *m_time;
+    UITextType   *m_cutind;
+    UITextType   *m_jumpstyle;
+    UITextType   *m_updatingind;
+  
+    UIGridEditSliderType  *m_slider;
+    QTimer                *slowMotionTimer;
+    int                    slowMotionDirection;
+    bool                   slowMotionActive;
+    bool                   readyForNextFrame;
+
+    QRect                  imageScreenArea;
+};
+
+#endif
diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmythtv/grideditimages.cpp release.20115.0305gridedit/mythtv/libs/libmythtv/grideditimages.cpp
--- mythtv/libs/libmythtv/grideditimages.cpp	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/grideditimages.cpp	2009-03-12 13:55:04.000000000 -0500
@@ -0,0 +1,685 @@
+#include <qapplication.h>
+#include <qpainter.h>
+#include <qfont.h>
+#include <qsqldatabase.h>
+#include <qsqlquery.h>
+#include <math.h>
+#include <qcursor.h>
+#include <qapplication.h>
+#include <qimage.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qdatetime.h>
+#include <qvgroupbox.h>
+#include <qheader.h>
+#include <qrect.h>
+
+#include <unistd.h>
+#include <iostream>
+#include <algorithm>
+using namespace std;
+
+#include "mythcontext.h"
+#include "mythdbcon.h"
+#include "grideditimages.h"
+#include "grideditcutpoints.h"
+#include "NuppelVideoPlayer.h"
+
+#define FRAME_DEBUG 0
+#define CACHE_DEBUG 1
+
+GridEditImages::GridEditImages(GridEditCutpoints *editor, NuppelVideoPlayer *player)
+{
+    m_editor = editor;
+    m_player = player;
+    usedSubVideoCount = 0;
+
+    lastmovewasright= true;
+
+    int i;
+    for (i = stillFrames.minIndex(); i <= stillFrames.maxIndex(); i++)
+    {
+        stillFrames[i] = NULL;
+        stillFramesBig[i] = NULL;
+        cutFrames[i] = 0;
+    }
+
+    memset(cutFramesCache, 0, sizeof(cutFramesCache));
+    memset(stillFramesCache, 0, sizeof(stillFramesCache));
+    memset(stillFramesBigCache, 0, sizeof(stillFramesBigCache));
+    stillFrameCacheCount = 0;
+    stillFramesCacheBase = 0;
+
+
+    // Get first frames...
+    stillMainFrameNumber = m_player->GetFramesPlayed();
+    if (stillMainFrameNumber <= 0)
+        stillMainFrameNumber = 1;
+
+    // maxFrameNumber may be overridden if neccessary
+    maxFrameNumber = m_player->GetTotalFrameCount();
+    maxFrameNumberNVP = m_player->GetTotalFrameCount();
+
+    if (stillMainFrameNumber <= 0)
+        stillMainFrameNumber = 1;
+
+    if (stillMainFrameNumber > maxFrameNumber)
+        stillMainFrameNumber = maxFrameNumber;
+
+    getImagesTimer = new QTimer(this);
+    QObject::connect(getImagesTimer, SIGNAL(timeout()),
+                     this,           SLOT(updateAllFrames()));
+
+
+}
+
+GridEditImages::~GridEditImages()
+{
+    emptyCache();
+    clearStillFrames();
+    m_player->EditSeekToFrame(stillMainFrameNumber);
+}
+
+QPixmap *GridEditImages::makeScaledPixmap(const QImage& qim, QSize sz)
+{
+    QPixmap *retval;
+    if (qim.size() == sz)
+    {
+        retval = new QPixmap(sz);
+        QPainter p(retval);
+        p.drawImage(0, 0, qim);
+    }
+    else
+    {
+        retval = new QPixmap(sz);
+        retval->fill(Qt::black);
+        QPainter p(retval);
+
+        int tl_left = 0;
+        int tl_top = 0;
+        if (sz.width() > qim.width())
+            tl_left += (sz.width() - qim.width()) / 2;
+
+        if (sz.height() > qim.height())
+            tl_top += (sz.height() - qim.height()) / 2;
+
+//        VERBOSE(VB_GENERAL, QString("Size mismatch qim(%1, %2) != sz(%3, %4) Shift to (%5, %6)")
+//             .arg(qim.width()).arg(qim.height())
+//             .arg(sz.width()).arg(sz.height())
+//             .arg(tl_left).arg(tl_top));
+
+        p.drawImage(tl_left, tl_top, qim);
+    }
+    return retval;
+}
+
+
+void GridEditImages::getMainStillFrame()
+{
+    getSpecificFrame(0);
+}
+
+bool GridEditImages::getFrameIndexes(int newlevel)
+{
+    // levels:
+    // 0 - onscreen frames
+    // 1 - precache frames
+    // 2 - fill-out-the buffer frames
+
+    if (newlevel > 2)
+        return false;
+
+    // This gets the upper and lower indexes of the frames we need to get.
+    // Note these indexes maybe negative
+    // Negative frames are before the main frame
+    // Frame #0 is the main frame
+    // Positive frames are after the main frame
+
+    // Basic initialization
+    // Doesn't go to absolute end because it slows down seeking
+    // when you go 1 frame left and right
+    frameIndexLeft       = stillFrames.minIndex() + 4;
+    frameIndexRight      = stillFrames.maxIndex() - 4;
+    getFutureFramesFirst = false;
+
+    if (newlevel == 0) // Current Display only
+    {
+        frameIndexLeft  = -usedSubVideoCount;
+        frameIndexRight =  usedSubVideoCount;
+    }
+    else
+    {
+        getFutureFramesFirst = lastmovewasright;
+
+        if (newlevel == 1) // PreCache only
+        {
+            // preCacheIndexLeft and Right are indexes for the frames to start and end on
+            frameIndexLeft  = preCacheIndexLeft;
+            frameIndexRight = preCacheIndexRight;
+        }
+    }
+
+    // Make sure we don't fall of the front of the file
+    if (stillMainFrameNumber + frameIndexLeft <= 0)
+        frameIndexLeft = -(stillMainFrameNumber-1);
+
+    // ... or the back of the file
+    if (frameIndexRight > (maxFrameNumber - stillMainFrameNumber))
+        frameIndexRight = (maxFrameNumber - stillMainFrameNumber);
+
+#if CACHE_DEBUG
+    VERBOSE(VB_GENERAL, QString("Getting frames from %1 to %2 for level %3")
+            .arg(frameIndexLeft).arg(frameIndexRight).arg(newlevel));
+#endif
+    return true;
+}
+
+bool GridEditImages::getStillFrames(int maxcount)
+{
+    // returns true if no more frames to get
+
+    // This will fill in all the missing frames in the cache
+
+    long long i;
+    if (getFutureFramesFirst && frameIndexLeft < 0)
+    {
+        // If we're filling out the cache and the last move was to the right
+        // grab future frames first before any past frames
+        for (i = 1; i <= frameIndexRight; i++)
+            if (getSpecificFrame(i))
+                if (--maxcount == 0) return false;
+    }
+
+    // grab all appropriate frames
+
+    for (i = frameIndexLeft; i <= frameIndexRight; i++)
+        if (getSpecificFrame(i))
+            if (--maxcount == 0) return false;
+
+    return true;
+}
+
+bool GridEditImages::getSpecificFrame(long long i)
+{
+    // i is the index within the cache of frames
+
+    // If we're outside of the normal boundaries of the buffer,
+    // see if we've precached this frame
+    if (i < stillFrames.minIndex() || i > stillFrames.maxIndex())
+    {
+        // First extra cached frame
+        if (stillFrameCacheCount == 0)
+            stillFramesCacheBase = stillMainFrameNumber + i;
+
+        int tmpi;
+        for (tmpi = 0; tmpi < stillFrameCacheCount; tmpi++)
+        {
+            long long tmpframe = (stillFramesCacheBase + tmpi);
+            if (tmpframe == stillMainFrameNumber + i)
+                return false;
+        }
+
+        // Check for cache overflow
+        if (stillFrameCacheCount >= MAX_SUB_VIDEOS)
+        {
+            VERBOSE(VB_GENERAL, QString("Cached too many videos. Max = %1").arg(MAX_SUB_VIDEOS));
+            return false;
+        }
+        
+        tmpi = stillFrameCacheCount++;
+
+#if CACHE_DEBUG
+        VERBOSE(VB_GENERAL, QString("Caching frame %1, (frm# %2, index %3)")
+                            .arg(tmpi).arg(stillMainFrameNumber + i)
+                            .arg(i));
+#endif
+
+        getFrame(i, cutFramesCache[tmpi], stillFramesCache[tmpi], stillFramesBigCache[tmpi]);
+        return true;
+    }
+    else if (!stillFrames[i])
+    {
+        getFrame(i, cutFrames[i], stillFrames[i], stillFramesBig[i]);
+        return true;
+    }
+
+    return false;
+}
+
+void GridEditImages::getFrame(long long i,
+        int &cutFrame,
+        QPixmap * &stillFrame,
+        QPixmap * &stillFrameBig)
+{
+    // get this frame
+    long long targetFrame = stillMainFrameNumber + i;
+
+    if (!m_player->EditSeekToFrame(targetFrame))
+    {
+        VERBOSE(VB_GENERAL, QString("Error seeking to Frame[%1] (frame # %2)")
+                            .arg(i).arg(targetFrame));
+        checkMaxFrameCount();
+
+        stillFrameBig = new QPixmap(videoSizeMain);
+        stillFrameBig->fill(Qt::gray);
+
+        stillFrame = new QPixmap(videoSizeSmall);
+        stillFrame->fill(Qt::gray);
+    }
+    else
+    {
+        cutFrame = m_player->GetCutStatus(targetFrame);
+        QImage normal, small;
+        m_player->GetScreenGrabsOfCurrentFrame(normal, small);
+
+        stillFrameBig = makeScaledPixmap(normal, videoSizeMain);
+        stillFrame    = makeScaledPixmap(small, videoSizeSmall);
+
+#if FRAME_DEBUG
+        VERBOSE(VB_GENERAL, QString("stillFrames[%1] = %2 (%3)")
+                .arg(i)
+                .arg(targetFrame)
+                .arg(cutFrame));
+#endif
+    }
+}
+
+void GridEditImages::SetVideoInfo(int vcount, QSize sizeMain, QSize sizeSmall)
+{
+    usedSubVideoCount = vcount;
+    videoSizeMain = sizeMain;
+    videoSizeSmall = sizeSmall;
+    SetPreCache(1);
+
+    m_player->SetScreenGrabSizes(videoSizeMain, videoSizeSmall);
+
+    // start to grab the current images
+    getMainStillFrame();
+    startFrameCaching();
+}
+
+void GridEditImages::startFrameCaching()
+{
+    frameCacheLevel=0;
+    getFrameIndexes(frameCacheLevel);
+    
+    getImagesTimer->start(0);
+}
+
+void GridEditImages::SetPreCache(long long pccount)
+{
+    preCacheIndexLeft  = pccount - usedSubVideoCount;
+    preCacheIndexRight = pccount + usedSubVideoCount;
+}
+
+void GridEditImages::updateAllFrames()
+{
+    // getStillFrames() returns 'true' on the next call after it's gotten all requested frames
+
+    if (getStillFrames(1))
+    {
+        // If we've pre-cached the next screen of frames, tell the editor about it
+        if (frameCacheLevel == 1)
+            m_editor->cacheFramesAreReady();
+
+        frameCacheLevel++;
+        if (getFrameIndexes(frameCacheLevel))
+            getStillFrames(1);
+        else
+            stopFrameCaching();
+    }
+    m_editor->refreshImages();
+}
+
+void GridEditImages::clearStillFrames()
+{
+    int i;
+    for (i = stillFrames.minIndex(); i <= stillFrames.maxIndex(); i++)
+    {
+        if (stillFrames[i])
+        {
+            delete stillFrames[i];
+            stillFrames[i] = NULL;
+        }
+        if (stillFramesBig[i])
+        {
+            delete stillFramesBig[i];
+            stillFramesBig[i] = NULL;
+        }
+        cutFrames[i] = 0;
+    }
+}
+
+bool GridEditImages::shiftStillFramesLeft(long long offset)
+{
+    if (offset > 2 * stillFrames.maxIndex())
+    {
+        // Dump all cached data and re-get it
+        clearStillFrames();
+    }
+    else if (offset < 0)
+    {
+        VERBOSE(VB_IMPORTANT, QString("Offset (%1) < 0").arg(offset));
+        // Dump all cached data and re-get it
+        clearStillFrames();
+        offset = 0;
+    }
+    else if (offset != 0)
+    {
+        // Shift backwards in the stream by offset
+
+        // All frames will actually shift to the right.
+        // frame 'n' will become frame 'n+1'
+        // frame stillFrameMinus[1] will become mainframe
+        // frame stillFramePlus[max] will drop off
+
+        // shove extra frames into the excess space above usedSubVideos
+
+        if (offset >= stillMainFrameNumber)
+            offset = (stillMainFrameNumber-1);
+
+        //    printStillFrameStats("Before SL");
+        int i,j;
+        int minIndex = stillFrames.minIndex();
+        int maxIndex = stillFrames.maxIndex();
+        for (i = 0; i < offset; i++)
+        {
+            
+            if (stillFrames[maxIndex]) 
+            {
+                delete stillFrames[maxIndex];
+                delete stillFramesBig[maxIndex];
+            }
+
+            for (j = maxIndex; j > minIndex; j--) {
+                stillFrames[j]    = stillFrames[j-1];
+                stillFramesBig[j] = stillFramesBig[j-1];
+                cutFrames[j]      = cutFrames[j-1];
+            }
+
+             stillFrames[minIndex]    = NULL;
+             stillFramesBig[minIndex] = NULL;
+             cutFrames[minIndex]      = 0;
+        }
+
+        //   printStillFrameStats("After SL");
+
+    }
+
+    stillMainFrameNumber -= offset;
+    if (stillMainFrameNumber < 1)
+        stillMainFrameNumber = 1;
+
+    emptyCache();
+
+    return (stillFramesBig[0] != NULL);
+}
+
+bool GridEditImages::shiftStillFramesRight(long long offset)
+{
+    //VERBOSE(VB_GENERAL, QString("Offset =  %1").arg(offset));
+    if (offset > 2 * stillFrames.maxIndex())
+    {
+        // Dump all cached data and re-get it
+        clearStillFrames();
+    }
+    else if (offset < 0)
+    {
+        VERBOSE(VB_IMPORTANT, QString("Offset (%1) < 0").arg(offset));
+        // Dump all cached data and re-get it
+        clearStillFrames();
+        offset = 0;
+    }
+    else if (offset != 0)
+    {
+
+        // Shift forwards in the stream by offset
+
+        // All frames will actually shift to the left.
+        // frame 'n' will become frame 'n-1'
+        // frame stillFramePlus[1] will become mainframe
+        // frame stillFrameMinus[max] will drop off
+
+        // shove extra frames into the excess space above usedSubVideos
+
+        if (stillMainFrameNumber + offset > maxFrameNumber)
+        {
+            offset = (maxFrameNumber - stillMainFrameNumber);
+            VERBOSE(VB_GENERAL, QString("new Offset =  %1").arg(offset));
+        }
+        //printStillFrameStats("Before SR");
+
+        int i,j;
+        int minIndex = stillFrames.minIndex();
+        int maxIndex = stillFrames.maxIndex();
+
+        for (i = 0; i < offset; i++)
+        {
+            if (stillFrames[minIndex])
+            {
+                delete stillFrames[minIndex];
+                delete stillFramesBig[minIndex];
+            }
+
+            for (j = minIndex; j < maxIndex; j++) {
+                stillFrames[j]    = stillFrames[j+1];
+                stillFramesBig[j] = stillFramesBig[j+1];
+                cutFrames[j]      = cutFrames[j+1];
+            }
+
+             stillFrames[maxIndex]    = NULL;
+             stillFramesBig[maxIndex] = NULL;
+             cutFrames[maxIndex]      = 0;
+        }
+
+        //printStillFrameStats("After SR");
+
+    }
+    stillMainFrameNumber += offset;
+    if (stillMainFrameNumber > maxFrameNumber )
+        stillMainFrameNumber = maxFrameNumber;
+
+    emptyCache();
+
+    return (stillFramesBig[0] != NULL);
+}
+
+void GridEditImages::emptyCache()
+{
+#if CACHE_DEBUG
+    if (stillFrameCacheCount > 0)
+    {
+        long long minindex = (stillFramesCacheBase - stillMainFrameNumber);
+        long long maxindex = (stillFramesCacheBase+stillFrameCacheCount-1) - stillMainFrameNumber;
+        VERBOSE(VB_GENERAL, QString("emptying %1 frames (%2 - %3) into [%4] - [%5] ")
+                            .arg(stillFrameCacheCount)
+                            .arg(stillFramesCacheBase)
+                            .arg(stillFramesCacheBase+(stillFrameCacheCount-1))
+                            .arg(minindex).arg(maxindex));
+    }
+#endif
+    int i;
+    for (i = 0; i < stillFrameCacheCount; i++)
+    {
+        long long tmpframe = stillFramesCacheBase + i;
+        long long frameIndex = tmpframe - stillMainFrameNumber;
+
+        // frameIndex is the index matching stillFramesCache[i] to stillFrames[frameIndex]
+        // If frameIndex is within the stillFrames range, then use this frame
+        // otherwise delete it
+
+        if (frameIndex >= stillFrames.minIndex() && frameIndex <= stillFrames.maxIndex())
+        {
+            // move cache data into normal arrays.
+            if (stillFrames[frameIndex])
+            {
+                VERBOSE(VB_GENERAL, QString("Frame %1 index %2 already exists")
+                                    .arg(tmpframe).arg(frameIndex));
+                // Can't move it -- the destination exists
+                delete stillFramesCache[i];
+                delete stillFramesBigCache[i];
+            }
+            else
+            {
+                cutFrames[frameIndex] = cutFramesCache[i];
+                stillFrames[frameIndex] = stillFramesCache[i];
+                stillFramesBig[frameIndex] = stillFramesBigCache[i];
+            }
+        }
+        else
+        {
+            delete stillFramesCache[i];
+            delete stillFramesBigCache[i];
+        }
+        cutFramesCache[i]=0;
+        stillFramesCache[i] = NULL;
+        stillFramesBigCache[i] = NULL;
+    }
+    stillFrameCacheCount = 0;
+}
+
+void GridEditImages::printStillFrameStats(QString caption)
+{
+    int i;
+    // Debug info for frame cache
+    QString foundframes= caption + " Found Frames: ";
+
+    for (i = stillFrames.minIndex(); i <= stillFrames.maxIndex(); i++)
+        if (stillFrames[i])
+            foundframes += QString("%1 ").arg(i);
+
+    VERBOSE(VB_GENERAL, foundframes);
+}
+
+void GridEditImages::refreshCutList(UIGridEditImageType* gridimagemain,
+                                    myArray<UIGridEditImageType*, MAX_SUB_VIDEOS> &gridimages)
+{
+    int i;
+
+    for (i = stillFrames.minIndex(); i <= stillFrames.maxIndex(); i++)
+    {
+        if (stillFrames[i])
+        {
+            cutFrames[i] = m_player->GetCutStatus(stillMainFrameNumber+i);
+            if (gridimages[i])
+                gridimages[i]->setCutStatus(cutFrames[i]);
+        }
+    }
+    gridimagemain->setCutStatus(cutFrames[0]);
+}
+
+bool GridEditImages::refreshImages(UIGridEditImageType* gridimagemain,
+                                   myArray<UIGridEditImageType*, MAX_SUB_VIDEOS> &gridimages,
+                                   bool mainFrameOnly)
+{
+//    VERBOSE(VB_GENERAL, "Start");
+    bool alldone = true;
+    if (!stillFramesBig[0])
+        VERBOSE(VB_GENERAL, QString("Null Big Main frame %1").arg(stillMainFrameNumber));
+    gridimagemain->setPixmap(stillFramesBig[0],
+                             stillMainFrameNumber,
+                             cutFrames[0]);
+
+    if (mainFrameOnly && gridimages[0])
+        gridimages[0]->setPixmap(stillFrames[0],
+                                 stillMainFrameNumber,
+                                 cutFrames[0]);
+
+    if (!mainFrameOnly)
+    {
+        int i;
+        for (i = -usedSubVideoCount; i <= usedSubVideoCount; i++)
+        {
+            if (stillFrames[i] == NULL)
+                alldone = false;
+            if (gridimages[i])
+                gridimages[i]->setPixmap(stillFrames[i],
+                                         (stillMainFrameNumber + i),
+                                         cutFrames[i]);
+        }
+    }
+
+//    VERBOSE(VB_GENERAL, "Finish");
+    return alldone;
+}
+
+
+// Back up x frames
+void GridEditImages::SeekLeft(long long seekamount, bool cutpointseek)
+{
+    lastmovewasright = false;
+    stopFrameCaching();
+
+    if (cutpointseek)
+        seekamount = m_player->CalcCutPointSeek(stillMainFrameNumber, false);
+    
+    //VERBOSE(VB_GENERAL, QString("SeekLeft %1, cutpoint = %2").arg(seekamount).arg(cutpointseek));
+
+    if (cutpointseek)
+        SetPreCache(-1);
+    else
+        SetPreCache(-seekamount);
+
+    if (!shiftStillFramesLeft(seekamount))
+    {
+        //VERBOSE(VB_GENERAL, QString("shiftStillFramesLeft(%1) == false")
+        //                           .arg(seekamount));
+        // Need to grab the main frame
+
+        getMainStillFrame();
+    }
+
+    startFrameCaching();
+}
+
+void GridEditImages::SeekRight(long long seekamount, bool cutpointseek)
+{
+    lastmovewasright = true;
+    stopFrameCaching();
+
+    if (cutpointseek)
+        seekamount = m_player->CalcCutPointSeek(stillMainFrameNumber, true);
+
+    //VERBOSE(VB_GENERAL, QString("SeekRight %1, cutpoint = %2").arg(seekamount).arg(cutpointseek));
+
+    if (cutpointseek)
+        SetPreCache(1);
+    else
+        SetPreCache(seekamount);
+
+    if (!shiftStillFramesRight(seekamount))
+    {
+        //VERBOSE(VB_GENERAL, QString("shiftStillFramesLeft(%1) == false")
+        //                           .arg(seekamount));
+        // Need to grab the main frame
+
+        getMainStillFrame();
+    }
+
+    startFrameCaching();
+}
+
+void GridEditImages::checkMaxFrameCount()
+{
+    long long tfc = m_player->GetTotalFrameCount();
+    if (tfc != maxFrameNumberNVP)
+    {
+       VERBOSE(VB_GENERAL, QString("Updating: tfc %1, mfn %2, mfnNVP %3")
+            .arg(tfc).arg(maxFrameNumber).arg(maxFrameNumberNVP));
+        // Check to see if things changed
+        maxFrameNumber = tfc;
+        maxFrameNumberNVP = tfc;
+    }
+}
+
+FrameStats GridEditImages::GetMainFrameStats()
+{
+    FrameStats result;
+
+    result.frameNumber = stillMainFrameNumber;
+    result.cutInd  = cutFrames[0];
+    result.maxFrameNumber = maxFrameNumber;
+
+    return result;
+}
+
diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmythtv/grideditimages.h release.20115.0305gridedit/mythtv/libs/libmythtv/grideditimages.h
--- mythtv/libs/libmythtv/grideditimages.h	1969-12-31 18:00:00.000000000 -0600
+++ mythtv/libs/libmythtv/grideditimages.h	2009-03-12 13:59:27.000000000 -0500
@@ -0,0 +1,138 @@
+// -*- Mode: c++ -*-
+#ifndef GRIDEDITIMAGES_H_
+#define GRIDEDITIMAGES_H_
+
+#include <qstring.h>
+
+#include "libmyth/mythwidgets.h"
+
+using namespace std;
+
+class QTimer;
+class NuppelVideoPlayer;
+class GridEditCutpoints;
+
+#define MAX_SUB_VIDEOS 25
+
+// Simple class to allow array indexing from -MAX_SUB_VIDEOS to +MAX_SUB_VIDEOS
+template<class T, int COUNT> class myArray
+{
+    public:
+        myArray() { memset(_array, 0, sizeof(_array));};
+
+        T& operator[](int i) { return _array[COUNT+i]; };
+        int minIndex() const { return -COUNT; };
+        int maxIndex() const { return  COUNT; };
+
+    private:
+        T _array[2*COUNT+1];
+};
+
+class FrameStats
+{
+    public:
+        long long frameNumber;
+        int cutInd;
+        long long maxFrameNumber;
+};
+
+class MPUBLIC GridEditImages : public QObject
+{
+    Q_OBJECT
+
+    public:
+        GridEditImages(GridEditCutpoints *er, NuppelVideoPlayer *player);
+        ~GridEditImages();
+
+        void refreshCutList(UIGridEditImageType* gridimagemain, myArray<UIGridEditImageType*, MAX_SUB_VIDEOS> &gridimages);
+
+        // return true if anything changed
+        bool refreshImages(UIGridEditImageType* gridimagemain,
+                           myArray<UIGridEditImageType*, MAX_SUB_VIDEOS> &gridimages,
+                           bool mainFrameOnly);
+
+        void SeekLeft(long long seekamount, bool cutpointseek = false);
+        void SeekRight(long long seekamount, bool cutpointseek = false);
+
+        FrameStats GetMainFrameStats();
+        long long GetCurrentFrameNumber() const { return stillMainFrameNumber; }
+        long long GetMaxFrameNumber() const { return maxFrameNumber; }
+
+        void SetVideoInfo(int vcount, QSize sizeMain, QSize sizeSmall);
+
+    protected slots:
+        void updateAllFrames();
+
+    private:
+        // Private functions
+        void clearStillFrames();
+        void printStillFrameStats(QString caption);
+        void checkMaxFrameCount();
+
+        // 'newlevel' paramter for getFrameIndexes():
+        //  0 = get on screen Frames only
+        //  1 = get preCache Frames only
+        //  2 = get any necessary frames
+        //  3 = done
+        bool getFrameIndexes(int newlevel);
+
+        bool getStillFrames(int maxcount = 1000);
+        void getMainStillFrame();
+        bool getSpecificFrame(long long frameindex);
+        void getFrame(long long i, int &cutFrame, QPixmap* &stillFrame, QPixmap* &stillFrameBig);
+        void emptyCache();
+
+        // return true if anything changed
+        bool shiftStillFramesLeft(long long offset);
+        bool shiftStillFramesRight(long long offset);
+        void startFrameCaching();
+        void stopFrameCaching() { getImagesTimer->stop(); };
+
+        QPixmap *makeScaledPixmap(const QImage& qim, QSize sz);
+
+        void SetPreCache(long long pccount);
+
+        // Private data
+        // These frames are in the cutlist
+        // 0 == not cut
+        // 1 == cut
+        // 2 == cutpoint (cut left)
+        // 3 == cutpoint (cut right)
+        myArray<int, MAX_SUB_VIDEOS> cutFrames;
+        myArray<QPixmap *, MAX_SUB_VIDEOS> stillFrames;
+        myArray<QPixmap *, MAX_SUB_VIDEOS> stillFramesBig;
+
+        // Right and Left are for pre-caching large seek amounts
+        // (i.e. while seeking +5 seconds, they're places to store the previously displayed data)
+        // If there is any overlap, the main arrays hold the pointers
+
+        int       cutFramesCache[MAX_SUB_VIDEOS];
+        QPixmap  *stillFramesCache[MAX_SUB_VIDEOS];
+        QPixmap  *stillFramesBigCache[MAX_SUB_VIDEOS];
+        int       stillFrameCacheCount;
+        long long stillFramesCacheBase; // Frame # for index[0]
+
+        QSize     videoSizeMain;
+        QSize     videoSizeSmall;
+        int       usedSubVideoCount;
+        long long preCacheIndexLeft;
+        long long preCacheIndexRight;
+        long long frameIndexLeft;
+        long long frameIndexRight;
+        bool      lastmovewasright;
+        bool      getFutureFramesFirst;
+        int       frameCacheLevel;
+
+        long long stillMainFrameNumber; // frame number for big still picture
+        long long currentFrameNumberNVP; // frame number the NVP should be on
+        long long maxFrameNumber;       // max frame number override for NVP
+        long long maxFrameNumberNVP;    // Original NVP number
+
+        GridEditCutpoints *m_editor;
+        NuppelVideoPlayer *m_player;
+
+        QTimer            *getImagesTimer;
+
+};
+
+#endif
diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmythtv/libmythtv.pro release.20115.0305gridedit/mythtv/libs/libmythtv/libmythtv.pro
--- mythtv/libs/libmythtv/libmythtv.pro	2009-03-06 14:07:22.000000000 -0600
+++ mythtv/libs/libmythtv/libmythtv.pro	2009-03-11 20:34:21.000000000 -0500
@@ -319,8 +319,10 @@
     # Misc. frontend
     HEADERS += guidegrid.h              infostructs.h
     HEADERS += progfind.h               ttfont.h
+    HEADERS += grideditcutpoints.h      grideditimages.h
     SOURCES += guidegrid.cpp            infostructs.cpp
     SOURCES += progfind.cpp             ttfont.cpp
+    SOURCES += grideditcutpoints.cpp    grideditimages.cpp
 
     # DSMCC stuff
     HEADERS += dsmcc.h                  dsmcccache.h
diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmythtv/tv_play.cpp release.20115.0305gridedit/mythtv/libs/libmythtv/tv_play.cpp
--- mythtv/libs/libmythtv/tv_play.cpp	2009-03-06 14:07:22.000000000 -0600
+++ mythtv/libs/libmythtv/tv_play.cpp	2009-03-11 20:34:21.000000000 -0500
@@ -25,6 +25,7 @@
 #include "remoteencoder.h"
 #include "remoteutil.h"
 #include "guidegrid.h"
+#include "grideditcutpoints.h"
 #include "progfind.h"
 #include "NuppelVideoPlayer.h"
 #include "programinfo.h"
@@ -440,6 +441,8 @@
     REG_KEY("TV Editing", "BIGJUMPFWD", "Jump forward 10x the normal amount",
             ">,.");
     REG_KEY("TV Editing", "TOGGLEEDIT", "Exit out of Edit Mode", "E");
+    REG_KEY("TV Editing", "SLOWMO", "Slow Motion Play", "Ctrl+P");
+    REG_KEY("TV Editing", "PAUSE", "Pause", "P");
 
     /* Teletext keys */
     REG_KEY("Teletext Menu", "NEXTPAGE",    "Next Page",             "Down");
@@ -2606,6 +2609,8 @@
 
     if (editmode)
     {   
+        if (nvp->GetHideEdits())
+            return;   
         if (!nvp->DoKeypress(e))
             editmode = nvp->GetEditMode();
         if (!editmode)
@@ -5992,6 +5997,19 @@
     qApp->postEvent(myWindow, me);
 }
 
+void TV::ShowEditRecordingGrid()
+{
+    // post the request to the main UI thread
+    // it will be caught in eventFilter and processed as CustomEvent
+    // this will create the program guide window (widget)
+    // on the main thread and avoid a deadlock on Win32
+
+    VERBOSE(VB_GENERAL, "Starting Grid Edit");
+    QString message = QString("START_EDIT");
+    MythEvent* me = new MythEvent(message);
+    qApp->postEvent(myWindow, me);
+}
+
 void TV::ChangeVolume(bool up)
 {
     AudioOutput *aud = nvp->getAudioOutput();
@@ -6525,6 +6543,10 @@
             int editType = tokens[1].toInt();
             doEditSchedule(editType);
         }
+        else if (message.left(10) == "START_EDIT")
+        {
+            nvp->ShowEditRecordingGrid();
+        }
 
         pbinfoLock.lock();
         if (playbackinfo && message.left(14) == "COMMFLAG_START")
diff -r -u -N -X diff.exclude.noxml -x release.20115.0305base -x release.20115.0305gridedit release.20115.0305base/mythtv/libs/libmythtv/tv_play.h release.20115.0305gridedit/mythtv/libs/libmythtv/tv_play.h
--- mythtv/libs/libmythtv/tv_play.h	2009-03-06 14:07:22.000000000 -0600
+++ mythtv/libs/libmythtv/tv_play.h	2009-03-11 20:34:21.000000000 -0500
@@ -158,6 +158,7 @@
     void setUnderNetworkControl(bool setting) { underNetworkControl = setting; }
     bool IsSameProgram(ProgramInfo *p);
 
+    void ShowEditRecordingGrid();
     void ShowNoRecorderDialog(void);
     void FinishRecording(void);
     void AskAllowRecording(const QStringList&, int, bool, bool);
