Add a pretty image viewer widget to the artist info pane

This commit is contained in:
David Sansome 2010-10-07 23:13:41 +00:00
parent f017587099
commit f09a115339
44 changed files with 447 additions and 11 deletions

View File

@ -174,6 +174,7 @@ set(SOURCES
widgets/nowplayingwidget.cpp
widgets/osd.cpp
widgets/osdpretty.cpp
widgets/prettyimageview.cpp
widgets/progressitemdelegate.cpp
widgets/sliderwidget.cpp
widgets/spinbox.cpp
@ -310,6 +311,7 @@ set(HEADERS
widgets/nowplayingwidget.h
widgets/osd.h
widgets/osdpretty.h
widgets/prettyimageview.h
widgets/progressitemdelegate.h
widgets/sliderwidget.h
widgets/spinbox.h

View File

@ -17,6 +17,7 @@
#include "artistinfofetcher.h"
#include "artistinfoview.h"
#include "collapsibleinfopane.h"
#include "widgets/prettyimageview.h"
#include <QFile>
#include <QScrollArea>
@ -24,8 +25,8 @@
#include <QVBoxLayout>
#include <QtDebug>
ArtistInfoView::ArtistInfoView(QWidget *parent)
: SongInfoBase(parent),
ArtistInfoView::ArtistInfoView(NetworkAccessManager* network, QWidget *parent)
: SongInfoBase(network, parent),
fetcher_(new ArtistInfoFetcher(this)),
current_request_id_(-1),
scroll_area_(new QScrollArea),
@ -75,13 +76,16 @@ void ArtistInfoView::Clear() {
void ArtistInfoView::Update(const Song& metadata) {
Clear();
current_request_id_ = fetcher_->FetchInfo(metadata.artist());
image_view_ = new PrettyImageView(network_);
AddChild(image_view_);
}
void ArtistInfoView::ImageReady(int id, const QUrl& url) {
if (id != current_request_id_)
return;
qDebug() << "Image" << url;
image_view_->AddImage(url);
}
void ArtistInfoView::InfoReady(int id, const QString& title, QWidget* widget) {

View File

@ -20,6 +20,7 @@
#include "songinfobase.h"
class ArtistInfoFetcher;
class PrettyImageView;
class QScrollArea;
class QVBoxLayout;
@ -28,7 +29,7 @@ class ArtistInfoView : public SongInfoBase {
Q_OBJECT
public:
ArtistInfoView(QWidget* parent = 0);
ArtistInfoView(NetworkAccessManager* network, QWidget* parent = 0);
~ArtistInfoView();
protected:
@ -48,6 +49,8 @@ private:
QScrollArea* scroll_area_;
QVBoxLayout* container_;
PrettyImageView* image_view_;
QList<QWidget*> children_;
};

View File

@ -18,8 +18,8 @@
#include "lyricview.h"
#include "ui_lyricview.h"
LyricView::LyricView(QWidget *parent)
: SongInfoBase(parent),
LyricView::LyricView(NetworkAccessManager* network, QWidget *parent)
: SongInfoBase(network, parent),
ui_(new Ui_LyricView),
fetcher_(NULL),
current_request_id_(-1)

View File

@ -28,7 +28,7 @@ class LyricView : public SongInfoBase {
Q_OBJECT
public:
LyricView(QWidget* parent = 0);
LyricView(NetworkAccessManager* network, QWidget* parent = 0);
~LyricView();
void set_network(NetworkAccessManager* network);

View File

@ -16,8 +16,9 @@
#include "songinfobase.h"
SongInfoBase::SongInfoBase(QWidget* parent)
SongInfoBase::SongInfoBase(NetworkAccessManager* network, QWidget* parent)
: QWidget(parent),
network_(network),
dirty_(false)
{
}

View File

@ -21,11 +21,13 @@
#include "core/song.h"
class NetworkAccessManager;
class SongInfoBase : public QWidget {
Q_OBJECT
public:
SongInfoBase(QWidget* parent = 0);
SongInfoBase(NetworkAccessManager* network, QWidget* parent = 0);
public slots:
void SongChanged(const Song& metadata);
@ -36,6 +38,8 @@ protected:
virtual void Update(const Song& metadata) {}
NetworkAccessManager* network_;
private:
Song queued_metadata_;
bool dirty_;

View File

@ -1083,6 +1083,9 @@ msgstr ""
msgid "Loading tracks"
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr ""

View File

@ -1084,6 +1084,9 @@ msgstr ""
msgid "Loading tracks"
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr ""

View File

@ -1111,6 +1111,9 @@ msgstr "Carregant fluxe"
msgid "Loading tracks"
msgstr "Carregant pistes"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Carregar fitxers/URLs, substituïnt l'actual llista de reproducció"

View File

@ -1088,6 +1088,9 @@ msgstr "Načítám kanál"
msgid "Loading tracks"
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Nahraj soubory/URL, výměnou za aktuální playlist"

View File

@ -1089,6 +1089,9 @@ msgstr "Indlæser stream"
msgid "Loading tracks"
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Indlæser filer/URL'er og erstatter nuværende spilleliste"

View File

@ -1112,6 +1112,9 @@ msgstr "Lade Stream"
msgid "Loading tracks"
msgstr "Lade Stücke"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Öffne Dateien/URLs und ersetze die Wiedergabeliste"

View File

@ -1115,6 +1115,9 @@ msgstr "Φόρτωμα ροής (stream)"
msgid "Loading tracks"
msgstr "Φόρτωση κομματιών"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Φορτώνει αρχεία/URLs, αντικαθιστώντας την τρέχουσα λίστα αναπαραγωγής"

View File

@ -1087,6 +1087,9 @@ msgstr "Loading stream"
msgid "Loading tracks"
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Loads files/URLs, replacing current playlist"

View File

@ -1085,6 +1085,9 @@ msgstr "Loading stream"
msgid "Loading tracks"
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Loads files/URLs, replacing current playlist"

View File

@ -1116,6 +1116,9 @@ msgstr "Cargando flujo"
msgid "Loading tracks"
msgstr "Cargando pistas"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Cargar archivos/URLs, remplazando la actual lista de reproducción"

View File

@ -1085,6 +1085,9 @@ msgstr ""
msgid "Loading tracks"
msgstr "Ladataan kappaleita"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr ""

View File

@ -1120,6 +1120,9 @@ msgstr "Chargement du flux"
msgid "Loading tracks"
msgstr "Chargement des pistes"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Charger des fichiers/URLs, et remplacer la liste de lecture actuelle"

View File

@ -1090,6 +1090,9 @@ msgstr "A carregar a stream"
msgid "Loading tracks"
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr ""

View File

@ -1109,6 +1109,9 @@ msgstr "Adatfolyam betöltése"
msgid "Loading tracks"
msgstr "Számok betöltése"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Fájlok/URL-ek betöltése, lejátszási lista cseréje"

View File

@ -1119,6 +1119,9 @@ msgstr "Caricamento flusso"
msgid "Loading tracks"
msgstr "Caricamento delle tracce"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Carica file/URL, sostituendo la scaletta attuale"

View File

@ -1085,6 +1085,9 @@ msgstr ""
msgid "Loading tracks"
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr ""

View File

@ -1084,6 +1084,9 @@ msgstr ""
msgid "Loading tracks"
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr ""

View File

@ -1087,6 +1087,9 @@ msgstr "Lader lydstrøm"
msgid "Loading tracks"
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr ""

View File

@ -1113,6 +1113,9 @@ msgstr "Radiostream laden"
msgid "Loading tracks"
msgstr "Tracks laden"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Laadt bestanden/URLs, en vervangt de huidige afspeellijst"

View File

@ -1083,6 +1083,9 @@ msgstr "Cargament del flux"
msgid "Loading tracks"
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr ""

View File

@ -1111,6 +1111,9 @@ msgstr "Ładowanie strumienia"
msgid "Loading tracks"
msgstr "Wczytywanie ścieżek"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Wczytywanie plików/adresów URL, zamiana obecnej listy odtwarzania"

View File

@ -1111,6 +1111,9 @@ msgstr "Carregando emissão"
msgid "Loading tracks"
msgstr "Carregando faixas"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Carregar ficheiros/URLs, substituindo a lista atual"

View File

@ -1100,6 +1100,9 @@ msgstr "Carregando transmissão"
msgid "Loading tracks"
msgstr "Carregando faixas"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Carregar arquivos/sites, substiuindo a lista de reprodução atual"

View File

@ -1084,6 +1084,9 @@ msgstr "Se încarcă fluxul"
msgid "Loading tracks"
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr ""

View File

@ -1104,6 +1104,9 @@ msgstr "Загрузка потока"
msgid "Loading tracks"
msgstr "Загрузить композиции"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Загрузить файлы/URLs, заменяя текущий список воспроизведения"

View File

@ -1106,6 +1106,9 @@ msgstr "Načítava sa stream"
msgid "Loading tracks"
msgstr "Načítavajú sa skladby"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Načítať súbory/URLy, nahradiť nimi aktuálny playlist"

View File

@ -1105,6 +1105,9 @@ msgstr "Nalaganje pretoka"
msgid "Loading tracks"
msgstr "Nalaganje skladb"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Naloži datoteke/povezave in zamenjaj trenutni seznam predvajanja"

View File

@ -1089,6 +1089,9 @@ msgstr "Учитавам ток"
msgid "Loading tracks"
msgstr "Учитавам нумере"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr ""

View File

@ -1093,6 +1093,9 @@ msgstr "Laddar ström"
msgid "Loading tracks"
msgstr "Läser in spår"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Ladda filer/webbadresser, ersätt nuvarande spellista"

View File

@ -1108,6 +1108,9 @@ msgstr "Yayın akışı yükleniyor"
msgid "Loading tracks"
msgstr "Parçalar yükleniyor"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Dosyaları/URLleri yükler, mevcut çalma listesinin yerine koyar"

View File

@ -1074,6 +1074,9 @@ msgstr ""
msgid "Loading tracks"
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr ""

View File

@ -1105,6 +1105,9 @@ msgstr "Завнтаження потоку"
msgid "Loading tracks"
msgstr "Завантаження доріжок"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "Завантажити файли/адреси, замінюючи поточний список відтворення"

View File

@ -1083,6 +1083,9 @@ msgstr ""
msgid "Loading tracks"
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "装入文件/URL替换当前播放列表"

View File

@ -1089,6 +1089,9 @@ msgstr "載入串流"
msgid "Loading tracks"
msgstr "載入歌曲"
msgid "Loading..."
msgstr ""
msgid "Loads files/URLs, replacing current playlist"
msgstr "加載檔案/網址,取代目前的播放清單"

View File

@ -142,8 +142,8 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg
file_view_(new FileView(this)),
radio_view_(new RadioViewContainer(this)),
device_view_(new DeviceView(this)),
lyric_view_(new LyricView(this)),
artist_info_view_(new ArtistInfoView(this)),
lyric_view_(new LyricView(network, this)),
artist_info_view_(new ArtistInfoView(network, this)),
settings_dialog_(NULL),
cover_manager_(NULL),
equalizer_(new Equalizer),

View File

@ -0,0 +1,228 @@
/* This file is part of Clementine.
Clementine 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 3 of the License, or
(at your option) any later version.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "prettyimageview.h"
#include "core/networkaccessmanager.h"
#include <QApplication>
#include <QMouseEvent>
#include <QNetworkReply>
#include <QPainter>
#include <QStyle>
#include <QStyleOption>
#include <QTimeLine>
#include <QtDebug>
const int PrettyImageView::kArrowWidth = 35;
const int PrettyImageView::kImageHeight = 160;
const int PrettyImageView::kTotalHeight = 200;
const int PrettyImageView::kBorderHeight = 10;
const int PrettyImageView::kBaseAnimationDuration = 500; // msec
const int PrettyImageView::kArrowAnimationDuration = 250; // msec
PrettyImageView::PrettyImageView(NetworkAccessManager* network, QWidget* parent)
: QWidget(parent),
network_(network),
next_image_request_id_(1),
current_index_(0),
base_timeline_(new QTimeLine(kBaseAnimationDuration, this)),
left_timeline_(new QTimeLine(kArrowAnimationDuration, this)),
right_timeline_(new QTimeLine(kArrowAnimationDuration, this))
{
setMouseTracking(true);
setMinimumHeight(kTotalHeight);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
connect(base_timeline_, SIGNAL(valueChanged(qreal)), SLOT(update()));
connect(left_timeline_, SIGNAL(valueChanged(qreal)), SLOT(update()));
connect(right_timeline_, SIGNAL(valueChanged(qreal)), SLOT(update()));
}
QRect PrettyImageView::left() const {
return QRect(0, 0, kArrowWidth, height());
}
QRect PrettyImageView::right() const {
return QRect(width() - kArrowWidth, 0, kArrowWidth, height());
}
QPolygon PrettyImageView::arrow(const QRect& rect, int direction) const {
QPoint point(direction > 0 ? rect.right() : rect.left(),
(rect.bottom() - rect.top()) / 2 + rect.top());
return QPolygon()
<< point
<< QPoint(point.x() - direction * kArrowWidth, point.y() - kArrowWidth)
<< QPoint(point.x() - direction * kArrowWidth, point.y() + kArrowWidth);
}
void PrettyImageView::Clear() {
images_.clear();
image_requests_.clear();
current_index_ = 0;
update();
}
void PrettyImageView::AddImage(const QUrl& url) {
const int index = images_.count();
const int id = next_image_request_id_ ++;
// Add the image to the list
images_ << Image(url);
// Start fetching the image
network_->Get(url, this, "ImageFetched", id);
image_requests_[id] = index;
}
void PrettyImageView::ImageFetched(quint64 id, QNetworkReply* reply) {
reply->deleteLater();
if (!image_requests_.contains(id))
return;
Image& data = images_[image_requests_.take(id)];
QImage image = QImage::fromData(reply->readAll());
if (image.isNull())
return;
data.SetImage(image);
update();
}
void PrettyImageView::paintEvent(QPaintEvent*) {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing, true);
p.setBrush(palette().color(QPalette::Highlight));
p.setPen(QPen(palette().color(QPalette::Text), 0.5));
// Draw left arrow
p.setOpacity(base_timeline_->currentValue() * (0.5 + left_timeline_->currentValue() * 0.5));
p.drawConvexPolygon(arrow(left(), -1));
// Draw right arrow
p.setOpacity(base_timeline_->currentValue() * (0.5 + right_timeline_->currentValue() * 0.5));
p.drawConvexPolygon(arrow(right(), +1));
// Draw the current image
if (current_index_ >= 0 && current_index_ < images_.count()) {
QRect rect(0, 0, width(), kImageHeight - kBorderHeight);
p.setOpacity(1.0);
DrawImage(&p, rect, images_[current_index_]);
}
}
void PrettyImageView::DrawImage(QPainter* p, const QRect& rect, const Image& image) {
const int width = image.image_.isNull() ? rect.height() * 1.6 :
float(image.image_.width()) / image.image_.height() * rect.height();
// Center the image in the rectangle
QRect actual_rect(0, kBorderHeight, width, rect.height());
actual_rect.moveLeft((rect.width() - width) / 2);
// Draw the main image
DrawThumbnail(p, actual_rect, image);
// Draw the reflection
// Figure out where to draw it
QRect reflection_rect(actual_rect);
reflection_rect.moveTop(reflection_rect.bottom());
// Create the reflected pixmap
QImage reflection(reflection_rect.size(), QImage::Format_ARGB32_Premultiplied);
reflection.fill(palette().color(QPalette::Base).rgba());
QPainter reflection_painter(&reflection);
// Set up the transformation
QTransform transform;
transform.scale(1.0, -1.0);
transform.translate(0.0, -actual_rect.height());
reflection_painter.setTransform(transform);
QRect fade_rect(reflection.rect().bottomLeft() - QPoint(0, kTotalHeight - kImageHeight),
reflection.rect().bottomRight());
// Draw the reflection into the buffer
DrawThumbnail(&reflection_painter, reflection.rect(), image);
// Make it fade out towards the bottom
QLinearGradient fade_gradient(fade_rect.topLeft(), fade_rect.bottomLeft());
fade_gradient.setColorAt(0.0, QColor(0, 0, 0, 0));
fade_gradient.setColorAt(1.0, QColor(0, 0, 0, 128));
reflection_painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
reflection_painter.fillRect(fade_rect, fade_gradient);
reflection_painter.end();
// Draw the reflection on the image
p->drawImage(reflection_rect, reflection);
}
void PrettyImageView::DrawThumbnail(QPainter* p, const QRect& rect, const Image& image) {
if (image.image_.isNull()) {
// Draw an empty box if there's no image to show
p->setPen(palette().color(QPalette::Disabled, QPalette::Text));
p->drawText(rect, Qt::AlignHCenter | Qt::AlignBottom, tr("Loading..."));
} else {
// Draw the image
p->drawPixmap(rect, image.thumbnail_);
}
}
void PrettyImageView::mouseReleaseEvent(QMouseEvent* e) {
if (left().contains(e->pos()))
current_index_ = qMax(0, current_index_ - 1);
else if (right().contains(e->pos()))
current_index_ = qMin(images_.count() - 1, current_index_ + 1);
update();
}
void PrettyImageView::mouseMoveEvent(QMouseEvent* e) {
SetTimeLineActive(left_timeline_, left().contains(e->pos()));
SetTimeLineActive(right_timeline_, right().contains(e->pos()));
}
void PrettyImageView::enterEvent(QEvent*) {
SetTimeLineActive(base_timeline_, true);
}
void PrettyImageView::leaveEvent(QEvent*) {
SetTimeLineActive(base_timeline_, false);
}
void PrettyImageView::SetTimeLineActive(QTimeLine* timeline, bool active) {
const QTimeLine::Direction direction =
active ? QTimeLine::Forward : QTimeLine::Backward;
if (timeline->state() == QTimeLine::Running && timeline->direction() == direction)
return;
timeline->setDirection(direction);
if (timeline->state() != QTimeLine::Running)
timeline->resume();
}
void PrettyImageView::Image::SetImage(const QImage& image) {
image_ = image;
thumbnail_ = QPixmap::fromImage(image_.scaledToHeight(
kImageHeight, Qt::SmoothTransformation));
}

View File

@ -0,0 +1,92 @@
/* This file is part of Clementine.
Clementine 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 3 of the License, or
(at your option) any later version.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PRETTYIMAGEVIEW_H
#define PRETTYIMAGEVIEW_H
#include <QMap>
#include <QUrl>
#include <QWidget>
class NetworkAccessManager;
class QNetworkReply;
class QTimeLine;
class PrettyImageView : public QWidget {
Q_OBJECT
public:
PrettyImageView(NetworkAccessManager* network, QWidget* parent = 0);
static const int kTotalHeight;
static const int kImageHeight;
static const int kBorderHeight;
static const int kArrowWidth;
static const int kBaseAnimationDuration;
static const int kArrowAnimationDuration;
public slots:
void Clear();
void AddImage(const QUrl& url);
protected:
void paintEvent(QPaintEvent*);
void mouseMoveEvent(QMouseEvent*);
void mouseReleaseEvent(QMouseEvent*);
void enterEvent(QEvent*);
void leaveEvent(QEvent*);
private:
struct Image {
Image(const QUrl& url) : loading_(false), url_(url) {}
void SetImage(const QImage& image);
bool loading_;
QUrl url_;
QImage image_;
QPixmap thumbnail_;
};
QRect left() const;
QRect right() const;
QPolygon arrow(const QRect& rect, int direction) const;
void SetTimeLineActive(QTimeLine* timeline, bool active);
void DrawImage(QPainter* p, const QRect& rect, const Image& image);
void DrawThumbnail(QPainter* p, const QRect& rect, const Image& image);
private slots:
void ImageFetched(quint64 id, QNetworkReply* reply);
private:
NetworkAccessManager* network_;
QMap<quint64, int> image_requests_;
quint64 next_image_request_id_;
QList<Image> images_;
int current_index_;
QTimeLine* base_timeline_;
QTimeLine* left_timeline_;
QTimeLine* right_timeline_;
};
#endif // PRETTYIMAGEVIEW_H