You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegraphics/kmrml/kmrml/mrml_view.cpp

481 lines
13 KiB

/* This file is part of the KDE project
Copyright (C) 2001,2002 Carsten Pfeiffer <pfeiffer@kde.org>
This program 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.
This program 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 this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <tqdom.h>
#include <tqlabel.h>
#include <tqpainter.h>
#include <tqtimer.h>
#include <tqtooltip.h>
#include <kcursor.h>
#include <kdatastream.h>
#include <tdelocale.h>
#include <kcombobox.h>
#include <kmimetype.h>
#include <kurl.h>
#include <kurldrag.h>
#include "loader.h"
#include "mrml_creator.h"
#include "mrml_view.h"
using namespace KMrml;
MrmlView::MrmlView( TQWidget *parent, const char *name )
: TQScrollView( parent, name )
{
setStaticBackground( true );
setResizePolicy( Manual );
setHScrollBarMode( AlwaysOff );
enableClipper( true ); // ### test this
m_items.setAutoDelete( true );
connect( Loader::self(), TQT_SIGNAL( finished(const KURL&, const TQByteArray&)),
TQT_SLOT( slotDownloadFinished( const KURL&, const TQByteArray& )));
m_timer = new TQTimer( this );
connect( m_timer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotLayout() ));
// we need a pixmap to be shown when no thumbnail is available for a
// query result image
TQLabel l( i18n( "No thumbnail available" ), 0L );
l.setFixedSize( 80, 80 );
l.setAlignment( WordBreak | AlignCenter );
// l.setFrameStyle( TQLabel::Box | TQLabel::Plain );
// l.setLineWidth( 1 );
l.setPaletteBackgroundColor( TQt::white );
l.setPaletteForegroundColor( TQt::black );
m_unavailablePixmap = TQPixmap::grabWidget( &l );
}
MrmlView::~MrmlView()
{
}
MrmlViewItem * MrmlView::addItem( const KURL& url, const KURL& thumbURL,
const TQString& similarity )
{
bool ok;
double value = similarity.toDouble( &ok );
if ( !ok || value < 0.05 )
return 0L;
return addItem( url, thumbURL, value );
}
MrmlViewItem * MrmlView::addItem( const KURL& url, const KURL& thumbURL,
double similarity )
{
if ( !url.isValid() ) {
tqWarning( "MrmlPart: received malformed URL from query: %s",
url.prettyURL().isNull() ? "(null)" : url.prettyURL().latin1() );
return 0L;
}
// tqDebug("** url: %s", thumbURL.url().latin1());
MrmlViewItem *item = new MrmlViewItem( url, thumbURL, similarity, this );
TQPixmap *pixmap = getPixmap( thumbURL );
if ( pixmap )
item->setPixmap( *pixmap );
m_items.append( item );
m_timer->start( 0, true );
return item;
}
void MrmlView::addRelevanceToQuery( TQDomDocument& document,
TQDomElement& parent )
{
TQPtrListIterator<MrmlViewItem> it( m_items );
for( ; it.current(); ++it ) {
it.current()->createRelevanceElement( document, parent );
}
}
void MrmlView::clear()
{
m_items.clear(); // items are deleted and removed from scrollview
setContentsPos( 0, 0 );
}
TQPixmap * MrmlView::getPixmap( const KURL& url )
{
TQString u = url.url();
TQPixmap *pix = m_pixmapCache.find( u );
if ( pix )
return pix;
if ( url.isLocalFile() ) {
TQPixmap p;
if ( !p.load( url.path() ) )
p = m_unavailablePixmap;
m_pixmapCache.insert( u, p );
return m_pixmapCache.find( u );
}
else { // remote url, download with TDEIO
Loader::self()->requestDownload( url );
}
return 0L;
}
void MrmlView::slotDownloadFinished( const KURL& url, const TQByteArray& data )
{
TQPtrListIterator<MrmlViewItem> it( m_items );
for( ; it.current(); ++it ) {
MrmlViewItem *item = it.current();
if ( item->thumbURL() == url )
{
TQPixmap p;
if ( data.isEmpty() || !p.loadFromData( data ) )
p = m_unavailablePixmap;
m_pixmapCache.insert( url.url(), p );
item->setPixmap( p );
slotLayout();
return;
}
}
}
void MrmlView::stopDownloads()
{
Loader *loader = Loader::self();
TQPtrListIterator<MrmlViewItem> it( m_items );
for( ; it.current(); ++it ) {
MrmlViewItem *item = it.current();
if ( !item->hasRemotePixmap() )
loader->removeDownload( item->url() );
}
}
void MrmlView::slotLayout()
{
int itemWidth = 0;
TQPtrListIterator<MrmlViewItem> it( m_items );
for ( ; it.current(); ++it ) {
itemWidth = TQMAX( itemWidth, it.current()->sizeHint().width() );
}
if ( itemWidth == 0 )
return;
uint itemsPerRow = visibleWidth() / itemWidth;
int margin = (visibleWidth() - (itemsPerRow * itemWidth)) / 2;
int rowHeight = 0;
uint item = 0;
uint y = 5;
// pointing to the first item of a row
TQPtrListIterator<MrmlViewItem> rowIt( m_items );
for ( it.toFirst(); it.current(); ++it ) {
if ( item >= itemsPerRow ) {
item = 0;
y += rowHeight;
rowHeight = 0;
}
if ( item == 0 )
rowIt = it;
rowHeight = TQMAX( rowHeight, it.current()->sizeHint().height() );
addChild( it.current(), margin + item * itemWidth, y );
it.current()->show();
item++;
// resize all items of the current row so they all have the same size
if ( item >= itemsPerRow || it.atLast() )
{
for ( uint i = 0; (i < itemsPerRow && rowIt.current()); i++ )
{
rowIt.current()->resize( itemWidth, rowHeight );
++rowIt;
}
}
}
resizeContents( visibleWidth(), y + rowHeight );
}
void MrmlView::resizeEvent( TQResizeEvent *e )
{
int oldW = visibleWidth();
TQScrollView::resizeEvent( e );
if ( visibleWidth() != oldW )
slotLayout();
}
void MrmlView::saveState( TQDataStream& stream )
{
stream << m_items.count();
TQPtrListIterator<MrmlViewItem> it( m_items );
for( ; it.current(); ++it ) {
stream << *it.current();
}
}
void MrmlView::restoreState( TQDataStream& stream )
{
stopDownloads();
clear();
int count;
stream >> count;
KURL url, thumbURL;
double similarity;
TQ_UINT32 relevance;
MrmlViewItem *item;
for ( int i = 0; i < count; i++ )
{
stream >> url;
stream >> thumbURL;
stream >> similarity;
stream >> relevance;
item = addItem( url, thumbURL, similarity );
if ( item )
item->setRelevance( (MrmlViewItem::Relevance) relevance );
}
}
TQDataStream& KMrml::operator<<( TQDataStream& stream,
const KMrml::MrmlViewItem& item )
{
return stream << item.url()
<< item.thumbURL()
<< item.similarity()
<< static_cast<TQ_UINT32>( item.relevance() );
}
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
MrmlViewItem::MrmlViewItem( const KURL& url, const KURL& thumbURL,
double similarity, MrmlView *view,
const char *name )
: TQFrame( view->viewport() , name ),
m_view( view ),
m_url( url ),
m_thumbURL( thumbURL ),
similarityFullWidth( 120 ), // ###
m_hasRemotePixmap( false )
{
if ( m_similarity != -1 )
m_similarity = TQMAX( 0.0, TQMIN( 1.0, similarity ));
setFrameStyle( Panel | Sunken );
setMouseTracking( true );
m_combo = new KComboBox( this );
TQToolTip::add( m_combo, i18n("You can refine queries by giving feedback about the current result and pressing the Search button again.") );
m_combo->insertItem( i18n("Relevant"), Relevant );
m_combo->insertItem( i18n("Neutral"), Neutral );
m_combo->insertItem( i18n("Irrelevant"), Irrelevant );
m_combo->adjustSize();
m_combo->setCurrentItem( Neutral );
/*
if ( similarity > -1 )
TQToolTip::add( this, TQString::fromLatin1("<qt>%1<br>%1</qt>")
.arg( url )
.arg(i18n("Similarity: %1").arg( TQString::number(similarity))));
else
TQToolTip::add( this, TQString::fromLatin1("<qt>%1</qt>").arg( url ) );
*/
setMinimumSize( 130, 130 ); // ###
}
MrmlViewItem::~MrmlViewItem()
{
}
void MrmlViewItem::setPixmap( const TQPixmap& pix )
{
if ( !m_url.isLocalFile() )
m_hasRemotePixmap = true;
m_pixmap = pix;
adjustSize();
update();
}
void MrmlViewItem::paintEvent( TQPaintEvent *e )
{
TQFrame::paintEvent( e );
if ( !m_pixmap.isNull() ) {
bitBlt( this, pixmapX(), pixmapY(),
&m_pixmap, 0, 0, m_pixmap.width(), m_pixmap.height(),
CopyROP );
}
if ( m_similarity >= 0 ) {
TQPainter p( this );
TQPen pen( colorGroup().highlight(), 1, TQPen::SolidLine );
p.setPen( pen );
int x = margin;
int y = m_combo->y() - similarityHeight - 2;
int w = (int) (similarityFullWidth * m_similarity);
int h = similarityHeight;
p.drawRect( x, y, similarityFullWidth, h );
p.fillRect( x, y, w, h, colorGroup().highlight() );
}
}
void MrmlViewItem::resizeEvent( TQResizeEvent *e )
{
TQFrame::resizeEvent( e );
int y = height() - m_combo->height() - margin;
m_combo->move( width()/2 - m_combo->width()/2, y );
}
TQSize MrmlViewItem::sizeHint() const
{
int w = TQMAX( TQMAX(minimumHeight(), m_combo->width()), m_pixmap.width() );
w += 2 * margin;
int h = m_pixmap.isNull() ? margin : margin + spacing + m_pixmap.height();
h += (m_similarity > -1) ? similarityHeight + spacing : 0;
h += m_combo->height() + margin;
return TQSize( w, h );
}
void MrmlViewItem::mousePressEvent( TQMouseEvent *e )
{
TQFrame::mousePressEvent( e );
pressedPos.setX( 0 );
pressedPos.setY( 0 );
if ( e->button() == Qt::LeftButton || e->button() == Qt::MidButton ) {
if ( hitsPixmap( e->pos() ) )
pressedPos = e->pos();
}
else if ( e->button() == Qt::RightButton && hitsPixmap( e->pos() ) )
emit view()->activated( m_url, e->button() );
}
void MrmlViewItem::mouseMoveEvent( TQMouseEvent *e )
{
if ( hitsPixmap( e->pos() ) ) {
if ( !ownCursor() ) { // nice hacklet :)
setCursor( KCursor::handCursor() );
emit view()->onItem( m_url );
}
}
else {
if ( ownCursor() ) {
unsetCursor();
emit view()->onItem( KURL() );
}
}
if ( (e->state() & Qt::LeftButton) && !pressedPos.isNull() ) {
TQPoint dist = e->pos() - pressedPos;
if ( dist.manhattanLength() > TDEGlobalSettings::dndEventDelay() ) {
// start drag here
KURL::List urls;
// ### support multiple files?
urls.append( m_url );
KURLDrag *drag = new KURLDrag( urls, this );
drag->setPixmap( KMimeType::pixmapForURL( m_url ) );
drag->drag();
}
}
}
void MrmlViewItem::mouseReleaseEvent( TQMouseEvent *e )
{
if ( hitsPixmap( e->pos() )) {
TQPoint dist = e->pos() - pressedPos;
if ( dist.manhattanLength() < TDEGlobalSettings::dndEventDelay() ) {
emit view()->activated( m_url, e->button() );
}
}
}
bool MrmlViewItem::hitsPixmap( const TQPoint& pos ) const
{
if ( m_pixmap.isNull() )
return false;
if ( pos.x() > pixmapX() && pos.x() < pixmapX() + m_pixmap.width() &&
pos.y() > pixmapY() && pos.y() < pixmapY() + m_pixmap.height() )
return true;
return false;
}
void MrmlViewItem::createRelevanceElement( TQDomDocument& document,
TQDomElement& parent )
{
int rel = m_combo->currentItem();
if ( rel == Neutral )
return;
MrmlCreator::createRelevanceElement( document, parent, m_url.url(),
(rel == Relevant) ? MrmlCreator::Relevant : MrmlCreator::Irrelevant );
}
MrmlViewItem::Relevance MrmlViewItem::relevance() const
{
return (Relevance) m_combo->currentItem();
}
void MrmlViewItem::setRelevance( Relevance relevance )
{
m_combo->setCurrentItem( relevance );
}
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
int MrmlViewItemList::compareItems( TQPtrCollection::Item item1,
TQPtrCollection::Item item2 )
{
double s1 = (static_cast<MrmlViewItem*>( item1 ))->similarity();
double s2 = (static_cast<MrmlViewItem*>( item2 ))->similarity();
if ( s1 < s2 )
return 1;
else if ( s1 > s2 )
return -1;
else
return 0;
}
#include "mrml_view.moc"