<commit_before>/*------------------------------------------------------------------------------

    Copyright (c) 2004 Media Development Loan Fund

    This file is part of the Campcaster project.
    http://campcaster.campware.org/
    To report bugs, send an e-mail to bugs@campware.org

    Campcaster is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Campcaster is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Campcaster; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Author   : $Author$
    Version  : $Revision$
    Location : $URL$

------------------------------------------------------------------------------*/

/* ============================================================ include files */

#ifdef HAVE_CONFIG_H
#include "configure.h"
#endif

#include <iostream>

#include "LiveSupport/Widgets/WidgetFactory.h"

#include "CuePlayer.h"

using namespace LiveSupport::Core;
using namespace LiveSupport::Widgets;
using namespace LiveSupport::GLiveSupport;

/* ===================================================  local data structures */

/* ================================================  local constants & macros */

/* ===============================================  local function prototypes */

/* =============================================================  module code */

/*------------------------------------------------------------------------------
 *  Constructor.
 *----------------------------------------------------------------------------*/
CuePlayer :: CuePlayer(Ptr<GLiveSupport>::Ref                   gLiveSupport,
                       Gtk::TreeView *                          treeView,
                       const PlayableTreeModelColumnRecord &    modelColumns)
                                                                    throw ()
          : gLiveSupport(gLiveSupport),
            treeView(treeView),
            modelColumns(modelColumns)
{
    Ptr<WidgetFactory>::Ref     wf = WidgetFactory::getInstance();

    playButton = Gtk::manage(wf->createButton(
                                    WidgetConstants::smallPlayButton ));
    pauseButton = Gtk::manage(wf->createButton(
                                    WidgetConstants::smallPauseButton ));
    stopButton = Gtk::manage(wf->createButton(
                                    WidgetConstants::smallStopButton ));

    playButton->signal_clicked().connect(sigc::mem_fun(*this,
                                        &CuePlayer::onPlayButtonClicked ));
    pauseButton->signal_clicked().connect(sigc::mem_fun(*this,
                                        &CuePlayer::onPauseButtonClicked ));
    stopButton->signal_clicked().connect(sigc::mem_fun(*this,
                                        &CuePlayer::onStopButtonClicked ));

    pack_end(*stopButton, Gtk::PACK_SHRINK, 3);
    pack_end(*playButton, Gtk::PACK_SHRINK, 3);

    audioState = waitingState;

    gLiveSupport->attachCueAudioListener(this);
}

/*------------------------------------------------------------------------------
 *  Destructor.
 *----------------------------------------------------------------------------*/
CuePlayer :: ~CuePlayer(void)                                       throw ()
{
    try {
        gLiveSupport->detachCueAudioListener(this);
    } catch (std::invalid_argument &e) {
        std::cerr << "Could not detach cue player audio listener."
                  << std::endl;
    }
}

/*------------------------------------------------------------------------------
 *  Event handler for the Play menu item selected from the entry context menu
 *----------------------------------------------------------------------------*/
void
CuePlayer :: onPlayItem(void)                                       throw ()
{
    Glib::RefPtr<Gtk::TreeView::Selection>
                    selection       = treeView->get_selection();
    std::vector<Gtk::TreePath>
                    selectedRows    = selection->get_selected_rows();
    Gtk::TreeIter   iter;

    if (selectedRows.size() > 0) {
        Gtk::TreePath   path = selectedRows.front();
        iter = treeView->get_model()->get_iter(path);
    } else {
        iter = treeView->get_model()->children().begin();
    }

    if (iter) {
        Ptr<Playable>::Ref  playable = (*iter)[modelColumns.playableColumn];
        try {
            gLiveSupport->playCueAudio(playable);
            setAudioState(playingState);
        } catch (std::runtime_error &e) {
            std::cerr << "GLiveSupport::playCueAudio() error:"
                        << std::endl << e.what() << std::endl;
        }
    }
}

/*------------------------------------------------------------------------------
 *  Event handler for the Play button getting clicked
 *----------------------------------------------------------------------------*/
void
CuePlayer :: onPlayButtonClicked(void)                              throw ()
{
    switch (audioState) {
        case waitingState:
            onPlayItem();
            break;

        case playingState:      // should never happen
            std::cerr << "Assertion failed in CuePlayer:" << std::endl
                      << "play button clicked when it should not be visible."
                      << std::endl;
            break;

        case pausedState:
            try {
                gLiveSupport->pauseCueAudio();      // ie, restart
                setAudioState(playingState);
            } catch (std::logic_error &e) {
                std::cerr << "GLiveSupport::pauseCueAudio() error:" << std::endl
                            << e.what() << std::endl;
            }
            break;
    }
}

/*------------------------------------------------------------------------------
 *  Event handler for the Pause button getting clicked
 *----------------------------------------------------------------------------*/
void
CuePlayer :: onPauseButtonClicked(void)                             throw ()
{
    try {
        gLiveSupport->pauseCueAudio();
        setAudioState(pausedState);
    } catch (std::logic_error &e) {
        std::cerr << "GLiveSupport::pauseCueAudio() error:" << std::endl
                    << e.what() << std::endl;
    }
}

/*------------------------------------------------------------------------------
 *  Event handler for the Stop button getting clicked
 *----------------------------------------------------------------------------*/
void
CuePlayer :: onStopButtonClicked(void)                              throw ()
{
    if (audioState != waitingState) {
        try {
            gLiveSupport->stopCueAudio();
        } catch (std::logic_error &e) {
            std::cerr << "GLiveSupport::stopCueAudio() error:" << std::endl
                        << e.what() << std::endl;
        }
        setAudioState(waitingState);
    }
}

/*------------------------------------------------------------------------------
 *  Event handler for the "cue audio player has stopped" event.
 *----------------------------------------------------------------------------*/
void
CuePlayer :: onStop(Ptr<const Glib::ustring>::Ref  errorMessage)    throw ()
{
    setAudioState(waitingState);

    if (errorMessage) {
        gLiveSupport->displayMessageWindow(errorMessage);
    }
}

/*------------------------------------------------------------------------------
 *  Set the state of the widget.
 *----------------------------------------------------------------------------*/
void
CuePlayer :: setAudioState(AudioState    newState)                  throw ()
{
    if ((audioState == waitingState || audioState == pausedState)
                && newState == playingState) {
        remove(*playButton);
        pack_end(*pauseButton, Gtk::PACK_SHRINK, 3);
        pauseButton->show();
        gLiveSupport->runMainLoop();

    } else if (audioState == playingState
                && (newState == waitingState || newState == pausedState)) {
        remove(*pauseButton);
        pack_end(*playButton, Gtk::PACK_SHRINK, 3);
        playButton->show();
        gLiveSupport->runMainLoop();
    }

    audioState = newState;
}

<commit_msg>fixed the bug that the Cue player Play/Pause button stays on Pause if there is an audio player error<commit_after>/*------------------------------------------------------------------------------

    Copyright (c) 2004 Media Development Loan Fund

    This file is part of the Campcaster project.
    http://campcaster.campware.org/
    To report bugs, send an e-mail to bugs@campware.org

    Campcaster is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Campcaster is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Campcaster; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Author   : $Author$
    Version  : $Revision$
    Location : $URL$

------------------------------------------------------------------------------*/

/* ============================================================ include files */

#ifdef HAVE_CONFIG_H
#include "configure.h"
#endif

#include <iostream>

#include "LiveSupport/Widgets/WidgetFactory.h"

#include "CuePlayer.h"

using namespace LiveSupport::Core;
using namespace LiveSupport::Widgets;
using namespace LiveSupport::GLiveSupport;

/* ===================================================  local data structures */

/* ================================================  local constants & macros */

/* ===============================================  local function prototypes */

/* =============================================================  module code */

/*------------------------------------------------------------------------------
 *  Constructor.
 *----------------------------------------------------------------------------*/
CuePlayer :: CuePlayer(Ptr<GLiveSupport>::Ref                   gLiveSupport,
                       Gtk::TreeView *                          treeView,
                       const PlayableTreeModelColumnRecord &    modelColumns)
                                                                    throw ()
          : gLiveSupport(gLiveSupport),
            treeView(treeView),
            modelColumns(modelColumns)
{
    Ptr<WidgetFactory>::Ref     wf = WidgetFactory::getInstance();

    playButton = Gtk::manage(wf->createButton(
                                    WidgetConstants::smallPlayButton ));
    pauseButton = Gtk::manage(wf->createButton(
                                    WidgetConstants::smallPauseButton ));
    stopButton = Gtk::manage(wf->createButton(
                                    WidgetConstants::smallStopButton ));

    playButton->signal_clicked().connect(sigc::mem_fun(*this,
                                        &CuePlayer::onPlayButtonClicked ));
    pauseButton->signal_clicked().connect(sigc::mem_fun(*this,
                                        &CuePlayer::onPauseButtonClicked ));
    stopButton->signal_clicked().connect(sigc::mem_fun(*this,
                                        &CuePlayer::onStopButtonClicked ));

    pack_end(*stopButton, Gtk::PACK_SHRINK, 3);
    pack_end(*playButton, Gtk::PACK_SHRINK, 3);

    audioState = waitingState;

    gLiveSupport->attachCueAudioListener(this);
}

/*------------------------------------------------------------------------------
 *  Destructor.
 *----------------------------------------------------------------------------*/
CuePlayer :: ~CuePlayer(void)                                       throw ()
{
    try {
        gLiveSupport->detachCueAudioListener(this);
    } catch (std::invalid_argument &e) {
        std::cerr << "Could not detach cue player audio listener."
                  << std::endl;
    }
}

/*------------------------------------------------------------------------------
 *  Event handler for the Play menu item selected from the entry context menu
 *----------------------------------------------------------------------------*/
void
CuePlayer :: onPlayItem(void)                                       throw ()
{
    Glib::RefPtr<Gtk::TreeView::Selection>
                    selection       = treeView->get_selection();
    std::vector<Gtk::TreePath>
                    selectedRows    = selection->get_selected_rows();
    Gtk::TreeIter   iter;

    if (selectedRows.size() > 0) {
        Gtk::TreePath   path = selectedRows.front();
        iter = treeView->get_model()->get_iter(path);
    } else {
        iter = treeView->get_model()->children().begin();
    }

    if (iter) {
        Ptr<Playable>::Ref  playable = (*iter)[modelColumns.playableColumn];
        try {
            setAudioState(playingState);
            gLiveSupport->playCueAudio(playable);
        } catch (std::runtime_error &e) {
            std::cerr << "GLiveSupport::playCueAudio() error:"
                        << std::endl << e.what() << std::endl;
        }
    }
}

/*------------------------------------------------------------------------------
 *  Event handler for the Play button getting clicked
 *----------------------------------------------------------------------------*/
void
CuePlayer :: onPlayButtonClicked(void)                              throw ()
{
    switch (audioState) {
        case waitingState:
            onPlayItem();
            break;

        case playingState:      // should never happen
            std::cerr << "Assertion failed in CuePlayer:" << std::endl
                      << "play button clicked when it should not be visible."
                      << std::endl;
            break;

        case pausedState:
            try {
                gLiveSupport->pauseCueAudio();      // ie, restart
                setAudioState(playingState);
            } catch (std::logic_error &e) {
                std::cerr << "GLiveSupport::pauseCueAudio() error:" << std::endl
                            << e.what() << std::endl;
            }
            break;
    }
}

/*------------------------------------------------------------------------------
 *  Event handler for the Pause button getting clicked
 *----------------------------------------------------------------------------*/
void
CuePlayer :: onPauseButtonClicked(void)                             throw ()
{
    try {
        gLiveSupport->pauseCueAudio();
        setAudioState(pausedState);
    } catch (std::logic_error &e) {
        std::cerr << "GLiveSupport::pauseCueAudio() error:" << std::endl
                    << e.what() << std::endl;
    }
}

/*------------------------------------------------------------------------------
 *  Event handler for the Stop button getting clicked
 *----------------------------------------------------------------------------*/
void
CuePlayer :: onStopButtonClicked(void)                              throw ()
{
    if (audioState != waitingState) {
        try {
            gLiveSupport->stopCueAudio();
        } catch (std::logic_error &e) {
            std::cerr << "GLiveSupport::stopCueAudio() error:" << std::endl
                        << e.what() << std::endl;
        }
        setAudioState(waitingState);
    }
}

/*------------------------------------------------------------------------------
 *  Event handler for the "cue audio player has stopped" event.
 *----------------------------------------------------------------------------*/
void
CuePlayer :: onStop(Ptr<const Glib::ustring>::Ref  errorMessage)    throw ()
{
    setAudioState(waitingState);

    if (errorMessage) {
        gLiveSupport->displayMessageWindow(errorMessage);
    }
}

/*------------------------------------------------------------------------------
 *  Set the state of the widget.
 *----------------------------------------------------------------------------*/
void
CuePlayer :: setAudioState(AudioState    newState)                  throw ()
{
    if ((audioState == waitingState || audioState == pausedState)
                && newState == playingState) {
        remove(*playButton);
        pack_end(*pauseButton, Gtk::PACK_SHRINK, 3);
        pauseButton->show();
        gLiveSupport->runMainLoop();

    } else if (audioState == playingState
                && (newState == waitingState || newState == pausedState)) {
        remove(*pauseButton);
        pack_end(*playButton, Gtk::PACK_SHRINK, 3);
        playButton->show();
        gLiveSupport->runMainLoop();
    }

    audioState = newState;
}

<|endoftext|>


<commit_before>/*
  Copyright 2013 Larry Gritz and the other authors and contributors.
  All Rights Reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:
  * Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
  * Neither the name of the software's owners nor the names of its
    contributors may be used to endorse or promote products derived from
    this software without specific prior written permission.
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

  (This is the Modified BSD License)
*/

#include "imageio.h"
#include <iostream>
#include <time.h>       /* time_t, struct tm, gmtime */
#include <libraw/libraw.h>

// This plugin utilises LibRaw:
// http://www.libraw.org/
// Documentation:
// http://www.libraw.org/docs

OIIO_PLUGIN_NAMESPACE_BEGIN

class RawInput : public ImageInput {
public:
    RawInput () : m_process(true), m_image(NULL) {}
    virtual ~RawInput() { close(); }
    virtual const char * format_name (void) const { return "raw"; }
    virtual bool open (const std::string &name, ImageSpec &newspec);
    virtual bool open (const std::string &name, ImageSpec &newspec, ImageSpec &config);
    virtual bool close();
    virtual bool read_native_scanline (int y, int z, void *data);

private:
    bool process();
    bool m_process;
    LibRaw m_processor;
    libraw_processed_image_t *m_image;
};

// Export version number and create function symbols
OIIO_PLUGIN_EXPORTS_BEGIN

    OIIO_EXPORT int raw_imageio_version = OIIO_PLUGIN_VERSION;
    OIIO_EXPORT ImageInput *raw_input_imageio_create () {
        return new RawInput;
    }
    OIIO_EXPORT const char *raw_input_extensions[] = {
        "bay", "bmq", "cr2", "crw", "cs1", "dc2", "dcr", "dng",
        "erf", "fff", "hdr", "k25", "kdc", "mdc", "mos", "mrw",
        "nef", "orf", "pef", "pxn", "raf", "raw", "rdc", "sr2",
        "srf", "x3f", "arw", "3fr", "cine", "ia", "kc2", "mef",
        "nrw", "qtk", "rw2", "sti", "rwl", "srw", "drf", "dsc",
        "ptx", "cap", "iiq", "rwz", NULL
    };

OIIO_PLUGIN_EXPORTS_END

bool
RawInput::open (const std::string &name, ImageSpec &newspec)
{
    // If user doesn't want to provide any config, just use an empty spec.
    ImageSpec config;
    return open(name, newspec, config);
}

bool
RawInput::open (const std::string &name, ImageSpec &newspec, ImageSpec &config)
{
    int ret;

    // open the image
    if ( (ret = m_processor.open_file(name.c_str()) ) != LIBRAW_SUCCESS) {
        error ("Could not open file \"%s\", %s", name.c_str(), libraw_strerror(ret));
        return false;
    }

    if ( (ret = m_processor.unpack() ) != LIBRAW_SUCCESS) {
        error ("Could not unpack \"%s\", %s",name.c_str(), libraw_strerror(ret));
        return false;
    }

    // Set file information
    m_spec = ImageSpec(m_processor.imgdata.sizes.iwidth,
                       m_processor.imgdata.sizes.iheight,
                       3, // LibRaw should only give us 3 channels
                       TypeDesc::UINT16);

    // Output 16 bit images
    m_processor.imgdata.params.output_bps = 16;

    // Set the gamma curve to Linear
    m_spec.attribute("oiio:ColorSpace","Linear");
    m_processor.imgdata.params.gamm[0] = 1.0;
    m_processor.imgdata.params.gamm[1] = 1.0;

    // Check to see if the user has explicitly set the output colorspace primaries
    ImageIOParameter *csp = config.find_attribute ("raw:ColorSpace", TypeDesc::STRING, false);
    if (csp) {
        static const char *colorspaces[] = { "raw",
                                             "sRGB",
                                             "Adobe",
                                             "Wide",
                                             "ProPhoto",
                                             "XYZ", NULL
                                             };

        std::string cs = *(const char**) csp->data();
        size_t c;
        for (c=0; c < sizeof(colorspaces) / sizeof(colorspaces[0]); c++)
            if (cs == colorspaces[c])
                break;
        if (cs == colorspaces[c])
            m_processor.imgdata.params.output_color = c;
        else {
            error("raw:ColorSpace set to unknown value");
            return false;
        }
        // Set the attribute in the output spec
        m_spec.attribute("raw:ColorSpace", cs);
    } else {
        // By default we use sRGB primaries for simplicity
        m_processor.imgdata.params.output_color = 1;
        m_spec.attribute("raw:ColorSpace", "sRGB");
    }

    // Exposure adjustment
    ImageIOParameter *ex = config.find_attribute ("raw:Exposure", TypeDesc::FLOAT, false);

    if (ex) {
        float exposure = *(float*)ex->data();
        if (exposure < 0.25f || exposure > 8.0f) {
            error("raw:Exposure invalid value. range 0.25f - 8.0f");
            return false;
        }
        m_processor.imgdata.params.exp_correc = 1; // enable exposure correction
        m_processor.imgdata.params.exp_shift = exposure; // set exposure correction
        // Set the attribute in the output spec
        m_spec.attribute ("raw:Exposure", exposure);
    }

    // Interpolation quality
    // note: LibRaw must be compiled with demosaic pack GPL2 to use
    // demosaic algorithms 5-9. It must be compiled with demosaic pack GPL3 for
    // algorithm 10. If either of these packs are not includeded, it will silently use option 3 - AHD
    ImageIOParameter *dm = config.find_attribute ("raw:Demosaic", TypeDesc::STRING, false);
    if (dm) {
        static const char *demosaic_algs[] = { "linear",
                                               "VNG",
                                               "PPG",
                                               "AHD",
                                               "DCB",
                                               "Modified AHD",
                                               "AFD",
                                               "VCD",
                                               "Mixed",
                                               "LMMSE",
                                               "AMaZE",
                                               // Future demosaicing algorithms should go here
                                               NULL
                                               };

        std::string demosaic = *(const char**) dm->data();
        size_t d;
        for (d=0; d < sizeof(demosaic_algs) / sizeof(demosaic_algs[0]); d++)
            if (demosaic == demosaic_algs[d])
                break;
        if (demosaic == demosaic_algs[d])
            m_processor.imgdata.params.user_qual = d;
        else if (demosaic == "none") {
            // See if we can access the Bayer patterned data for this raw file
            libraw_decoder_info_t decoder_info;
            m_processor.get_decoder_info(&decoder_info);
            if (!(decoder_info.decoder_flags & LIBRAW_DECODER_FLATFIELD)) {
                error("Unable to extract unbayered data from file \"%s\"", name.c_str());
                return false;
            }

            // User has selected no demosaicing, so no processing needs to be done
            m_process = false;

            // The image width and height may be different now, so update with new values
            // Also we will only be reading back a single, bayered channel
            m_spec.width = m_processor.imgdata.sizes.raw_width;
            m_spec.height = m_processor.imgdata.sizes.raw_height;
            m_spec.nchannels = 1;
            m_spec.channelnames.clear();
            m_spec.channelnames.push_back("R");

            // Also, any previously set demosaicing options are void, so remove them
            m_spec.erase_attribute("oiio:Colorspace", TypeDesc::STRING);
            m_spec.erase_attribute("raw:Colorspace", TypeDesc::STRING);
            m_spec.erase_attribute("raw:Exposure", TypeDesc::STRING);

        }
        else {
            error("raw:Demosaic set to unknown value");
            return false;
        }
        // Set the attribute in the output spec
        m_spec.attribute("raw:Demosaic", demosaic);
    } else {
        m_processor.imgdata.params.user_qual = 3;
        m_spec.attribute("raw:Demosaic", "AHD");
    }

    // Metadata

    const libraw_image_sizes_t &sizes (m_processor.imgdata.sizes);
    m_spec.attribute ("PixelAspectRatio", (float)sizes.pixel_aspect);
    // FIXME: sizes. top_margin, left_margin, raw_pitch, flip, mask?

    const libraw_iparams_t &idata (m_processor.imgdata.idata);
    if (idata.make[0])
        m_spec.attribute ("Make", idata.make);
    if (idata.model[0])
        m_spec.attribute ("Model", idata.model);
    // FIXME: idata. dng_version, is_foveon, colors, filters, cdesc

    const libraw_colordata_t &color (m_processor.imgdata.color);
    m_spec.attribute("Exif:Flash", (int) color.flash_used);
    if (color.model2[0])
        m_spec.attribute ("Software", color.model2);

    // FIXME -- all sorts of things in this struct

    const libraw_imgother_t &other (m_processor.imgdata.other);
    m_spec.attribute ("Exif:ISOSpeedRatings", (int) other.iso_speed);
    m_spec.attribute ("ExposureTime", other.shutter);
    m_spec.attribute ("Exif:ShutterSpeedValue", -log2f(other.shutter));
    m_spec.attribute ("FNumber", other.aperture);
    m_spec.attribute ("Exif:ApertureValue", 2.0f * log2f(other.aperture));
    m_spec.attribute ("Exif:FocalLength", other.focal_len);
    struct tm * m_tm = localtime(&m_processor.imgdata.other.timestamp);
    char datetime[20];
    strftime (datetime, 20, "%Y-%m-%d %H:%M:%S", m_tm);
    m_spec.attribute ("DateTime", datetime);
    // FIXME: other.shot_order
    // FIXME: other.gpsdata
    if (other.desc[0])
        m_spec.attribute ("ImageDescription", other.desc);
    if (other.artist[0])
        m_spec.attribute ("Artist", other.artist);

    // FIXME -- thumbnail possibly in m_processor.imgdata.thumbnail

    // Copy the spec to return to the user
    newspec = m_spec;
    return true;
}

bool
RawInput::close()
{
    if (m_image) {
        LibRaw::dcraw_clear_mem(m_image);
        m_image = NULL;
    }
    return true;
}

bool
RawInput::process()
{
    if (!m_image) {
        int ret = m_processor.dcraw_process();
        if (ret != LIBRAW_SUCCESS) {
            error("Processing image failed, %s", libraw_strerror(ret));
            return false;
        }

        m_image = m_processor.dcraw_make_mem_image(&ret);
        if (!m_image) {
            error("LibRaw failed to create in memory image");
            return false;
        }

        if (m_image->type != LIBRAW_IMAGE_BITMAP) {
            error("LibRaw did not return expected image type");
            return false;
        }

        if (m_image->colors != 3) {
            error("LibRaw did not return 3 channel image");
            return false;
        }

    }
    return true;
}

bool
RawInput::read_native_scanline (int y, int z, void *data)
{
    if (y < 0 || y >= m_spec.height) // out of range scanline
        return false;

    if (! m_process) {
        // The user has selected not to apply any debayering.
        // We take the raw data directly
        unsigned short *scanline = &((m_processor.imgdata.rawdata.raw_image)[m_spec.width*y]);
        memcpy(data, scanline, m_spec.scanline_bytes(true));
        return true;
    }

    // Check the state of the internal RAW reader.
    // Have to load the entire image at once, so only do this once
    if (! m_image) {
        if (!process()) {
            return false;
        }
    }

    int length = m_spec.width*m_image->colors; // Should always be 3 colors

    // Because we are reading UINT16's, we need to cast m_image->data
    unsigned short *scanline = &(((unsigned short *)m_image->data)[length*y]);
    memcpy(data, scanline, m_spec.scanline_bytes(true));

    return true;
}

OIIO_PLUGIN_NAMESPACE_END

<commit_msg>Read more Exif metadata from raw files by attempting to open the file as a TIFF and copying any Exif data we find -- which surprisingly works for quite a few raw formats.<commit_after>/*
  Copyright 2013 Larry Gritz and the other authors and contributors.
  All Rights Reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:
  * Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
  * Neither the name of the software's owners nor the names of its
    contributors may be used to endorse or promote products derived from
    this software without specific prior written permission.
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

  (This is the Modified BSD License)
*/

#include "imageio.h"
#include <iostream>
#include <time.h>       /* time_t, struct tm, gmtime */
#include <libraw/libraw.h>

// This plugin utilises LibRaw:
// http://www.libraw.org/
// Documentation:
// http://www.libraw.org/docs

OIIO_PLUGIN_NAMESPACE_BEGIN

class RawInput : public ImageInput {
public:
    RawInput () : m_process(true), m_image(NULL) {}
    virtual ~RawInput() { close(); }
    virtual const char * format_name (void) const { return "raw"; }
    virtual bool open (const std::string &name, ImageSpec &newspec);
    virtual bool open (const std::string &name, ImageSpec &newspec, ImageSpec &config);
    virtual bool close();
    virtual bool read_native_scanline (int y, int z, void *data);

private:
    bool process();
    bool m_process;
    LibRaw m_processor;
    libraw_processed_image_t *m_image;

    void read_tiff_metadata (const std::string &filename);
};

// Export version number and create function symbols
OIIO_PLUGIN_EXPORTS_BEGIN

    OIIO_EXPORT int raw_imageio_version = OIIO_PLUGIN_VERSION;
    OIIO_EXPORT ImageInput *raw_input_imageio_create () {
        return new RawInput;
    }
    OIIO_EXPORT const char *raw_input_extensions[] = {
        "bay", "bmq", "cr2", "crw", "cs1", "dc2", "dcr", "dng",
        "erf", "fff", "hdr", "k25", "kdc", "mdc", "mos", "mrw",
        "nef", "orf", "pef", "pxn", "raf", "raw", "rdc", "sr2",
        "srf", "x3f", "arw", "3fr", "cine", "ia", "kc2", "mef",
        "nrw", "qtk", "rw2", "sti", "rwl", "srw", "drf", "dsc",
        "ptx", "cap", "iiq", "rwz", NULL
    };

OIIO_PLUGIN_EXPORTS_END

bool
RawInput::open (const std::string &name, ImageSpec &newspec)
{
    // If user doesn't want to provide any config, just use an empty spec.
    ImageSpec config;
    return open(name, newspec, config);
}

bool
RawInput::open (const std::string &name, ImageSpec &newspec, ImageSpec &config)
{
    int ret;

    // open the image
    if ( (ret = m_processor.open_file(name.c_str()) ) != LIBRAW_SUCCESS) {
        error ("Could not open file \"%s\", %s", name.c_str(), libraw_strerror(ret));
        return false;
    }

    if ( (ret = m_processor.unpack() ) != LIBRAW_SUCCESS) {
        error ("Could not unpack \"%s\", %s",name.c_str(), libraw_strerror(ret));
        return false;
    }

    // Set file information
    m_spec = ImageSpec(m_processor.imgdata.sizes.iwidth,
                       m_processor.imgdata.sizes.iheight,
                       3, // LibRaw should only give us 3 channels
                       TypeDesc::UINT16);

    // Output 16 bit images
    m_processor.imgdata.params.output_bps = 16;

    // Set the gamma curve to Linear
    m_spec.attribute("oiio:ColorSpace","Linear");
    m_processor.imgdata.params.gamm[0] = 1.0;
    m_processor.imgdata.params.gamm[1] = 1.0;

    // Check to see if the user has explicitly set the output colorspace primaries
    ImageIOParameter *csp = config.find_attribute ("raw:ColorSpace", TypeDesc::STRING, false);
    if (csp) {
        static const char *colorspaces[] = { "raw",
                                             "sRGB",
                                             "Adobe",
                                             "Wide",
                                             "ProPhoto",
                                             "XYZ", NULL
                                             };

        std::string cs = *(const char**) csp->data();
        size_t c;
        for (c=0; c < sizeof(colorspaces) / sizeof(colorspaces[0]); c++)
            if (cs == colorspaces[c])
                break;
        if (cs == colorspaces[c])
            m_processor.imgdata.params.output_color = c;
        else {
            error("raw:ColorSpace set to unknown value");
            return false;
        }
        // Set the attribute in the output spec
        m_spec.attribute("raw:ColorSpace", cs);
    } else {
        // By default we use sRGB primaries for simplicity
        m_processor.imgdata.params.output_color = 1;
        m_spec.attribute("raw:ColorSpace", "sRGB");
    }

    // Exposure adjustment
    ImageIOParameter *ex = config.find_attribute ("raw:Exposure", TypeDesc::FLOAT, false);

    if (ex) {
        float exposure = *(float*)ex->data();
        if (exposure < 0.25f || exposure > 8.0f) {
            error("raw:Exposure invalid value. range 0.25f - 8.0f");
            return false;
        }
        m_processor.imgdata.params.exp_correc = 1; // enable exposure correction
        m_processor.imgdata.params.exp_shift = exposure; // set exposure correction
        // Set the attribute in the output spec
        m_spec.attribute ("raw:Exposure", exposure);
    }

    // Interpolation quality
    // note: LibRaw must be compiled with demosaic pack GPL2 to use
    // demosaic algorithms 5-9. It must be compiled with demosaic pack GPL3 for
    // algorithm 10. If either of these packs are not includeded, it will silently use option 3 - AHD
    ImageIOParameter *dm = config.find_attribute ("raw:Demosaic", TypeDesc::STRING, false);
    if (dm) {
        static const char *demosaic_algs[] = { "linear",
                                               "VNG",
                                               "PPG",
                                               "AHD",
                                               "DCB",
                                               "Modified AHD",
                                               "AFD",
                                               "VCD",
                                               "Mixed",
                                               "LMMSE",
                                               "AMaZE",
                                               // Future demosaicing algorithms should go here
                                               NULL
                                               };

        std::string demosaic = *(const char**) dm->data();
        size_t d;
        for (d=0; d < sizeof(demosaic_algs) / sizeof(demosaic_algs[0]); d++)
            if (demosaic == demosaic_algs[d])
                break;
        if (demosaic == demosaic_algs[d])
            m_processor.imgdata.params.user_qual = d;
        else if (demosaic == "none") {
            // See if we can access the Bayer patterned data for this raw file
            libraw_decoder_info_t decoder_info;
            m_processor.get_decoder_info(&decoder_info);
            if (!(decoder_info.decoder_flags & LIBRAW_DECODER_FLATFIELD)) {
                error("Unable to extract unbayered data from file \"%s\"", name.c_str());
                return false;
            }

            // User has selected no demosaicing, so no processing needs to be done
            m_process = false;

            // The image width and height may be different now, so update with new values
            // Also we will only be reading back a single, bayered channel
            m_spec.width = m_processor.imgdata.sizes.raw_width;
            m_spec.height = m_processor.imgdata.sizes.raw_height;
            m_spec.nchannels = 1;
            m_spec.channelnames.clear();
            m_spec.channelnames.push_back("R");

            // Also, any previously set demosaicing options are void, so remove them
            m_spec.erase_attribute("oiio:Colorspace", TypeDesc::STRING);
            m_spec.erase_attribute("raw:Colorspace", TypeDesc::STRING);
            m_spec.erase_attribute("raw:Exposure", TypeDesc::STRING);

        }
        else {
            error("raw:Demosaic set to unknown value");
            return false;
        }
        // Set the attribute in the output spec
        m_spec.attribute("raw:Demosaic", demosaic);
    } else {
        m_processor.imgdata.params.user_qual = 3;
        m_spec.attribute("raw:Demosaic", "AHD");
    }

    // Metadata

    const libraw_image_sizes_t &sizes (m_processor.imgdata.sizes);
    m_spec.attribute ("PixelAspectRatio", (float)sizes.pixel_aspect);
    // FIXME: sizes. top_margin, left_margin, raw_pitch, flip, mask?

    const libraw_iparams_t &idata (m_processor.imgdata.idata);
    if (idata.make[0])
        m_spec.attribute ("Make", idata.make);
    if (idata.model[0])
        m_spec.attribute ("Model", idata.model);
    // FIXME: idata. dng_version, is_foveon, colors, filters, cdesc

    const libraw_colordata_t &color (m_processor.imgdata.color);
    m_spec.attribute("Exif:Flash", (int) color.flash_used);
    if (color.model2[0])
        m_spec.attribute ("Software", color.model2);

    // FIXME -- all sorts of things in this struct

    const libraw_imgother_t &other (m_processor.imgdata.other);
    m_spec.attribute ("Exif:ISOSpeedRatings", (int) other.iso_speed);
    m_spec.attribute ("ExposureTime", other.shutter);
    m_spec.attribute ("Exif:ShutterSpeedValue", -log2f(other.shutter));
    m_spec.attribute ("FNumber", other.aperture);
    m_spec.attribute ("Exif:ApertureValue", 2.0f * log2f(other.aperture));
    m_spec.attribute ("Exif:FocalLength", other.focal_len);
    struct tm * m_tm = localtime(&m_processor.imgdata.other.timestamp);
    char datetime[20];
    strftime (datetime, 20, "%Y-%m-%d %H:%M:%S", m_tm);
    m_spec.attribute ("DateTime", datetime);
    // FIXME: other.shot_order
    // FIXME: other.gpsdata
    if (other.desc[0])
        m_spec.attribute ("ImageDescription", other.desc);
    if (other.artist[0])
        m_spec.attribute ("Artist", other.artist);

    // FIXME -- thumbnail possibly in m_processor.imgdata.thumbnail

    read_tiff_metadata (name);

    // Copy the spec to return to the user
    newspec = m_spec;
    return true;
}

void
RawInput::read_tiff_metadata (const std::string &filename)
{
    // Many of these raw formats look just like TIFF files, and we can use
    // that to extract a bunch of extra Exif metadata and thumbnail.
    ImageInput *in = ImageInput::create ("tiff");
    if (! in) {
        (void) OIIO::geterror();  // eat the error
        return;
    }
    ImageSpec newspec;
    bool ok = in->open (filename, newspec);
    if (ok) {
        // Transfer "Exif:" metadata to the raw spec.
        for (ParamValueList::const_iterator p = newspec.extra_attribs.begin();
             p != newspec.extra_attribs.end();  ++p) {
            if (Strutil::istarts_with (p->name().c_str(), "Exif:")) {
                m_spec.attribute (p->name().c_str(), p->type(), p->data());
            }
        }
    }

    in->close ();
    delete in;
}

bool
RawInput::close()
{
    if (m_image) {
        LibRaw::dcraw_clear_mem(m_image);
        m_image = NULL;
    }
    return true;
}

bool
RawInput::process()
{
    if (!m_image) {
        int ret = m_processor.dcraw_process();
        if (ret != LIBRAW_SUCCESS) {
            error("Processing image failed, %s", libraw_strerror(ret));
            return false;
        }

        m_image = m_processor.dcraw_make_mem_image(&ret);
        if (!m_image) {
            error("LibRaw failed to create in memory image");
            return false;
        }

        if (m_image->type != LIBRAW_IMAGE_BITMAP) {
            error("LibRaw did not return expected image type");
            return false;
        }

        if (m_image->colors != 3) {
            error("LibRaw did not return 3 channel image");
            return false;
        }

    }
    return true;
}

bool
RawInput::read_native_scanline (int y, int z, void *data)
{
    if (y < 0 || y >= m_spec.height) // out of range scanline
        return false;

    if (! m_process) {
        // The user has selected not to apply any debayering.
        // We take the raw data directly
        unsigned short *scanline = &((m_processor.imgdata.rawdata.raw_image)[m_spec.width*y]);
        memcpy(data, scanline, m_spec.scanline_bytes(true));
        return true;
    }

    // Check the state of the internal RAW reader.
    // Have to load the entire image at once, so only do this once
    if (! m_image) {
        if (!process()) {
            return false;
        }
    }

    int length = m_spec.width*m_image->colors; // Should always be 3 colors

    // Because we are reading UINT16's, we need to cast m_image->data
    unsigned short *scanline = &(((unsigned short *)m_image->data)[length*y]);
    memcpy(data, scanline, m_spec.scanline_bytes(true));

    return true;
}

OIIO_PLUGIN_NAMESPACE_END

<|endoftext|>


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/login/account_creation_view.h"

#include "app/gfx/canvas.h"
#include "chrome/browser/chromeos/login/rounded_rect_painter.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "views/border.h"

namespace {

const int kCornerRadius = 12;
const int kShadow = 10;
const int kPadding = 30;
const SkColor kBackground = SK_ColorWHITE;
const SkColor kShadowColor = 0x40223673;

}  // namespace

///////////////////////////////////////////////////////////////////////////////
// AccountCreationView, public:
AccountCreationView::AccountCreationView() {
}

AccountCreationView::~AccountCreationView() {
}

void AccountCreationView::Init() {
  // Use rounded rect background.
  views::Border* border = chromeos::CreateWizardBorder(
      &chromeos::BorderDefinition::kScreenBorder);
  set_border(border);
}

void AccountCreationView::InitDOM(Profile* profile,
                                  SiteInstance* site_instance) {
  DOMView::Init(profile, site_instance);
}

void AccountCreationView::SetTabContentsDelegate(
    TabContentsDelegate* delegate) {
  tab_contents_->set_delegate(delegate);
}

///////////////////////////////////////////////////////////////////////////////
// AccountCreationView, views::View implementation:
void AccountCreationView::Paint(gfx::Canvas* canvas) {
  PaintBorder(canvas);
  DOMView::Paint(canvas);
}
<commit_msg>chromeos: remove some variables that are now not more necessary. <commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/login/account_creation_view.h"

#include "app/gfx/canvas.h"
#include "chrome/browser/chromeos/login/rounded_rect_painter.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "views/border.h"

///////////////////////////////////////////////////////////////////////////////
// AccountCreationView, public:
AccountCreationView::AccountCreationView() {
}

AccountCreationView::~AccountCreationView() {
}

void AccountCreationView::Init() {
  // Use rounded rect background.
  views::Border* border = chromeos::CreateWizardBorder(
      &chromeos::BorderDefinition::kScreenBorder);
  set_border(border);
}

void AccountCreationView::InitDOM(Profile* profile,
                                  SiteInstance* site_instance) {
  DOMView::Init(profile, site_instance);
}

void AccountCreationView::SetTabContentsDelegate(
    TabContentsDelegate* delegate) {
  tab_contents_->set_delegate(delegate);
}

///////////////////////////////////////////////////////////////////////////////
// AccountCreationView, views::View implementation:
void AccountCreationView::Paint(gfx::Canvas* canvas) {
  PaintBorder(canvas);
  DOMView::Paint(canvas);
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/base_switches.h"
#include "base/command_line.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "net/base/mock_host_resolver.h"

class ExtensionHistoryApiTest : public ExtensionApiTest {
 public:
  virtual void SetUpInProcessBrowserTestFixture() {
    ExtensionApiTest::SetUpInProcessBrowserTestFixture();

    host_resolver()->AddRule("www.a.com", "127.0.0.1");
    host_resolver()->AddRule("www.b.com", "127.0.0.1");

    ASSERT_TRUE(StartTestServer());
  }
};

// Full text search indexing sometimes exceeds a timeout.
// Fix this as part of crbug/76170.
IN_PROC_BROWSER_TEST_F(ExtensionHistoryApiTest, FLAKY_MiscSearch) {
  ASSERT_TRUE(RunExtensionSubtest("history", "misc_search.html")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionHistoryApiTest, TimedSearch) {
  ASSERT_TRUE(RunExtensionSubtest("history", "timed_search.html")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionHistoryApiTest, Delete) {
  ASSERT_TRUE(RunExtensionSubtest("history", "delete.html")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionHistoryApiTest, GetVisits) {
  ASSERT_TRUE(RunExtensionSubtest("history", "get_visits.html")) << message_;
}

#if defined(OS_WIN)
// Searching for a URL right after adding it fails on win XP.
// Fix this as part of crbug/76170.
#define MAYBE_SearchAfterAdd FLAKY_SearchAfterAdd
#else
#define MAYBE_SearchAfterAdd SearchAfterAdd
#endif

IN_PROC_BROWSER_TEST_F(ExtensionHistoryApiTest, MAYBE_SearchAfterAdd) {
  ASSERT_TRUE(RunExtensionSubtest("history", "search_after_add.html"))
      << message_;
}
<commit_msg>Mark ExtensionHistoryApiTest.GetVisits flaky.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/base_switches.h"
#include "base/command_line.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "net/base/mock_host_resolver.h"

class ExtensionHistoryApiTest : public ExtensionApiTest {
 public:
  virtual void SetUpInProcessBrowserTestFixture() {
    ExtensionApiTest::SetUpInProcessBrowserTestFixture();

    host_resolver()->AddRule("www.a.com", "127.0.0.1");
    host_resolver()->AddRule("www.b.com", "127.0.0.1");

    ASSERT_TRUE(StartTestServer());
  }
};

// Full text search indexing sometimes exceeds a timeout.
// Fix this as part of crbug/76170.
IN_PROC_BROWSER_TEST_F(ExtensionHistoryApiTest, FLAKY_MiscSearch) {
  ASSERT_TRUE(RunExtensionSubtest("history", "misc_search.html")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionHistoryApiTest, TimedSearch) {
  ASSERT_TRUE(RunExtensionSubtest("history", "timed_search.html")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionHistoryApiTest, Delete) {
  ASSERT_TRUE(RunExtensionSubtest("history", "delete.html")) << message_;
}

// See crbug.com/79074
IN_PROC_BROWSER_TEST_F(ExtensionHistoryApiTest, FLAKY_GetVisits) {
  ASSERT_TRUE(RunExtensionSubtest("history", "get_visits.html")) << message_;
}

#if defined(OS_WIN)
// Searching for a URL right after adding it fails on win XP.
// Fix this as part of crbug/76170.
#define MAYBE_SearchAfterAdd FLAKY_SearchAfterAdd
#else
#define MAYBE_SearchAfterAdd SearchAfterAdd
#endif

IN_PROC_BROWSER_TEST_F(ExtensionHistoryApiTest, MAYBE_SearchAfterAdd) {
  ASSERT_TRUE(RunExtensionSubtest("history", "search_after_add.html"))
      << message_;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2010, Intel Corporation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Intel Corporation nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "chrome/browser/ui/meegotouch/back_forward_button_qt.h"
#include "chrome/browser/ui/meegotouch/browser_window_qt.h"
#include "chrome/browser/ui/meegotouch/browser_toolbar_qt.h"
#include "base/utf_string_conversions.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "base/string_number_conversions.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/metrics/user_metrics.h"
#include "content/browser/tab_contents/navigation_controller.h"
#include "content/browser/tab_contents/navigation_entry.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "chrome/common/url_constants.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "net/base/registry_controlled_domain.h"
#include "chrome/browser/history/history.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"

#include <QGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QVector>
#include <QList>
#include <QTimer>
#include <algorithm>
#include <QGraphicsSceneResizeEvent>
#include <QGraphicsWidget>
#include <QEvent>
#include <QDebug>
//#include <QTapAndHoldGesture>

#include <QDeclarativeEngine>
#include <QDeclarativeView>
#include <QDeclarativeContext>
#include <QDeclarativeItem>
#include <QDeclarativeImageProvider>
#include <QAbstractListModel>

// Image provider, managing to provide image to QML
// for history view
// Due to qml limitation, the id of image is suffixed with
// a static increaser, which forces using new image source
// each time
class HistoryImageProvider : public QDeclarativeImageProvider
{
public:
    HistoryImageProvider()
        : QDeclarativeImageProvider(QDeclarativeImageProvider::Image)
    {}

    // clear all images in image hashmap
    void clear()
    {
        imageList_.clear();
    }

    // overrided function, inherited from QDeclarativeImageProvider
    virtual QImage requestImage(const QString& id,
                                QSize* size,
                                const QSize& requestedSize)
    {
        DLOG(INFO) << "requesting image id: " << id.toStdString();
        int finded = id.indexOf("_");
        if (finded != -1) {
            QImage& image = imageList_[id.left(finded)];
            if (!image.isNull()) {
                //QImage scaled = image.scaled(requestedSize);
                *size = image.size();
                return image;
            }
        }
        *size = QSize(0, 0);
        return QImage();
    }

    // add a new image
    void addImage(const QString& id, const QImage &image)
    {
        imageList_.insert(id, image);
    }

private:
    QMap<QString, QImage> imageList_;
};

class HistoryStackModel;

// history entry to represent one history data
class HistoryEntry
{
public:

    // constructor
    // @param index the index in history stack
    // @param hiProvider image provider for history stack
    // @param entry representation of navigation entry in browser
    // @param controller current navigation controller in browser
    // @param model history stack model
    HistoryEntry(const int index,
                 HistoryImageProvider& hiProvider,
                 NavigationEntry* entry,
                 NavigationController* controller,
                 HistoryStackModel* model)
        : index_(index), hiProvider_(hiProvider), entry_(entry), model_(model)
    {
        // get image
        getThumbnailData(controller);
        std::wstring title_str = UTF16ToWide(entry->title());
        title_ = QString::fromStdWString(title_str);
    }

    void imgURLGen()
    {
        static QString prefix("image://historystack/");
        imageSrc_ = prefix + QString::number(index_) + "_" + QString::number(reloadNumber_);
    }

    void getThumbnailData(NavigationController *controller);

    NavigationEntry* entry() { return entry_; }
    QString image() { return imageSrc_; }
    QString title() { return title_; }

    static void incReloadNumber() { reloadNumber_++; }
    static unsigned long reloadNumber() { return reloadNumber_; }

private:
    // static increaser to generate unique image source
    static unsigned long reloadNumber_;

    int index_;
    HistoryImageProvider& hiProvider_;
    NavigationEntry* entry_;
    HistoryStackModel* model_;
    // image source
    QString imageSrc_;
    // history title
    QString title_;
    CancelableRequestConsumer consumer_;
};

unsigned long HistoryEntry::reloadNumber_ = 0;

// represent list model used in QML to store history data
class HistoryStackModel: public QAbstractListModel
{
    Q_OBJECT
    enum HistoryRole {
        IMAGE_ROLE = Qt::UserRole + 1,
        TITLE_ROLE
    };

public:
    HistoryStackModel(BackForwardButtonQtImpl* backForward)
        : back_forward_(backForward), returnedImages_(0)
    {
        QHash<int, QByteArray> roles;
        roles[IMAGE_ROLE] = "thumbSrc";
        roles[TITLE_ROLE] = "title";
        setRoleNames(roles);
    }

    ~HistoryStackModel()
    {
        clear();
    }

    // clear saved data
    void clear()
    {
        returnedImages_ = 0;
        beginResetModel();
        for(int i = 0; i < entryList_.count(); i++) {
            delete entryList_[i];
        }
        entryList_.clear();
        hiProvider_.clear();
        endResetModel();
    }

    int rowCount(const QModelIndex& parent = QModelIndex()) const
    {
        return entryList_.count();
    }

    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const
    {
        DLOG(INFO) << "read list model data: row = " << index.row() << ", column = " << index.column();
        if(index.row() < 0 || index.row() > entryList_.count())
            return QVariant();
        HistoryEntry* entry = entryList_[index.row()];
        switch(role) {
            case IMAGE_ROLE:
                return entry->image();
            case TITLE_ROLE:
                return entry->title();
            default:
                return QVariant();
        }
    }

    void appendEntry(NavigationController* controller, NavigationEntry *entry)
    {
        HistoryEntry* historyEntry
                                = new HistoryEntry(entryList_.count(),
                                                   hiProvider_,
                                                   entry,
                                                   controller,
                                                   this);
        historyEntry->imgURLGen();
        beginInsertRows(QModelIndex(), rowCount(), rowCount());
        entryList_.push_back(historyEntry);
        endInsertRows();
    }

    // emit a showHistory signal to QML
    void show() { emit showHistory(); }

    // emit a hideHistory signal to QML
    void hide() { emit hideHistory(); }

    // emit a current selected(focused) history entry
    void setCurrent(int index) { emit current(index); }

    HistoryImageProvider* hiProvider() { return &hiProvider_; }

Q_SIGNALS:
    // three signals used to notify QML
    void showHistory();
    void hideHistory();
    void current(int index);

public Q_SLOTS:
    // open specific page, called by QML element
    // It's called by QML element when user clicks one history entry in QML view
    void openPage(const int index);

private:
    BackForwardButtonQtImpl* back_forward_;
    // list to hold entries
    QList<HistoryEntry*> entryList_;
    HistoryImageProvider hiProvider_;
    // count of returned images from browser
    int returnedImages_;
};

class BackForwardButtonQtImpl
{
public:
    enum NaviState {
        ONLY_BACK = 0,
        ONLY_FORWARD,
        BACK_FORWARD
    };

    // constructor
    BackForwardButtonQtImpl(BrowserToolbarQt* toolbar, Browser* browser, BrowserWindowQt* window)
        :toolbar_(toolbar), browser_(browser), model_(this),
        state_(ONLY_BACK), active_(false)
    {
        QDeclarativeView* view = window->DeclarativeView();
        QDeclarativeContext *context = view->rootContext();
        context->setContextProperty("historyStackModel", &model_);
        context->engine()->addImageProvider(QLatin1String("historystack"), model_.hiProvider());
    }

    NavigationController* currentController()
    {
        return &(browser_->GetSelectedTabContents()->controller());
    }

    void openPage(NavigationEntry *entry)
    {
        currentController()->GoToIndex(currentController()->GetIndexOfEntry(entry));
        updateStatus();
    }

    void updateStatus()
    {
        if(currentController()->GetCurrentEntryIndex() == currentController()->entry_count() - 1)
        {
            state_ = ONLY_BACK;
            if (currentController()->entry_count() > 1) {
                active_ = true;
            } else {
                active_ = false;
            }
        } else if (currentController()->GetCurrentEntryIndex() == 0
                   && currentController()->entry_count() > 0) {
            state_ = ONLY_FORWARD;
        } else if (currentController()->CanGoBack() && currentController()->CanGoForward()) {
            state_ = BACK_FORWARD;
        } else {
            state_ = ONLY_BACK;
            active_ = false;
        }
        // update button status in qml
        updateButton();
        DLOG(INFO) << "In C++, updateStatus is invoked\n";
    }

    // update back-forward button icon
    void updateButton() { toolbar_->updateBfButton((int)state_, active_); }

    void tap()
    {
        DLOG(INFO) << "In C++, tap is invoked";
        switch(state_) {
        case ONLY_BACK:
            if (currentController()->CanGoBack()) {
                currentController()->GoBack();
            }
            break;
        case ONLY_FORWARD:
            if (currentController()->entry_count() == 2) {
                if (currentController()->CanGoForward()) {
                    currentController()->GoForward();
                }
            } else {
                prepareAndShowHistory();
            }
            break;
        case BACK_FORWARD:
            prepareAndShowHistory();
            break;
        default:
            break;
        }
    }

    void tapAndHold()
    {
        switch(state_) {
        case ONLY_BACK:
            prepareAndShowHistory();
            break;
        case ONLY_FORWARD:
            if (currentController()->entry_count() == 2) {
                if (currentController()->CanGoForward()) {
                    currentController()->GoForward();
                }
            } else {
                prepareAndShowHistory();
            }
            break;
        case BACK_FORWARD:
                prepareAndShowHistory();
            break;
        default:
            break;
        }
        updateStatus();
    }

    //prepare c++ list model and emit signal to QML to show it
    void prepareAndShowHistory()
    {
        model_.clear();
        int count = currentController()->entry_count();
        int curr = -1;
        HistoryEntry::incReloadNumber();
        for(int i = count - 1; i >= 0; i--)
        {
            DLOG(INFO) << "page index: ---" << i << "---\n";
            NavigationEntry* navEntry = currentController()->GetEntryAtIndex(i);
            // don't skip 'newtab' now, if yes, we do not only skip newtab here
            // but also skip count calculation for 'updateStatus'
            /*
            if (navEntry->url().HostNoBrackets() == "newtab") {
                // skip 'newtab'
                continue;
            }*/
            model_.appendEntry(currentController(), navEntry);
            curr++;
            if(currentController()->GetCurrentEntryIndex() == i) {
                model_.setCurrent(curr);
            }
        }
        model_.show();
        toolbar_->showHistory();
    }

private:
    BrowserToolbarQt* toolbar_;
    Browser* browser_;
    HistoryStackModel model_;

    // current navigation state
    NaviState state_;
    // whether the backward/forward button is active
    bool active_;
};

BackForwardButtonQt::BackForwardButtonQt(BrowserToolbarQt* toolbar, Browser* browser, BrowserWindowQt* window)
{
  impl_ = new BackForwardButtonQtImpl(toolbar, browser, window);
}

BackForwardButtonQt::~BackForwardButtonQt()
{
  delete impl_;
}

void BackForwardButtonQt::tap()
{
  impl_->tap();
}

void BackForwardButtonQt::tapAndHold()
{
  impl_->tapAndHold();
}

void BackForwardButtonQt::updateStatus()
{
  impl_->updateStatus();
}

void HistoryEntry::getThumbnailData(NavigationController *controller)
{
  history::TopSites* ts = controller->profile()->GetTopSites();
  if (ts) {
    scoped_refptr<RefCountedBytes> thumbnail_data;
    ts->GetPageThumbnail(entry_->url(), &thumbnail_data);
    if (thumbnail_data.get()) {
      std::vector <unsigned char> jpeg;
      std::copy(thumbnail_data->data.begin(),
                thumbnail_data->data.end(),
                std::back_inserter(jpeg));
      QImage image = QImage::fromData(jpeg.data(), jpeg.size());
      DLOG(INFO) << "image size ===== " << jpeg.size();
      hiProvider_.addImage(QString::number(index_), image);
    }
  }
}

void HistoryStackModel::openPage(const int index)
{
    back_forward_->openPage(entryList_[index]->entry());
    hide();
}

#include "moc_back_forward_button_qt.cc"
<commit_msg>History: also keep previous implementation for backward compatibility<commit_after>/*
 * Copyright (c) 2010, Intel Corporation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Intel Corporation nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "chrome/browser/ui/meegotouch/back_forward_button_qt.h"
#include "chrome/browser/ui/meegotouch/browser_window_qt.h"
#include "chrome/browser/ui/meegotouch/browser_toolbar_qt.h"
#include "base/utf_string_conversions.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "base/string_number_conversions.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/metrics/user_metrics.h"
#include "content/browser/tab_contents/navigation_controller.h"
#include "content/browser/tab_contents/navigation_entry.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "chrome/common/url_constants.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "net/base/registry_controlled_domain.h"
#include "chrome/browser/history/history.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"

#include <QGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QVector>
#include <QList>
#include <QTimer>
#include <algorithm>
#include <QGraphicsSceneResizeEvent>
#include <QGraphicsWidget>
#include <QEvent>
#include <QDebug>
//#include <QTapAndHoldGesture>

#include <QDeclarativeEngine>
#include <QDeclarativeView>
#include <QDeclarativeContext>
#include <QDeclarativeItem>
#include <QDeclarativeImageProvider>
#include <QAbstractListModel>

// Image provider, managing to provide image to QML
// for history view
// Due to qml limitation, the id of image is suffixed with
// a static increaser, which forces using new image source
// each time
class HistoryImageProvider : public QDeclarativeImageProvider
{
public:
    HistoryImageProvider()
        : QDeclarativeImageProvider(QDeclarativeImageProvider::Image)
    {}

    // clear all images in image hashmap
    void clear()
    {
        imageList_.clear();
    }

    // overrided function, inherited from QDeclarativeImageProvider
    virtual QImage requestImage(const QString& id,
                                QSize* size,
                                const QSize& requestedSize)
    {
        DLOG(INFO) << "requesting image id: " << id.toStdString();
        int finded = id.indexOf("_");
        if (finded != -1) {
            QImage& image = imageList_[id.left(finded)];
            if (!image.isNull()) {
                //QImage scaled = image.scaled(requestedSize);
                *size = image.size();
                return image;
            }
        }
        *size = QSize(0, 0);
        return QImage();
    }

    // add a new image
    void addImage(const QString& id, const QImage &image)
    {
        imageList_.insert(id, image);
    }

private:
    QMap<QString, QImage> imageList_;
};

class HistoryStackModel;

// history entry to represent one history data
class HistoryEntry
{
public:

    // constructor
    // @param index the index in history stack
    // @param hiProvider image provider for history stack
    // @param entry representation of navigation entry in browser
    // @param controller current navigation controller in browser
    // @param model history stack model
    HistoryEntry(const int index,
                 HistoryImageProvider& hiProvider,
                 NavigationEntry* entry,
                 NavigationController* controller,
                 HistoryStackModel* model)
        : index_(index), hiProvider_(hiProvider), entry_(entry), model_(model)
    {
        // get image
        getThumbnailData(controller);
        std::wstring title_str = UTF16ToWide(entry->title());
        title_ = QString::fromStdWString(title_str);
    }

    void imgURLGen()
    {
        static QString prefix("image://historystack/");
        imageSrc_ = prefix + QString::number(index_) + "_" + QString::number(reloadNumber_);
    }

    // get thumbnail
    void getThumbnailData(NavigationController *controller);

    // callback to get the image data from browser
    void onThumbnailDataAvailable(HistoryService::Handle request_handle,
                                  scoped_refptr<RefCountedBytes> jpeg_data);

    NavigationEntry* entry() { return entry_; }
    QString image() { return imageSrc_; }
    QString title() { return title_; }

    static void incReloadNumber() { reloadNumber_++; }
    static unsigned long reloadNumber() { return reloadNumber_; }

private:
    // static increaser to generate unique image source
    static unsigned long reloadNumber_;

    int index_;
    HistoryImageProvider& hiProvider_;
    NavigationEntry* entry_;
    HistoryStackModel* model_;
    // image source
    QString imageSrc_;
    // history title
    QString title_;
    CancelableRequestConsumer consumer_;
};

unsigned long HistoryEntry::reloadNumber_ = 0;

// represent list model used in QML to store history data
class HistoryStackModel: public QAbstractListModel
{
    Q_OBJECT
    enum HistoryRole {
        IMAGE_ROLE = Qt::UserRole + 1,
        TITLE_ROLE
    };

public:
    HistoryStackModel(BackForwardButtonQtImpl* backForward)
        : back_forward_(backForward), returnedImages_(0)
    {
        QHash<int, QByteArray> roles;
        roles[IMAGE_ROLE] = "thumbSrc";
        roles[TITLE_ROLE] = "title";
        setRoleNames(roles);
    }

    ~HistoryStackModel()
    {
        clear();
    }

    // clear saved data
    void clear()
    {
        returnedImages_ = 0;
        beginResetModel();
        for(int i = 0; i < entryList_.count(); i++) {
            delete entryList_[i];
        }
        entryList_.clear();
        hiProvider_.clear();
        endResetModel();
    }

    // wrapper to check whether browser returns all images of history
    // so we can reset model to avoid resetting model many times
    void beginReset()
    {
        // begin reset if necessary
        returnedImages_++;
        if (returnedImages_ == rowCount()) {
            DLOG(INFO) << "begin reset history stack model";
            // generate new number to create new image url
            HistoryEntry::incReloadNumber();
            for(int i = 0; i < entryList_.count(); i++) {
                HistoryEntry* entry = entryList_[i];
                entry->imgURLGen();
            }
            beginResetModel();
        }
    }

    void endReset()
    {
        // end reset if necessary
        if (returnedImages_ == rowCount()) {
            DLOG(INFO) << "end reset history stack model";
            endResetModel();
        }
    }

    int rowCount(const QModelIndex& parent = QModelIndex()) const
    {
        return entryList_.count();
    }

    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const
    {
        DLOG(INFO) << "read list model data: row = " << index.row() << ", column = " << index.column();
        if(index.row() < 0 || index.row() > entryList_.count())
            return QVariant();
        HistoryEntry* entry = entryList_[index.row()];
        switch(role) {
            case IMAGE_ROLE:
                return entry->image();
            case TITLE_ROLE:
                return entry->title();
            default:
                return QVariant();
        }
    }

    void appendEntry(NavigationController* controller, NavigationEntry *entry)
    {
        HistoryEntry* historyEntry
                                = new HistoryEntry(entryList_.count(),
                                                   hiProvider_,
                                                   entry,
                                                   controller,
                                                   this);
        historyEntry->imgURLGen();
        beginInsertRows(QModelIndex(), rowCount(), rowCount());
        entryList_.push_back(historyEntry);
        endInsertRows();
    }

    // emit a showHistory signal to QML
    void show() { emit showHistory(); }

    // emit a hideHistory signal to QML
    void hide() { emit hideHistory(); }

    // emit a current selected(focused) history entry
    void setCurrent(int index) { emit current(index); }

    HistoryImageProvider* hiProvider() { return &hiProvider_; }

Q_SIGNALS:
    // three signals used to notify QML
    void showHistory();
    void hideHistory();
    void current(int index);

public Q_SLOTS:
    // open specific page, called by QML element
    // It's called by QML element when user clicks one history entry in QML view
    void openPage(const int index);

private:
    BackForwardButtonQtImpl* back_forward_;
    // list to hold entries
    QList<HistoryEntry*> entryList_;
    HistoryImageProvider hiProvider_;
    // count of returned images from browser
    int returnedImages_;
};

class BackForwardButtonQtImpl
{
public:
    enum NaviState {
        ONLY_BACK = 0,
        ONLY_FORWARD,
        BACK_FORWARD
    };

    // constructor
    BackForwardButtonQtImpl(BrowserToolbarQt* toolbar, Browser* browser, BrowserWindowQt* window)
        :toolbar_(toolbar), browser_(browser), model_(this),
        state_(ONLY_BACK), active_(false)
    {
        QDeclarativeView* view = window->DeclarativeView();
        QDeclarativeContext *context = view->rootContext();
        context->setContextProperty("historyStackModel", &model_);
        context->engine()->addImageProvider(QLatin1String("historystack"), model_.hiProvider());
    }

    NavigationController* currentController()
    {
        return &(browser_->GetSelectedTabContents()->controller());
    }

    void openPage(NavigationEntry *entry)
    {
        currentController()->GoToIndex(currentController()->GetIndexOfEntry(entry));
        updateStatus();
    }

    void updateStatus()
    {
        if(currentController()->GetCurrentEntryIndex() == currentController()->entry_count() - 1)
        {
            state_ = ONLY_BACK;
            if (currentController()->entry_count() > 1) {
                active_ = true;
            } else {
                active_ = false;
            }
        } else if (currentController()->GetCurrentEntryIndex() == 0
                   && currentController()->entry_count() > 0) {
            state_ = ONLY_FORWARD;
        } else if (currentController()->CanGoBack() && currentController()->CanGoForward()) {
            state_ = BACK_FORWARD;
        } else {
            state_ = ONLY_BACK;
            active_ = false;
        }
        // update button status in qml
        updateButton();
        DLOG(INFO) << "In C++, updateStatus is invoked\n";
    }

    // update back-forward button icon
    void updateButton() { toolbar_->updateBfButton((int)state_, active_); }

    void tap()
    {
        DLOG(INFO) << "In C++, tap is invoked";
        switch(state_) {
        case ONLY_BACK:
            if (currentController()->CanGoBack()) {
                currentController()->GoBack();
            }
            break;
        case ONLY_FORWARD:
            if (currentController()->entry_count() == 2) {
                if (currentController()->CanGoForward()) {
                    currentController()->GoForward();
                }
            } else {
                prepareAndShowHistory();
            }
            break;
        case BACK_FORWARD:
            prepareAndShowHistory();
            break;
        default:
            break;
        }
    }

    void tapAndHold()
    {
        switch(state_) {
        case ONLY_BACK:
            prepareAndShowHistory();
            break;
        case ONLY_FORWARD:
            if (currentController()->entry_count() == 2) {
                if (currentController()->CanGoForward()) {
                    currentController()->GoForward();
                }
            } else {
                prepareAndShowHistory();
            }
            break;
        case BACK_FORWARD:
                prepareAndShowHistory();
            break;
        default:
            break;
        }
        updateStatus();
    }

    //prepare c++ list model and emit signal to QML to show it
    void prepareAndShowHistory()
    {
        model_.clear();
        int count = currentController()->entry_count();
        int curr = -1;
        HistoryEntry::incReloadNumber();
        for(int i = count - 1; i >= 0; i--)
        {
            DLOG(INFO) << "page index: ---" << i << "---\n";
            NavigationEntry* navEntry = currentController()->GetEntryAtIndex(i);
            // don't skip 'newtab' now, if yes, we do not only skip newtab here
            // but also skip count calculation for 'updateStatus'
            /*
            if (navEntry->url().HostNoBrackets() == "newtab") {
                // skip 'newtab'
                continue;
            }*/
            model_.appendEntry(currentController(), navEntry);
            curr++;
            if(currentController()->GetCurrentEntryIndex() == i) {
                model_.setCurrent(curr);
            }
        }
        model_.show();
        toolbar_->showHistory();
    }

private:
    BrowserToolbarQt* toolbar_;
    Browser* browser_;
    HistoryStackModel model_;

    // current navigation state
    NaviState state_;
    // whether the backward/forward button is active
    bool active_;
};

BackForwardButtonQt::BackForwardButtonQt(BrowserToolbarQt* toolbar, Browser* browser, BrowserWindowQt* window)
{
  impl_ = new BackForwardButtonQtImpl(toolbar, browser, window);
}

BackForwardButtonQt::~BackForwardButtonQt()
{
  delete impl_;
}

void BackForwardButtonQt::tap()
{
  impl_->tap();
}

void BackForwardButtonQt::tapAndHold()
{
  impl_->tapAndHold();
}

void BackForwardButtonQt::updateStatus()
{
  impl_->updateStatus();
}

void HistoryEntry::onThumbnailDataAvailable(HistoryService::Handle request_handle,
                                            scoped_refptr<RefCountedBytes> jpeg_data)
{
    model_->beginReset();
    if (jpeg_data.get()) {
        DLOG(INFO) << "get image id: " << index_;
        std::vector<unsigned char> thumbnail_data;
        std::copy(jpeg_data->data.begin(), jpeg_data->data.end(),
                std::back_inserter(thumbnail_data));
        QImage image = QImage::fromData(thumbnail_data.data(), thumbnail_data.size());
        hiProvider_.addImage(QString::number(index_), image);
    }
    model_->endReset();
}

void HistoryEntry::getThumbnailData(NavigationController *controller)
{
  history::TopSites* ts = controller->profile()->GetTopSites();
  if (ts) {
    scoped_refptr<RefCountedBytes> thumbnail_data;
    ts->GetPageThumbnail(entry_->url(), &thumbnail_data);
    if (thumbnail_data.get()) {
      std::vector <unsigned char> jpeg;
      std::copy(thumbnail_data->data.begin(),
                thumbnail_data->data.end(),
                std::back_inserter(jpeg));
      QImage image = QImage::fromData(jpeg.data(), jpeg.size());
      DLOG(INFO) << "image size ===== " << jpeg.size();
      hiProvider_.addImage(QString::number(index_), image);
    }
  } else {
     HistoryService* hs = controller->profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
     hs->GetPageThumbnail(entry_->url(),
                          &consumer_,
                          NewCallback(static_cast<HistoryEntry*>(this),
                                      &HistoryEntry::onThumbnailDataAvailable));
  }
}

void HistoryStackModel::openPage(const int index)
{
    back_forward_->openPage(entryList_[index]->entry());
    hide();
}

#include "moc_back_forward_button_qt.cc"
<|endoftext|>