Ticket #5606: mhegupdate.patch

File mhegupdate.patch, 55.7 KB (added by David Matthews <dm@…>, 11 years ago)
  • libs/libmythtv/mhi.h

     
    182182
    183183    uint             m_lastNbiVersion;
    184184    Q3MemArray<unsigned char> m_nbiData;
     185
     186    QRect            m_videoRect;
    185187};
    186188
    187189// Object for drawing text.
  • libs/libmythtv/mhi.cpp

     
    3737      m_face_loaded(false), m_currentChannel(-1),
    3838      m_isLive(false),      m_currentCard(0),
    3939      m_audioTag(-1),       m_videoTag(-1),
    40       m_tuningTo(-1),       m_lastNbiVersion(NBI_VERSION_UNSET)
     40      m_tuningTo(-1),       m_lastNbiVersion(NBI_VERSION_UNSET),
     41      m_videoRect(0, 0, StdDisplayWidth, StdDisplayHeight)
    4142{
    4243    m_display.setAutoDelete(true);
    4344    m_dsmccQueue.setAutoDelete(true);
     
    418419{
    419420    m_displayWidth = display.width();
    420421    m_displayHeight = display.height();
     422    m_videoRect = display; // Assume full screen at this stage.
    421423}
    422424
    423425void MHIContext::SetInputRegister(int num)
     
    468470void MHIContext::AddToDisplay(const QImage &image, int x, int y)
    469471{
    470472    MHIImageData *data = new MHIImageData;
    471     data->m_image = image;
     473    // It seems that OSDTypeImage::Load doesn't deal well with images
     474    // located on odd pixel boundaries and the resulting display contains
     475    // transparent lines.  To avoid this we create a new image if either
     476    // the x or y offset would be odd and set the extra pixels to transparent.
     477    QImage img = image;
     478    int xboundary = x & 1;
     479    int yboundary = y & 1;
     480   
     481    if (xboundary || yboundary)
     482    {
     483        int width = img.width(), height = img.height();
     484        if (xboundary)
     485        {
     486            width++;
     487            x--;
     488        }
     489        if (yboundary)
     490        {
     491            height++;
     492            y--;
     493        }
     494        img = QImage(width, height, QImage::Format_ARGB32);
     495        QRgb qTransparent = qRgba(0,0,0,0);
     496        if (xboundary)
     497        {
     498            for (int i = 0; i < height; i++)
     499                img.setPixel(0, i, qTransparent);
     500        }
     501
     502        if (yboundary)
     503        {
     504            for (int j = 0; j < width; j++)
     505                img.setPixel(j, 0, qTransparent);
     506        }
     507
     508        for (int i = 0; i < height-yboundary; i++)
     509        {
     510            for (int j = 0; j < width-xboundary; j++)
     511            {
     512                img.setPixel(j+xboundary, i+yboundary, image.pixel(j,i));
     513            }
     514        }
     515    }
     516    data->m_image = img;
    472517    data->m_x = x;
    473518    data->m_y = y;
    474519    QMutexLocker locker(&m_display_lock);
     
    486531{
    487532    // tell the video player to resize the video stream
    488533    if (m_parent->GetNVP())
    489         m_parent->GetNVP()->SetVideoResize(videoRect);
     534    {
     535        QRect vidRect(videoRect.x() * m_displayWidth/StdDisplayWidth,
     536                      videoRect.y() * m_displayHeight/StdDisplayHeight,
     537                      videoRect.width() * m_displayWidth/StdDisplayWidth,
     538                      videoRect.height() * m_displayHeight/StdDisplayHeight);
     539        if (m_videoRect != vidRect)
     540        {
     541            m_parent->GetNVP()->SetVideoResize(vidRect);
     542            m_videoRect = vidRect;
     543        }
     544    }
    490545
    491546    QMutexLocker locker(&m_display_lock);
    492547    QRect displayRect(dispRect.x() * m_displayWidth/StdDisplayWidth,
     
    729784    QRgb qColour = qRgba(colour.red(), colour.green(),
    730785                         colour.blue(), colour.alpha());
    731786
    732     // This is a bit of a mess: we should be able to create a rectangle object.
    733     // Scale the image to the current display size
    734787    int scaledWidth = width * GetWidth() / MHIContext::StdDisplayWidth;
    735788    int scaledHeight = height * GetHeight() / MHIContext::StdDisplayHeight;
    736     QImage qImage(scaledWidth, scaledHeight, 32);
    737     qImage.setAlphaBuffer(true);
     789    QImage qImage(scaledWidth, scaledHeight, QImage::Format_ARGB32);
    738790
    739     // As far as I can tell this is the only way to draw with an
    740     // intermediate transparency.
    741791    for (int i = 0; i < scaledHeight; i++)
    742792    {
    743793        for (int j = 0; j < scaledWidth; j++)
     
    921971// different layers.  The background is drawn separately as a rectangle.
    922972void MHIText::Clear(void)
    923973{
    924     m_image = QImage(m_width, m_height, 32);
    925     //
    926     m_image.setAlphaBuffer(true);
     974    m_image = QImage(m_width, m_height, QImage::Format_ARGB32);
    927975    // QImage::fill doesn't set the alpha buffer.
    928976    for (int i = 0; i < m_height; i++)
    929977    {
     
    10621110        m_image = QImage();
    10631111        return;
    10641112    }
    1065     m_image = QImage(m_width, m_height, 32);
     1113    m_image = QImage(m_width, m_height, QImage::Format_ARGB32);
    10661114    // Fill the image with "transparent colour".
    10671115    DrawRect(0, 0, m_width, m_height, MHRgba(0, 0, 0, 0));
    10681116}
     
    12741322// The UK profile says that MHEG should not contain concave or
    12751323// self-crossing polygons but we can get the former at least as
    12761324// a result of rounding when drawing ellipses.
     1325typedef struct { int yBottom, yTop, xBottom; float slope; } lineSeg;
     1326
    12771327void MHIDLA::DrawPoly(bool isFilled, const Q3PointArray &points)
    12781328{
    12791329    int nPoints = points.size();
     
    12821332
    12831333    if (isFilled)
    12841334    {
    1285         // Polygon filling is done by sketching the outline of
    1286         // the polygon in a separate bitmap and then raster scanning
    1287         // across this to generate the fill.  There are some special
    1288         // cases that have to be considered when doing this.  Maximum
    1289         // and minimum points have to be removed otherwise they will
    1290         // turn the scan on but not off again.  Horizontal lines are
    1291         // suppressed and their ends handled specially.
    1292         QRect bounds = points.boundingRect();
    1293         int width = bounds.width()+1, height = bounds.height()+1;
    1294         QBitArray boundsMap(width*height);
    1295         boundsMap.fill(0);
    1296         // Draw the boundaries in the bounds map.  This is
    1297         // the Bresenham algorithm if the absolute gradient is
    1298         // greater than 1 but puts only the centre of each line
    1299         // (so there is only one point for each y value) if less.
    1300         QPoint last = points[nPoints-1]; // Last point
    1301         for (int i = 0; i < nPoints; i++)
     1335        Q3MemArray <lineSeg> lineArray(nPoints);
     1336        int nLines = 0;
     1337        // Initialise the line segment array.  Include all lines
     1338        // apart from horizontal.  Close the polygon by starting
     1339        // with the last point in the array.
     1340        int lastX = points[nPoints-1].x(); // Last point
     1341        int lastY = points[nPoints-1].y();
     1342        int yMin = lastY, yMax = lastY;
     1343        for (int k = 0; k < nPoints; k++)
    13021344        {
    1303             QPoint thisPoint = points[i];
    1304             int x1 = last.x() - bounds.x();
    1305             int y1 = last.y() - bounds.y();
    1306             int x2 = thisPoint.x() - bounds.x();
    1307             int y2 = thisPoint.y() - bounds.y();
    1308             int x, xEnd, y, yEnd;
    1309             if (y2 > y1)
     1345            int thisX = points[k].x();
     1346            int thisY = points[k].y();
     1347            if (lastY != thisY)
    13101348            {
    1311                 x = x1;
    1312                 y = y1;
    1313                 xEnd = x2;
    1314                 yEnd = y2;
    1315             }
    1316             else
    1317             {
    1318                 x = x2;
    1319                 y = y2;
    1320                 xEnd = x1;
    1321                 yEnd = y1;
    1322             }
    1323             int dx = abs(xEnd-x), dy = yEnd-y;
    1324             int xStep = xEnd >= x ? 1 : -1;
    1325             if (abs(y2-y1) > abs(x2-x1))
    1326             {
    1327                 int error = dy/2;
    1328                 y++;
    1329                 for (; y < yEnd; y++) // Exclude endpoints
     1349                if (lastY > thisY)
    13301350                {
    1331                     boundsMap.toggleBit(x+y*width);
    1332                     error += dx;
    1333                     if (error*2 > dy)
    1334                     {
    1335                         error -= dy;
    1336                         x += xStep;
    1337                     }
     1351                    lineArray[nLines].yBottom = thisY;
     1352                    lineArray[nLines].yTop = lastY;
     1353                    lineArray[nLines].xBottom = thisX;
    13381354                }
    1339             }
    1340             else
    1341             {
    1342                 int error = 0;
    1343                 y++;
    1344                 for (; y < yEnd; y++)
     1355                else
    13451356                {
    1346                     boundsMap.toggleBit(x+y*width);
    1347                     error += dx;
    1348                     while (error > dy)
    1349                     {
    1350                         x += xStep;
    1351                         error -= dy;
    1352                     }
     1357                    lineArray[nLines].yBottom = lastY;
     1358                    lineArray[nLines].yTop = thisY;
     1359                    lineArray[nLines].xBottom = lastX;
    13531360                }
     1361                lineArray[nLines++].slope =
     1362                    (float)(thisX-lastX) / (float)(thisY-lastY);
    13541363            }
    1355             QPoint nextPoint = points[(i+1) % nPoints];
    1356             int nextY = nextPoint.y() - bounds.y();
    1357             int turn = (y2 - y1) * (nextY - y2);
    1358             if (turn > 0) // Not a max or min
    1359                 boundsMap.toggleBit(x2+y2*width);
    1360             else if (turn == 0) // Previous or next line is horizontal
    1361             {
    1362                 // We only draw a point at the beginning or end of a horizontal
    1363                 // line if it turns clockwise.  This means that the fill
    1364                 // will be different depending on the direction the polygon was
    1365                 // drawn but that will be tidied up when we draw the lines round.
    1366                 if (y1 == y2)
    1367                 {
    1368                     if ((x2-x1) * (nextY - y2) > 0)
    1369                        boundsMap.toggleBit(x2+y2*width);
    1370                 }
    1371                 else if ((nextPoint.x() - bounds.x() - x2) * (y2 - y1) < 0)
    1372                     // Next line is horizontal -  draw point if turn is clockwise.
    1373                     boundsMap.toggleBit(x2+y2*width);
    1374             }
    1375             last = thisPoint;
     1364            if (thisY < yMin)
     1365                yMin = thisY;
     1366            if (thisY > yMax)
     1367                yMax = thisY;
     1368            lastX = thisX;
     1369            lastY = thisY;
    13761370        }
     1371       
     1372        // Find the intersections of each line in the line segment array
     1373        // with the scan line.  Because UK MHEG says that figures should be
     1374        // convex we only need to consider two intersections.
    13771375        QRgb fillColour = qRgba(m_fillColour.red(), m_fillColour.green(),
    13781376                                m_fillColour.blue(), m_fillColour.alpha());
    1379         // Now scan the bounds map and use this to fill the polygon.
    1380         for (int j = 0; j < bounds.height(); j++)
     1377        for (int y = yMin; y < yMax; y++)
    13811378        {
    1382             bool penDown = false;
    1383             for (int k = 0; k < bounds.width(); k++)
     1379            int crossings = 0, xMin = 0, xMax = 0;
     1380            for (int l = 0; l < nLines; l++)
    13841381            {
    1385                 if (boundsMap.testBit(k+j*width))
    1386                     penDown = ! penDown;
    1387                 else if (penDown && k+bounds.x() >= 0 && j+bounds.y() >= 0 &&
    1388                          k+bounds.x() < m_width && j+bounds.y() < m_height)
    1389                     m_image.setPixel(k+bounds.x(), j+bounds.y(), fillColour);
     1382                if (y >= lineArray[l].yBottom && y < lineArray[l].yTop)
     1383                {
     1384                    int x = (int)round((float)(y - lineArray[l].yBottom) *
     1385                        lineArray[l].slope) + lineArray[l].xBottom;
     1386                    if (crossings == 0 || x < xMin)
     1387                        xMin = x;
     1388                    if (crossings == 0 || x > xMax)
     1389                        xMax = x;
     1390                    crossings++;
     1391                }
    13901392            }
     1393            if (crossings == 2)
     1394            {
     1395                for (int x = xMin; x <= xMax; x++)
     1396                    m_image.setPixel(x, y, fillColour);
     1397            }
    13911398        }
    13921399
    13931400        // Draw the boundary
    1394         last = points[nPoints-1]; // Last point
     1401        QPoint last = points[nPoints-1]; // Last point
    13951402        for (int i = 0; i < nPoints; i++)
    13961403        {
    13971404            DrawLine(points[i].x(), points[i].y(), last.x(), last.y());
     
    15021509    {
    15031510        int nContentWidth = c->width;
    15041511        int nContentHeight = c->height;
    1505         m_image = QImage(nContentWidth, nContentHeight, 32);
     1512        m_image = QImage(nContentWidth, nContentHeight, QImage::Format_ARGB32);
    15061513        m_opaque = true; // MPEG images are always opaque.
    15071514
    15081515        AVPicture retbuf;
     
    15591566    m_image = m_image.scaled(newWidth, newHeight,
    15601567            Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    15611568}
     1569
     1570
  • libs/libmythfreemheg/Actions.cpp

     
    11/* Actions.cpp
    22
    3    Copyright (C)  David C. J. Matthews 2004  dm at prolingua.co.uk
     3   Copyright (C)  David C. J. Matthews 2004, 2008  dm at prolingua.co.uk
    44
    55   This program is free software; you can redistribute it and/or
    66   modify it under the terms of the GNU General Public License
     
    9393        case C_GET_ENTRY_POINT: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break; // EntryField
    9494        case C_GET_FILL_COLOUR: pAction = new MHGetFillColour; break;
    9595        case C_GET_FIRST_ITEM: pAction = new MHGetFirstItem; break;
    96         case C_GET_HIGHLIGHT_STATUS: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break;// ?
    97         case C_GET_INTERACTION_STATUS: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break;// ?
     96        case C_GET_HIGHLIGHT_STATUS: pAction = new MHGetHighlightStatus; break;
     97        case C_GET_INTERACTION_STATUS: pAction = new MHGetInteractionStatus; break;
    9898        case C_GET_ITEM_STATUS: pAction = new MHGetItemStatus; break;
    9999        case C_GET_LABEL: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break;// PushButton
    100100        case C_GET_LAST_ANCHOR_FIRED: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break;// HyperText
     
    104104        case C_GET_LIST_ITEM: pAction = new MHGetListItem; break;
    105105        case C_GET_LIST_SIZE: pAction = new MHGetListSize; break;
    106106        case C_GET_OVERWRITE_MODE: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break;// ?
    107         case C_GET_PORTION: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break;// Slider
     107        case C_GET_PORTION: pAction = new MHGetPortion; break;
    108108        case C_GET_POSITION: pAction = new MHGetPosition; break;
    109109        case C_GET_RUNNING_STATUS: pAction = new MHGetRunningStatus; break;
    110110        case C_GET_SELECTION_STATUS: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break;// ?
    111         case C_GET_SLIDER_VALUE: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break;// Slider
     111        case C_GET_SLIDER_VALUE: pAction = new MHGetSliderValue; break;
    112112        case C_GET_TEXT_CONTENT: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break;// Text
    113113        case C_GET_TEXT_DATA: pAction = new MHGetTextData; break;
    114114        case C_GET_TOKEN_POSITION: pAction = new MHGetTokenPosition; break;
     
    145145        case C_SET_FILL_COLOUR: pAction = new MHSetFillColour; break;
    146146        case C_SET_FIRST_ITEM: pAction = new MHSetFirstItem; break;
    147147        case C_SET_FONT_REF: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break; // Text
    148         case C_SET_HIGHLIGHT_STATUS: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break; // ?
    149         case C_SET_INTERACTION_STATUS: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break; // ?
     148        case C_SET_HIGHLIGHT_STATUS: pAction = new MHSetHighlightStatus; break;
     149        case C_SET_INTERACTION_STATUS: pAction = new MHSetInteractionStatus; break;
    150150        case C_SET_LABEL: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break; // PushButton
    151151        case C_SET_LINE_COLOUR: pAction = new MHSetLineColour; break;
    152152        case C_SET_LINE_STYLE: pAction = new MHSetLineStyle; break;
    153153        case C_SET_LINE_WIDTH: pAction = new MHSetLineWidth; break;
    154154        case C_SET_OVERWRITE_MODE: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break; // EntryField
    155155        case C_SET_PALETTE_REF: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break; // Visible
    156         case C_SET_PORTION: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break; // Slider
     156        case C_SET_PORTION: pAction = new MHSetPortion; break;
    157157        case C_SET_POSITION: pAction = new MHSetPosition; break;
    158         case C_SET_SLIDER_VALUE: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break; // Slider
     158        case C_SET_SLIDER_VALUE: pAction = new MHSetSliderValue; break;
    159159        case C_SET_SPEED: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break; // ?
    160160        case C_SET_TIMER: pAction = new MHSetTimer; break;
    161161        case C_SET_TRANSPARENCY: pAction = new MHSetTransparency; break;
    162162        case C_SET_VARIABLE: pAction = new MHSetVariable; break;
    163163        case C_SET_VOLUME: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break; // ?
    164164        case C_SPAWN: pAction = new MHSpawn; break;
    165         case C_STEP: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break; // Slider
     165        case C_STEP: pAction = new MHStep; break;
    166166        case C_STOP: pAction = new MHStop; break;
    167167        case C_STORE_PERSISTENT: pAction = new MHPersistent(":StorePersistent", false); break;
    168168        case C_SUBTRACT: pAction = new MHSubtract; break;
     
    184184        case C_SET_FOCUS_POSITION: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break; // HyperText
    185185        case C_SET_BITMAP_DECODE_OFFSET: pAction = new MHSetBitmapDecodeOffset; break;
    186186        case C_GET_BITMAP_DECODE_OFFSET: pAction = new MHGetBitmapDecodeOffset; break;
    187         case C_SET_SLIDER_PARAMETERS: pAction = new MHUnimplementedAction(pElemAction->GetTagNo()); break; // ?
     187        case C_SET_SLIDER_PARAMETERS: pAction = new MHSetSliderParameters; break;
    188188
    189189        default:
    190190            MHLOG(MHLogWarning, QString("Unknown action %1").arg(pElemAction->GetTagNo()));
  • libs/libmythfreemheg/Engine.cpp

     
    11/* Engine.cpp
    22
    3    Copyright (C)  David C. J. Matthews 2004  dm at prolingua.co.uk
     3   Copyright (C)  David C. J. Matthews 2004, 2008  dm at prolingua.co.uk
    44
    55   This program is free software; you can redistribute it and/or
    66   modify it under the terms of the GNU General Public License
     
    3131#include "ASN1Codes.h"
    3232#include "Logging.h"
    3333#include "freemheg.h"
     34#include "Visible.h"  // For MHInteractible
    3435
    3536#include <stdio.h>
    3637#include <stdlib.h>
     
    4647{
    4748    m_fInTransition = false;
    4849    m_fBooting = true;
     50    m_Interacting = 0;
    4951}
    5052
    5153MHEngine::~MHEngine()
     
    323325        pApp->m_pCurrentScene = NULL;
    324326    }
    325327
     328    m_Interacting = 0;
    326329
    327330    // Switch to the new scene.
    328331    CurrentApp()->m_pCurrentScene = (MHScene*) pProgram;
     
    620623{
    621624    MHScene *pScene = CurrentScene();
    622625    if (! pScene) return;
    623     EventTriggered(pScene, EventUserInput, nCode);
     626    // Various keys generate engine events as well as user events.
     627    // These are generated before the user events and even if there
     628    // is an interactible.
     629    switch (nCode)
     630    {
     631    case 104:
     632    case 105: // Text key
     633        EventTriggered(pScene, EventEngineEvent, 4);
     634        break;
     635    case 16: // Text Exit/Cancel key
     636    case 100: // Red
     637    case 101: // Green
     638    case 102: // Yellow
     639    case 103: // Blue
     640        EventTriggered(pScene, EventEngineEvent, nCode);
     641        break;
     642    }
     643
     644    // If we are interacting with an interactible send the key
     645    // there otherwise generate a user event.
     646    if (m_Interacting)
     647        m_Interacting->KeyEvent(this, nCode);
     648    else EventTriggered(pScene, EventUserInput, nCode);
    624649}
    625650
    626651// Called by an ingredient wanting external content.
  • libs/libmythfreemheg/ParseText.cpp

     
    11/* ParseText.cpp
    22
    3    Copyright (C)  David C. J. Matthews 2004  dm at prolingua.co.uk
     3   Copyright (C)  David C. J. Matthews 2004, 2008  dm at prolingua.co.uk
    44
    55   This program is free software; you can redistribute it and/or
    66   modify it under the terms of the GNU General Public License
     
    536536                if (m_nInt > 0) return;
    537537                m_nInt = MHText::GetStartCorner(buff);
    538538                if (m_nInt > 0) return;
     539                m_nInt = MHSlider::GetOrientation(buff);
     540                if (m_nInt > 0) return;
     541                m_nInt = MHSlider::GetStyle(buff);
     542                if (m_nInt > 0) return;
    539543
    540544                // Check the colour table.  If it's there generate a string containing the colour info.
    541545                for (int i = 0; i < (int)(sizeof(colourTable)/sizeof(colourTable[0])); i++) {
  • libs/libmythfreemheg/Text.cpp

     
    11/* Text.cpp
    22
    3    Copyright (C)  David C. J. Matthews 2004  dm at prolingua.co.uk
     3   Copyright (C)  David C. J. Matthews 2004, 2008  dm at prolingua.co.uk
    44
    55   This program is free software; you can redistribute it and/or
    66   modify it under the terms of the GNU General Public License
     
    465465                    pNewLine->m_Items.Append(pNewItem);
    466466                    pNewItem->m_Unicode = pItem->m_Unicode.mid(nNewStart, nNewWidth);
    467467                    pNewItem->m_nUnicode = nNewWidth;
     468                    // Move any remaining items, e.g. in a different colour, from this line onto the new line.
     469                    while (pLine->m_Items.Size() > j+1) {
     470                        pNewLine->m_Items.Append(pLine->m_Items.GetAt(j+1));
     471                        pLine->m_Items.RemoveAt(j+1);
     472                    }
    468473                }
    469474                // Remove any spaces at the end of the old section.  If we don't do that and
    470475                // we are centering or right aligning the text we'll get it wrong.
     
    536541}
    537542
    538543
    539 MHHyperText::MHHyperText()
     544MHHyperText::MHHyperText(): MHInteractible(this)
    540545{
    541546
    542547}
  • libs/libmythfreemheg/Root.h

     
    176176    virtual void SetVideoDecodeOffset(int /*newXOffset*/, int /*newYOffset*/, MHEngine *) { InvalidAction("SetVideoDecodeOffset"); }
    177177    virtual void GetVideoDecodeOffset(MHRoot * /*pXOffset*/, MHRoot */*pYOffset*/, MHEngine *) { InvalidAction("GetVideoDecodeOffset"); }
    178178
     179    // Actions on Interactibles.
     180    virtual void SetInteractionStatus(bool /*newStatus*/, MHEngine *) { InvalidAction("SetInteractionStatus"); }
     181    virtual bool GetInteractionStatus(void) { InvalidAction("GetInteractionStatus"); return false; }
     182    virtual void SetHighlightStatus(bool /*newStatus*/, MHEngine *engine) { InvalidAction("SetHighlightStatus"); }
     183    virtual bool GetHighlightStatus(void) { InvalidAction("GetHighlightStatus"); return false; }
     184
     185    // Actions on Sliders.
     186    virtual void Step(int /*nbSteps*/, MHEngine */*engine*/) { InvalidAction("Step"); }
     187    virtual void SetSliderValue(int /*nbSteps*/, MHEngine */*engine*/) { InvalidAction("SetSliderValue"); }
     188    virtual int GetSliderValue(void) { InvalidAction("GetSliderValue"); return 0; }
     189    virtual void SetPortion(int /*newPortion*/, MHEngine */*engine*/) { InvalidAction("SetPortion"); }
     190    virtual int GetPortion(void) { InvalidAction("GetPortion"); return 0; }
     191    // Additional action defined in UK MHEG.
     192    virtual void SetSliderParameters(int /*newMin*/, int /*newMax*/, int /*newStep*/, MHEngine */*engine*/)
     193         { InvalidAction("SetSliderParameters"); }
     194
    179195protected:
    180196
    181197    void InvalidAction(const char *actionName);
  • libs/libmythfreemheg/BaseActions.h

     
    11/* BaseActions.h
    22
    3    Copyright (C)  David C. J. Matthews 2004  dm at prolingua.co.uk
     3   Copyright (C)  David C. J. Matthews 2004, 2008  dm at prolingua.co.uk
    44
    55   This program is free software; you can redistribute it and/or
    66   modify it under the terms of the GNU General Public License
     
    7373    MHGenericInteger m_Argument1, m_Argument2;
    7474};
    7575
     76// Base class for actions with three integers.  Used for SetSliderParameters
     77class MHActionInt3: public MHElemAction
     78{
     79public:
     80    MHActionInt3(const char *name): MHElemAction(name) {}
     81    virtual void Initialise(MHParseNode *p, MHEngine *engine);
     82    virtual void PrintArgs(FILE *fd, int nTabs) const;
     83    virtual void Perform(MHEngine *engine);
     84    virtual void CallAction(MHEngine *engine, MHRoot *pTarget, int nArg1, int nArg2, int nArg3) = 0;
     85protected:
     86    MHGenericInteger m_Argument1, m_Argument2, m_Argument3;
     87};
     88
    7689// Base class for actions with four integers.  Used in the DynamicLineArt class
    7790class MHActionInt4: public MHElemAction
    7891{
     
    139152};
    140153
    141154
     155// Base class for actions with a single boolean argument.
     156class MHActionBool: public MHElemAction
     157{
     158public:
     159    MHActionBool(const char *name): MHElemAction(name) {}
     160    virtual void Initialise(MHParseNode *p, MHEngine *engine);
     161    virtual void PrintArgs(FILE *fd, int) const { m_Argument.PrintMe(fd, 0); }
     162    virtual void Perform(MHEngine *engine);
     163    virtual void CallAction(MHEngine *engine, MHRoot *pTarget, bool fArg) = 0;
     164protected:
     165    MHGenericBoolean m_Argument;
     166};
     167
    142168#endif
  • libs/libmythfreemheg/Visible.h

     
    11/* Visible.h
    22
    3    Copyright (C)  David C. J. Matthews 2004  dm at prolingua.co.uk
     3   Copyright (C)  David C. J. Matthews 2004, 2008  dm at prolingua.co.uk
    44
    55   This program is free software; you can redistribute it and/or
    66   modify it under the terms of the GNU General Public License
     
    124124    virtual MHIngredient *Clone(MHEngine *) { return new MHRectangle(*this); } // Create a clone of this ingredient.
    125125};
    126126
     127// The Interactible class is described as a "mix-in" class.  It is used
     128// in various classes which complicates inheritance.
    127129class MHInteractible
    128130{
    129131public:
    130     MHInteractible();
     132    MHInteractible(MHVisible *parent);
    131133    virtual ~MHInteractible();
    132     virtual void Initialise(MHParseNode *p, MHEngine *engine);
    133     virtual void PrintMe(FILE *fd, int nTabs) const;
     134    void Initialise(MHParseNode *p, MHEngine *engine);
     135    void PrintMe(FILE *fd, int nTabs) const;
     136
     137    virtual void Interaction(MHEngine *engine);
     138
     139    // This is called whenever a key is pressed while this
     140    // interactible is set to interactive.
     141    virtual void KeyEvent(MHEngine */*engine*/, int /*nCode*/) {}
     142    virtual void InteractionCompleted(MHEngine */*engine*/) {}
     143
     144    void InteractSetInteractionStatus(bool newStatus, MHEngine *engine);
     145    bool InteractGetInteractionStatus(void) { return m_fInteractionStatus; }
     146    void InteractSetHighlightStatus(bool newStatus, MHEngine *engine);
     147    bool InteractGetHighlightStatus(void) { return m_fHighlightStatus; }
     148    // InteractDeactivation should be applied in every Deactivation action
     149    // of derived classes.
     150    void InteractDeactivation(void) { m_fInteractionStatus = false; }
     151
     152protected:
     153    // Exchanged attributes
     154    bool     m_fEngineResp;
     155    MHColour m_highlightRefColour;
     156    // Internal attributes
     157    bool     m_fHighlightStatus;
     158    bool     m_fInteractionStatus;
     159
     160private:
     161    MHVisible *m_parent;
    134162};
    135163
    136164class MHSlider : public MHVisible, public MHInteractible
     
    141169    virtual const char *ClassName() { return "Slider"; }
    142170    virtual void Initialise(MHParseNode *p, MHEngine *engine);
    143171    virtual void PrintMe(FILE *fd, int nTabs) const;
    144     virtual void Display(MHEngine *) {} // Not (yet?) supported
     172    virtual void Display(MHEngine *);
     173    virtual void Preparation(MHEngine *engine);
     174
     175    virtual void Interaction(MHEngine *engine);
     176    virtual void InteractionCompleted(MHEngine *engine);
     177    virtual void KeyEvent(MHEngine *engine, int nCode);
     178
     179    // Implement the actions in the main inheritance line.
     180    virtual void SetInteractionStatus(bool newStatus, MHEngine *engine)
     181    { InteractSetInteractionStatus(newStatus, engine); }
     182    virtual bool GetInteractionStatus(void) { return InteractGetInteractionStatus(); }
     183    virtual void SetHighlightStatus(bool newStatus, MHEngine *engine)
     184    { InteractSetHighlightStatus(newStatus, engine); }
     185    virtual bool GetHighlightStatus(void) { return InteractGetHighlightStatus(); }
     186    virtual void Deactivation(MHEngine *engine) { InteractDeactivation(); }
     187
     188    // Actions
     189    virtual void Step(int nbSteps, MHEngine *engine);
     190    virtual void SetSliderValue(int newValue, MHEngine *engine);
     191    virtual int GetSliderValue(void) { return slider_value; }
     192    virtual void SetPortion(int newPortion, MHEngine *engine);
     193    virtual int GetPortion(void) { return portion; }
     194    // Additional action defined in UK MHEG.
     195    virtual void SetSliderParameters(int newMin, int newMax, int newStep, MHEngine *engine);
     196
     197    // Enumerated type lookup functions for the text parser.
     198    static int GetOrientation(const char *str);
     199    static int GetStyle(const char *str);
     200protected:
     201    void Increment(MHEngine *engine);
     202    void Decrement(MHEngine *engine);
     203
     204    // Exchanged attributes
     205    // Orientation and direction of increasing value.
     206    enum SliderOrientation { SliderLeft = 1, SliderRight, SliderUp, SliderDown }
     207        m_orientation;
     208    int initial_value, initial_portion;
     209    int orig_max_value, orig_min_value, orig_step_size;
     210    // Style of slider.  Normal represents a mark on a scale,
     211    // Thermometer a range from the start up to the mark and Proportional
     212    // a range from the slider to the portion.
     213    enum SliderStyle { SliderNormal = 1, SliderThermo, SliderProp }
     214        m_style;
     215    MHColour m_sliderRefColour;
     216    // Internal attributes
     217    // In UK MHEG min_value, max_value and step_size can be changed.
     218    int max_value, min_value, step_size;
     219    int slider_value, portion;
    145220};
    146221
    147222class MHEntryField : public MHVisible, public MHInteractible
     
    153228    virtual void Initialise(MHParseNode *p, MHEngine *engine);
    154229    virtual void PrintMe(FILE *fd, int nTabs) const;
    155230    virtual void Display(MHEngine *) {} // Not (yet?) supported
     231
     232    // Implement the actions in the main inheritance line.
     233    virtual void SetInteractionStatus(bool newStatus, MHEngine *engine)
     234    { InteractSetInteractionStatus(newStatus, engine); }
     235    virtual bool GetInteractionStatus(void) { return InteractGetInteractionStatus(); }
     236    virtual void SetHighlightStatus(bool newStatus, MHEngine *engine)
     237    { InteractSetHighlightStatus(newStatus, engine); }
     238    virtual bool GetHighlightStatus(void) { return InteractGetHighlightStatus(); }
     239    virtual void Deactivation(MHEngine *engine) { InteractDeactivation(); }
    156240};
    157241
    158242// Button - not needed for UK MHEG.
     
    311395    virtual void CallAction(MHEngine *engine, MHRoot *pTarget, int nArg) { pTarget->SetLineStyle(nArg, engine); };
    312396};
    313397
     398class MHSetInteractionStatus: public MHActionBool
     399{
     400public:
     401    MHSetInteractionStatus(): MHActionBool("SetInteractionStatus") {}
     402    virtual void CallAction(MHEngine *engine, MHRoot *pTarget, bool newStatus)
     403    { Target(engine)->SetInteractionStatus(newStatus, engine); }
     404};
     405
     406class MHGetInteractionStatus: public MHActionObjectRef
     407{
     408public:
     409    MHGetInteractionStatus(): MHActionObjectRef(":GetInteractionStatus")  {}
     410    virtual void CallAction(MHEngine *, MHRoot *pTarget, MHRoot *pResult)
     411        { pResult->SetVariableValue(pTarget->GetInteractionStatus());}
     412};
     413
     414class MHSetHighlightStatus: public MHActionBool
     415{
     416public:
     417    MHSetHighlightStatus(): MHActionBool("SetHighlightStatus") {}
     418    virtual void CallAction(MHEngine *engine, MHRoot *pTarget, bool newStatus)
     419    { Target(engine)->SetHighlightStatus(newStatus, engine); }
     420};
     421
     422class MHGetHighlightStatus: public MHActionObjectRef
     423{
     424public:
     425    MHGetHighlightStatus(): MHActionObjectRef(":GetHighlightStatus")  {}
     426    virtual void CallAction(MHEngine *, MHRoot *pTarget, MHRoot *pResult)
     427        { pResult->SetVariableValue(pTarget->GetHighlightStatus());}
     428};
     429
     430class MHStep: public MHActionInt
     431{
     432public:
     433    MHStep(): MHActionInt(":Step") {}
     434    virtual void CallAction(MHEngine *engine, MHRoot *pTarget, int nArg) { pTarget->Step(nArg, engine); };
     435};
     436
     437class MHSetSliderValue: public MHActionInt
     438{
     439public:
     440    MHSetSliderValue(): MHActionInt(":SetSliderValue") {}
     441    virtual void CallAction(MHEngine *engine, MHRoot *pTarget, int nArg) { pTarget->SetSliderValue(nArg, engine); };
     442};
     443
     444class MHGetSliderValue: public MHActionObjectRef
     445{
     446public:
     447    MHGetSliderValue(): MHActionObjectRef(":GetSliderValue")  {}
     448    virtual void CallAction(MHEngine *, MHRoot *pTarget, MHRoot *pResult)
     449        { pResult->SetVariableValue(pTarget->GetSliderValue());}
     450};
     451
     452class MHSetPortion: public MHActionInt
     453{
     454public:
     455    MHSetPortion(): MHActionInt(":SetPortion") {}
     456    virtual void CallAction(MHEngine *engine, MHRoot *pTarget, int nArg) { pTarget->SetPortion(nArg, engine); };
     457};
     458
     459class MHGetPortion: public MHActionObjectRef
     460{
     461public:
     462    MHGetPortion(): MHActionObjectRef(":GetPortion")  {}
     463    virtual void CallAction(MHEngine *, MHRoot *pTarget, MHRoot *pResult)
     464        { pResult->SetVariableValue(pTarget->GetPortion());}
     465};
     466
     467class MHSetSliderParameters: public MHActionInt3
     468{
     469public:
     470    MHSetSliderParameters(): MHActionInt3(":SetSliderParameters") {}
     471    virtual void CallAction(MHEngine *engine, MHRoot *pTarget, int newMin, int newMax, int newStep)
     472        { pTarget->SetSliderParameters(newMin, newMax, newStep, engine); };
     473};
     474
    314475#endif
  • libs/libmythfreemheg/Engine.h

     
    11/* Engine.h
    22
    3    Copyright (C)  David C. J. Matthews 2004  dm at prolingua.co.uk
     3   Copyright (C)  David C. J. Matthews 2004, 2008  dm at prolingua.co.uk
    44
    55   This program is free software; you can redistribute it and/or
    66   modify it under the terms of the GNU General Public License
     
    6464    MHIngredient *m_pRequester;
    6565};
    6666
     67class MHInteractible;
     68
    6769class MHEngine: public MHEG {
    6870public:
    6971    MHEngine(MHContext *context);
     
    149151
    150152    static const char *MHEGEngineProviderIdString;
    151153
     154    // Interaction: Set if an Interactible has the focus and is receiving key presses.
     155    MHInteractible *GetInteraction(void) { return m_Interacting; }
     156    void SetInteraction(MHInteractible *p) { m_Interacting = p; }
     157
    152158protected:
    153159    void CheckLinks(const MHObjectRef &sourceRef, enum EventType ev, const MHUnion &un);
    154160    MHGroup *ParseProgram(QByteArray &text);
     
    193199
    194200    MHContext       *m_Context; // Pointer to the context providing drawing and other operations
    195201    bool            m_fBooting;
     202
     203    MHInteractible  *m_Interacting; // Set to current interactive object if any.
    196204};
    197205
    198206#endif
  • libs/libmythfreemheg/Bitmap.cpp

     
    11/* Bitmap.cpp
    22
    3    Copyright (C)  David C. J. Matthews 2004  dm at prolingua.co.uk
     3   Copyright (C)  David C. J. Matthews 2004, 2008  dm at prolingua.co.uk
    44
    55   This program is free software; you can redistribute it and/or
    66   modify it under the terms of the GNU General Public License
     
    113113    if (nCHook == 4) { // PNG.
    114114        m_pContent->CreateFromPNG(data, length);
    115115    }
    116     else if (nCHook == 2) { // MPEG I-frame.
     116    // CHook 5 seems to be used by the BBC on Freesat for an MPEG I-frame for the
     117    // background but enabling it here results in it overlaying the video.
     118    // Presumably it is not simply the same as CHook 2.
     119    else if (nCHook == 2 /* ||nCHook == 5 */) { // MPEG I-frame.
    117120        m_pContent->CreateFromMPEG(data, length);
    118121    }
    119122
  • libs/libmythfreemheg/freemheg.h

     
    6969{
    7070public:
    7171    MHRgba(int red, int green, int blue, int alpha):
    72       m_red(Qt::red), m_green(Qt::green), m_blue(Qt::blue), m_alpha(alpha) {};
     72      m_red(red), m_green(green), m_blue(blue), m_alpha(alpha) {};
    7373    MHRgba(): m_red(0), m_green(0), m_blue(0), m_alpha(0) {};
    7474    int red() const { return m_red; }
    7575    int green() const { return m_green; }
  • libs/libmythfreemheg/Text.h

     
    9090    void CreateContent(const unsigned char *p, int s, MHEngine *engine);
    9191};
    9292
    93 class MHHyperText : public MHText, public  MHInteractible
     93class MHHyperText : public MHText, public MHInteractible
    9494{
    9595public:
    9696    MHHyperText();
     
    9898    virtual ~MHHyperText();
    9999    virtual void Initialise(MHParseNode *p, MHEngine *engine);
    100100    virtual void PrintMe(FILE *fd, int nTabs) const;
     101
     102    // Implement the actions in the main inheritance line.
     103    virtual void SetInteractionStatus(bool newStatus, MHEngine *engine)
     104    { InteractSetInteractionStatus(newStatus, engine); }
     105    virtual bool GetInteractionStatus(void) { return InteractGetInteractionStatus(); }
     106    virtual void SetHighlightStatus(bool newStatus, MHEngine *engine)
     107    { InteractSetHighlightStatus(newStatus, engine); }
     108    virtual bool GetHighlightStatus(void) { return InteractGetHighlightStatus(); }
     109    virtual void Deactivation(MHEngine *engine) { InteractDeactivation(); }
    101110};
    102111
    103112// Get Text Data - get the data out of a text object.
  • libs/libmythfreemheg/Programs.cpp

     
    492492        }
    493493
    494494        else if (m_Name.Equal("DBG")) { // Debug - optional
    495             MHERROR("Debug ResidentProgram is not implemented");
     495            QString message = "DEBUG: ";
     496            for (int i = 0; i < args.Size(); i++) {
     497                MHUnion un;
     498                un.GetValueFrom(*(args.GetAt(i)), engine);
     499                switch (un.m_Type) {
     500                case MHUnion::U_Int:
     501                    message.append(QString("%1").arg(un.m_nIntVal));
     502                    break;
     503                case MHParameter::P_Bool:
     504                    message.append(un.m_fBoolVal ? "True" : "False");
     505                    break;
     506                case MHParameter::P_String:
     507                    message.append(QString::fromUtf8((const char *)un.m_StrVal.Bytes(), un.m_StrVal.Size()));
     508                    break;
     509                case MHParameter::P_ObjRef:
     510                    message.append(un.m_ObjRefVal.Printable());
     511                    break;
     512                case MHParameter::P_ContentRef:
     513                    message.append(un.m_ContentRefVal.Printable());
     514                    break;
     515                case MHParameter::P_Null:
     516                    break;
     517                }
     518            }
     519            MHLOG(MHLogNotifications, message);
    496520        }
    497521
    498522        else {
  • libs/libmythfreemheg/BaseActions.cpp

     
    11/* BaseActions.cpp
    22
    3    Copyright (C)  David C. J. Matthews 2004  dm at prolingua.co.uk
     3   Copyright (C)  David C. J. Matthews 2004, 2008  dm at prolingua.co.uk
    44
    55   This program is free software; you can redistribute it and/or
    66   modify it under the terms of the GNU General Public License
     
    8787    m_ResultVar2.Initialise(p->GetArgN(2), engine);
    8888}
    8989
     90void MHActionInt3::Initialise(MHParseNode *p, MHEngine *engine)
     91{
     92    MHElemAction::Initialise(p, engine);
     93    m_Argument1.Initialise(p->GetArgN(1), engine);
     94    m_Argument2.Initialise(p->GetArgN(2), engine);
     95    m_Argument3.Initialise(p->GetArgN(3), engine);
     96}
     97
     98void MHActionInt3::PrintArgs(FILE *fd, int /*nTabs*/) const
     99{
     100    m_Argument1.PrintMe(fd, 0);
     101    m_Argument2.PrintMe(fd, 0);
     102    m_Argument3.PrintMe(fd, 0);
     103}
     104
     105void MHActionInt3::Perform(MHEngine *engine)
     106{
     107    CallAction(engine, Target(engine), m_Argument1.GetValue(engine), m_Argument2.GetValue(engine), m_Argument3.GetValue(engine));
     108}
     109
    90110void MHActionInt4::Initialise(MHParseNode *p, MHEngine *engine)
    91111{
    92112    MHElemAction::Initialise(p, engine);
     
    160180{
    161181    CallAction(engine, Target(engine), engine->FindObject(m_ResultVar1), engine->FindObject(m_ResultVar2));
    162182}
     183
     184void MHActionBool::Initialise(MHParseNode *p, MHEngine *engine)
     185{
     186    MHElemAction::Initialise(p, engine);
     187    m_Argument.Initialise(p->GetArgN(1), engine);
     188}
     189
     190
     191void MHActionBool::Perform(MHEngine *engine)
     192{
     193    CallAction(engine, Target(engine), m_Argument.GetValue(engine));
     194}
  • libs/libmythfreemheg/Link.cpp

     
    11/* Link.cpp
    22
    3    Copyright (C)  David C. J. Matthews 2004  dm at prolingua.co.uk
     3   Copyright (C)  David C. J. Matthews 2004, 2008  dm at prolingua.co.uk
    44
    55   This program is free software; you can redistribute it and/or
    66   modify it under the terms of the GNU General Public License
  • libs/libmythfreemheg/TokenGroup.cpp

     
    5454        for (int i = 0; i < m_ActionSlots.Size(); i++) {
    5555            PrintTabs(fd, nTabs+2); fprintf(fd, "(\n");
    5656            MHActionSequence *pActions = m_ActionSlots.GetAt(i);
    57             if (pActions->Size() == 0) fprintf(fd, "NULL\n");
     57            if (pActions->Size() == 0)
     58                { PrintTabs(fd, nTabs+2); fprintf(fd, "NULL\n"); }
    5859            else pActions->PrintMe(fd, nTabs+2);
    5960            PrintTabs(fd, nTabs+2); fprintf(fd, ")\n");
    6061        }
  • libs/libmythfreemheg/Visible.cpp

     
    355355}
    356356
    357357
    358 MHInteractible::MHInteractible()
     358MHInteractible::MHInteractible(MHVisible *parent): m_parent(parent)
    359359{
    360 
     360    m_fEngineResp = true;
     361    m_fHighlightStatus = false;
     362    m_fInteractionStatus = false;
    361363}
    362364
    363365MHInteractible::~MHInteractible()
     
    365367
    366368}
    367369
    368 void MHInteractible::Initialise(MHParseNode */*p*/, MHEngine */*engine*/)
     370void MHInteractible::Initialise(MHParseNode *p, MHEngine *engine)
    369371{
     372    // Engine Resp - optional
     373    MHParseNode *pEngineResp = p->GetNamedArg(C_ENGINE_RESP);
     374    if (pEngineResp) m_fEngineResp = pEngineResp->GetArgN(0)->GetBoolValue();
     375    // Highlight colour.
     376    MHParseNode *phlCol = p->GetNamedArg(C_HIGHLIGHT_REF_COLOUR);
     377    if (phlCol) m_highlightRefColour.Initialise(phlCol->GetArgN(0), engine);
     378    else engine->GetDefaultHighlightRefColour(m_highlightRefColour);
     379    m_fHighlightStatus = false;
     380    m_fInteractionStatus = false;
    370381}
    371382
    372 void MHInteractible::PrintMe(FILE */*fd*/, int /*nTabs*/) const
     383void MHInteractible::PrintMe(FILE *fd, int nTabs) const
    373384{
     385    if (! m_fEngineResp)  { PrintTabs(fd, nTabs); fprintf(fd, ":EngineResp false\n"); }
     386
     387    if (m_highlightRefColour.IsSet()) {
     388        PrintTabs(fd, nTabs);
     389        fprintf(fd, ":HighlightRefColour ");
     390        m_highlightRefColour.PrintMe(fd, nTabs+1);
     391        fprintf(fd, "\n");
     392    }
    374393}
    375394
    376 MHSlider::MHSlider()
     395void MHInteractible::Interaction(MHEngine *engine)
    377396{
     397    m_fInteractionStatus = true;
     398    engine->SetInteraction(this);
     399    // The MHEG standard says: "generate visual feedback" here
     400    // but it appears that any visual feedback is controlled only
     401    // by the highlight status combined with engine-resp.
     402}
    378403
     404void MHInteractible::InteractSetInteractionStatus(bool newStatus, MHEngine *engine)
     405{
     406    if (newStatus) { // Turning interaction on.
     407        if (engine->GetInteraction() == 0) // No current interactible
     408            Interaction(engine); // virtual function
     409    }
     410    else { // Turning interaction off.
     411        if (m_fInteractionStatus) {
     412            m_fInteractionStatus = false;
     413            engine->SetInteraction(0);
     414            InteractionCompleted(engine); // Interaction is interrupted.
     415            engine->EventTriggered(m_parent, EventInteractionCompleted);
     416        }
     417    }
    379418}
    380419
    381 MHSlider::~MHSlider()
     420void MHInteractible::InteractSetHighlightStatus(bool newStatus, MHEngine *engine)
    382421{
     422    if (newStatus == m_fHighlightStatus) return;
     423    m_fHighlightStatus = newStatus;
     424    // If active redraw to show change of status.
     425    if (m_parent->GetRunningStatus() && m_fEngineResp)
     426        engine->Redraw(m_parent->GetVisibleArea());
     427    // Generate the event for the change of highlight status.
     428    engine->EventTriggered(m_parent, m_fHighlightStatus ? EventHighlightOn: EventHighlightOff);
     429}
    383430
     431MHSlider::MHSlider(): MHInteractible(this)
     432{
     433    m_orientation = SliderLeft;
     434    orig_max_value = -1;
     435    orig_min_value = initial_value = orig_step_size = 1;
     436    initial_portion = 0;
     437    m_style = SliderNormal;
    384438}
    385439
     440MHSlider::~MHSlider()
     441{
     442}
     443
    386444void MHSlider::Initialise(MHParseNode *p, MHEngine *engine)
    387445{
    388446    MHVisible::Initialise(p, engine);
    389447    MHInteractible::Initialise(p, engine);
    390     //
     448    //
     449    MHParseNode *pOrientation = p->GetNamedArg(C_ORIENTATION);
     450    if (pOrientation)
     451        m_orientation = (enum SliderOrientation)pOrientation->GetArgN(0)->GetEnumValue();
     452    // This is not optional.
     453
     454    MHParseNode *pMin = p->GetNamedArg(C_MIN_VALUE);
     455    if (pMin) orig_min_value = pMin->GetArgN(0)->GetIntValue();
     456    else orig_min_value = 1;
     457
     458    MHParseNode *pMax = p->GetNamedArg(C_MAX_VALUE);
     459    if (pMax) orig_max_value = pMax->GetArgN(0)->GetIntValue();
     460    else orig_max_value = orig_min_value-1; // Unset
     461
     462    MHParseNode *pInit = p->GetNamedArg(C_INITIAL_VALUE);
     463    if (pInit) initial_value = pInit->GetArgN(0)->GetIntValue();
     464    else initial_value = orig_min_value; // Default is min_value
     465
     466    MHParseNode *pPortion = p->GetNamedArg(C_INITIAL_PORTION);
     467    if (pPortion) initial_portion = pPortion->GetArgN(0)->GetIntValue();
     468    else initial_portion = orig_min_value-1; // Unset
     469
     470    MHParseNode *pStep = p->GetNamedArg(C_STEP_SIZE);
     471    if (pStep) orig_step_size = pStep->GetArgN(0)->GetIntValue();
     472    else orig_step_size = 1; // Unset
     473
     474    MHParseNode *pStyle = p->GetNamedArg(C_SLIDER_STYLE);
     475    if (pStyle) m_style = (enum SliderStyle)pStyle->GetArgN(0)->GetEnumValue();
     476    else m_style = SliderNormal;
     477
     478    MHParseNode *pslCol = p->GetNamedArg(C_SLIDER_REF_COLOUR);
     479    if (pslCol) m_sliderRefColour.Initialise(pslCol->GetArgN(0), engine);
     480    else engine->GetDefaultSliderRefColour(m_sliderRefColour);
    391481}
    392482
     483static const char *rchOrientation[] =
     484{
     485    "left", // 1
     486    "right",
     487    "up",
     488    "down" // 4
     489};
     490
     491// Look up the Orientation. Returns zero if it doesn't match.  Used in the text parser only.
     492int MHSlider::GetOrientation(const char *str)
     493{
     494    for (int i = 0; i < (int)(sizeof(rchOrientation)/sizeof(rchOrientation[0])); i++) {
     495        if (strcasecmp(str, rchOrientation[i]) == 0) return (i+1); // Numbered from 1
     496    }
     497    return 0;
     498}
     499
     500static const char *rchStyle[] =
     501{
     502    "normal", // 1
     503    "thermometer",
     504    "proportional" // 3
     505};
     506
     507int MHSlider::GetStyle(const char *str)
     508{
     509    for (int i = 0; i < (int)(sizeof(rchStyle)/sizeof(rchStyle[0])); i++) {
     510        if (strcasecmp(str, rchStyle[i]) == 0) return (i+1); // Numbered from 1
     511    }
     512    return 0;
     513}
     514
    393515void MHSlider::PrintMe(FILE *fd, int nTabs) const
    394516{
    395517    PrintTabs(fd, nTabs); fprintf(fd, "{:Slider ");
    396     MHVisible::PrintMe(fd, nTabs);
     518    MHVisible::PrintMe(fd, nTabs+1);
    397519    MHInteractible::PrintMe(fd, nTabs+1);
    398     fprintf(fd, "****TODO\n");
     520
     521    PrintTabs(fd, nTabs); fprintf(fd, ":Orientation %s\n", rchOrientation[m_orientation-1]);
     522
     523    if (initial_value >= orig_min_value) {
     524        PrintTabs(fd, nTabs+1); fprintf(fd, ":InitialValue %d\n", initial_value);
     525    }
     526
     527    if (orig_min_value != 1) {
     528        PrintTabs(fd, nTabs+1); fprintf(fd, ":MinValue %d\n", orig_min_value);
     529    }
     530
     531    if (orig_max_value > orig_min_value) {
     532        PrintTabs(fd, nTabs+1); fprintf(fd, ":MaxValue %d\n", orig_max_value);
     533    }
     534
     535    if (initial_portion >= orig_min_value) {
     536        PrintTabs(fd, nTabs+1); fprintf(fd, ":InitialPortion %d\n", initial_portion);
     537    }
     538
     539    if (orig_step_size != 1) {
     540        PrintTabs(fd, nTabs+1); fprintf(fd, ":StepSize %d\n", orig_step_size);
     541    }
     542
     543    if (m_style != SliderNormal)
     544    {
     545        PrintTabs(fd, nTabs+1);
     546        fprintf(fd, ":SliderStyle %s\n", rchStyle[m_style-1]);
     547    }
     548
     549    if (m_sliderRefColour.IsSet()) {
     550        PrintTabs(fd, nTabs+1);
     551        fprintf(fd, ":SliderRefColour ");
     552        m_sliderRefColour.PrintMe(fd, nTabs+2);
     553        fprintf(fd, "\n");
     554    }
     555
    399556    PrintTabs(fd, nTabs); fprintf(fd, "}\n");
    400557}
    401558
    402 MHEntryField::MHEntryField()
     559// The MHEG standard doesn't define where the internal values are
     560// initialised.  Assume it's during preparation.
     561void MHSlider::Preparation(MHEngine *engine)
    403562{
     563    MHVisible::Preparation(engine);
     564    max_value = orig_max_value;
     565    min_value = orig_min_value;
     566    step_size = orig_step_size;
     567    slider_value = initial_value;
     568    portion = initial_portion;
     569}
    404570
     571void MHSlider::Display(MHEngine *engine)
     572{
     573    MHContext *d = engine->GetContext();
     574    MHRgba colour;
     575    if (m_fHighlightStatus && m_fEngineResp)
     576        colour = GetColour(m_highlightRefColour);
     577    else colour = GetColour(m_sliderRefColour);
     578
     579    int major; // Direction of change.
     580    if (m_orientation == SliderLeft || m_orientation == SliderRight)
     581        major = m_nBoxWidth;
     582    else major = m_nBoxHeight;
     583
     584    if (max_value <= min_value) return; // Avoid divide by zero if error.
     585
     586    if (m_style == SliderNormal)
     587    {
     588        // This is drawn as a 9 pixel wide "thumb" at the position.
     589        major -= 9; // Width of "thumb"
     590        int posn = major * (slider_value-min_value) / (max_value-min_value);
     591        switch (m_orientation)
     592        {
     593        case SliderLeft:
     594            d->DrawRect(m_nPosX + posn, m_nPosY, 9, m_nBoxHeight, colour);
     595            break;
     596        case SliderRight:
     597            d->DrawRect(m_nPosX + m_nBoxWidth - posn - 9, m_nPosY, 9, m_nBoxHeight, colour);
     598            break;
     599        case SliderUp:
     600            d->DrawRect(m_nPosX, m_nPosY + m_nBoxHeight - posn - 9, m_nBoxWidth, 9, colour);
     601            break;
     602        case SliderDown:
     603            d->DrawRect(m_nPosX, m_nPosY + posn, m_nBoxWidth, 9, colour);
     604            break;
     605        }
     606    }
     607    else {
     608        // Thermometer and proportional sliders are drawn as bars.  Thermometers
     609        // run from the start to the position, proportional sliders from the
     610        // position for the "portion".
     611        int start = 0;
     612        int end = major * (slider_value-min_value) / (max_value-min_value);
     613        if (m_style == SliderProp)
     614        {
     615            start = end;
     616            end = major * (slider_value+portion -min_value) / (max_value-min_value);
     617        }
     618        switch (m_orientation)
     619        {
     620        case SliderLeft:
     621            d->DrawRect(m_nPosX + start, m_nPosY, end-start, m_nBoxHeight, colour);
     622            break;
     623        case SliderRight:
     624            d->DrawRect(m_nPosX + m_nBoxWidth - end, m_nPosY, end-start, m_nBoxHeight, colour);
     625            break;
     626        case SliderUp:
     627            d->DrawRect(m_nPosX, m_nPosY + m_nBoxHeight - end, m_nBoxWidth, end-start, colour);
     628            break;
     629        case SliderDown:
     630            d->DrawRect(m_nPosX, m_nPosY + start, m_nBoxWidth, end-start, colour);
     631            break;
     632        }
     633
     634    }
    405635}
    406636
     637void MHSlider::Interaction(MHEngine *engine)
     638{
     639    MHInteractible::Interaction(engine);
     640    // All the interaction is handled by KeyEvent.
     641}
     642
     643// Called when the interaction has been terminated and we need
     644// to restore the state to non-interacting.
     645void MHSlider::InteractionCompleted(MHEngine *engine)
     646{
     647    MHInteractible::InteractionCompleted(engine);
     648    // Redraw with the interaction highlighting turned off
     649    engine->Redraw(GetVisibleArea());
     650}
     651
     652// Called when a key is pressed.  The only keys that have an effect are
     653// the Select and Cancel keys which both terminate the action and the
     654// arrow keys.  The effect of the arrow keys depends on the orientation of
     655// the slider.
     656void MHSlider::KeyEvent(MHEngine *engine, int nCode)
     657{
     658    switch (nCode)
     659    {
     660    case 15: // Select key
     661    case 16: // Cancel key
     662        m_fInteractionStatus = false;
     663        engine->SetInteraction(0);
     664        InteractionCompleted(engine); // Interaction is interrupted.
     665        engine->EventTriggered(this, EventInteractionCompleted);
     666        break;
     667
     668    case 1: // Up
     669        if (m_orientation == SliderUp)
     670            Increment(engine);
     671        else if (m_orientation == SliderDown)
     672            Decrement(engine);
     673        break;
     674
     675    case 2: // Down
     676        if (m_orientation == SliderUp)
     677            Decrement(engine);
     678        else if (m_orientation == SliderDown)
     679            Increment(engine);
     680        break;
     681
     682    case 3: // Left
     683        if (m_orientation == SliderLeft)
     684            Increment(engine);
     685        else if (m_orientation == SliderRight)
     686            Decrement(engine);
     687        break;
     688
     689    case 4: // Right
     690        if (m_orientation == SliderLeft)
     691            Decrement(engine);
     692        else if (m_orientation == SliderRight)
     693            Increment(engine);
     694        break;
     695
     696    }
     697}
     698
     699void MHSlider::Increment(MHEngine *engine)
     700{
     701    if (slider_value+step_size <= max_value)
     702    {
     703        slider_value += step_size;
     704        engine->Redraw(GetVisibleArea());
     705        engine->EventTriggered(this, EventSliderValueChanged);
     706    }
     707}
     708
     709void MHSlider::Decrement(MHEngine *engine)
     710{
     711    if (slider_value-step_size >= min_value)
     712    {
     713        slider_value -= step_size;
     714        engine->Redraw(GetVisibleArea());
     715        engine->EventTriggered(this, EventSliderValueChanged);
     716    }
     717}
     718
     719void MHSlider::Step(int nbSteps, MHEngine *engine)
     720{
     721    step_size = nbSteps;
     722    if (m_fRunning) engine->Redraw(GetVisibleArea());
     723    engine->EventTriggered(this, EventSliderValueChanged);
     724}
     725
     726void MHSlider::SetSliderValue(int newValue, MHEngine *engine)
     727{
     728    slider_value = newValue;
     729    if (m_fRunning) engine->Redraw(GetVisibleArea());
     730    engine->EventTriggered(this, EventSliderValueChanged);
     731}
     732
     733void MHSlider::SetPortion(int newPortion, MHEngine *engine)
     734{
     735    portion = newPortion;
     736    if (m_fRunning) engine->Redraw(GetVisibleArea());
     737    engine->EventTriggered(this, EventSliderValueChanged);
     738}
     739
     740// Additional action defined in UK MHEG.
     741void MHSlider::SetSliderParameters(int newMin, int newMax, int newStep, MHEngine *engine)
     742{
     743    min_value = newMin;
     744    max_value = newMax;
     745    step_size = newStep;
     746    slider_value = newMin;
     747    if (m_fRunning) engine->Redraw(GetVisibleArea());
     748    engine->EventTriggered(this, EventSliderValueChanged);
     749}
     750
     751
     752MHEntryField::MHEntryField(): MHInteractible(this)
     753{
     754
     755}
     756
    407757MHEntryField::~MHEntryField()
    408758{
    409759