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.
konversation/konversation/src/osd.cpp

447 lines
12 KiB

/*
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.
*/
/*
Provides an interface to a plain TQWidget, which is independent of KDE (bypassed to X11)
begin: Fre Sep 26 2003
Copyright (C) 2003 Christian Muehlhaeuser <chris@chris.de>
Copyright (C) 2004 Michael Goettsche <michael.goettsche@kdemail.net>
*/
#include "osd.h"
#include "konversationapplication.h"
#include "common.h"
#include <tqapplication.h>
#include <tqbitmap.h>
#include <tqpainter.h>
#include <tqregexp.h>
#include <dcopclient.h>
#include <tdeapplication.h>
#include <kdebug.h>
#include <tdeglobalsettings.h> //unsetColors()
#include <X11/Xlib.h> //reposition()
OSDWidget::OSDWidget( const TQString &appName, TQWidget *parent, const char *name )
: TQWidget( parent, name, WNoAutoErase | WStyle_Customize | WX11BypassWM | WStyle_StaysOnTop | WStyle_Tool )
, m_appName( appName )
, m_duration( 5000 )
, m_shadow( true )
, m_alignment( Middle )
, m_screen( 0 )
, m_y( MARGIN )
, m_dirty( false )
{
setFocusPolicy( TQ_NoFocus );
setBackgroundMode( NoBackground );
unsetColors();
connect( &timer, TQT_SIGNAL( timeout() ), TQT_SLOT( hide() ) );
connect( &timerMin, TQT_SIGNAL( timeout() ), TQT_SLOT( minReached() ) );
}
void OSDWidget::renderOSDText( const TQString &txt )
{
// Escaped text
TQString text = Konversation::removeIrcMarkup(txt);
static TQBitmap mask;
//This is various spacings and margins, based on the font to look "just right"
const uint METRIC = fontMetrics().width( 'x' );
// Set a sensible maximum size, don't cover the whole desktop or cross the screen
TQSize max = TQApplication::desktop()->screen( m_screen )->size() - TQSize( MARGIN*2 + METRIC*2, 100 );
TQFont titleFont( "Arial", 12, TQFont::Bold );
TQFontMetrics titleFm( titleFont );
// The title cannnot be taller than one line
// AlignAuto = align Arabic to the right, etc.
TQRect titleRect = titleFm.boundingRect( 0, 0, max.width() - METRIC, titleFm.height(), AlignAuto, m_appName );
// The osd cannot be larger than the screen
TQRect textRect = fontMetrics().boundingRect( 0, 0, max.width(), max.height(), AlignAuto | WordBreak, text );
if ( textRect.width() < titleRect.width() )
textRect.setWidth( titleRect.width() );
//this should still be within the screen bounds
textRect.addCoords( 0, 0, METRIC*2, titleRect.height() + METRIC );
osdBuffer.resize( textRect.size() );
mask.resize( textRect.size() );
// Start painting!
TQPainter bufferPainter( &osdBuffer );
TQPainter maskPainter( &mask );
// Draw backing rectangle
const uint xround = (METRIC * 200) / textRect.width();
const uint yround = (METRIC * 200) / textRect.height();
bufferPainter.setPen( TQt::black );
bufferPainter.setBrush( backgroundColor() );
bufferPainter.drawRoundRect( textRect, xround, yround );
bufferPainter.setFont( font() );
const uint w = textRect.width() - 1;
const uint h = textRect.height() - 1;
// Draw the text shadow
if ( m_shadow )
{
bufferPainter.setPen( backgroundColor().dark( 175 ) );
bufferPainter.drawText( METRIC + 3, (METRIC/2) + titleFm.height() + 1, w, h, AlignLeft | WordBreak, text );
}
// Draw the text
bufferPainter.setPen( foregroundColor() );
bufferPainter.drawText( METRIC, (METRIC/2) + titleFm.height() - 1, w, h, AlignLeft | WordBreak, text );
// Draw the title text
bufferPainter.setFont( titleFont );
bufferPainter.drawText( METRIC * 2, (METRIC/2), w, h, AlignLeft, m_appName );
// Masking for transparency
mask.fill( TQt::black );
maskPainter.setBrush( TQt::white );
maskPainter.drawRoundRect( textRect, xround, yround );
setMask( mask );
//do last to reduce noticeable change when showing multiple OSDs in succession
reposition( textRect.size() );
m_currentText = text;
m_dirty = false;
update();
}
// slot
void OSDWidget::showOSD( const TQString &text, bool preemptive )
{
if ( isEnabled() && !text.isEmpty() )
{
TQString plaintext = text.copy();
plaintext.replace(TQRegExp("</?(?:font|a|b|i)\\b[^>]*>"), TQString(""));
plaintext.replace(TQString("&lt;"), TQString("<"));
plaintext.replace(TQString("&gt;"), TQString(">"));
plaintext.replace(TQString("&amp;"), TQString("&"));
if ( preemptive || !timerMin.isActive() )
{
m_currentText = plaintext;
m_dirty = true;
show();
}
else textBuffer.append( plaintext ); //queue
}
}
void OSDWidget::minReached() //SLOT
{
if ( !textBuffer.isEmpty() )
{
renderOSDText( textBuffer.front() );
textBuffer.pop_front();
if( m_duration )
//timerMin is still running
timer.start( m_duration, true );
}
else timerMin.stop();
}
void OSDWidget::setDuration( int ms )
{
m_duration = ms;
if( !m_duration ) timer.stop();
}
void OSDWidget::setFont( TQFont newFont )
{
TQWidget::setFont( newFont );
refresh();
}
void OSDWidget::setShadow( bool shadow )
{
m_shadow = shadow;
refresh();
}
void OSDWidget::setTextColor( const TQColor &newColor )
{
setPaletteForegroundColor( newColor );
refresh();
}
void OSDWidget::setBackgroundColor( const TQColor &newColor )
{
setPaletteBackgroundColor( newColor );
refresh();
}
void OSDWidget::unsetColors()
{
setPaletteForegroundColor( TDEGlobalSettings::activeTextColor() );
setPaletteBackgroundColor( TDEGlobalSettings::activeTitleColor() );
refresh();
}
void OSDWidget::setOffset( int /*x*/, int y )
{
//m_offset = TQPoint( x, y );
m_y = y;
reposition();
}
void OSDWidget::setAlignment( Alignment a )
{
m_alignment = a;
reposition();
}
void OSDWidget::setScreen( uint screen )
{
const uint n = TQApplication::desktop()->numScreens();
m_screen = (screen >= n) ? n-1 : (int)screen;
reposition();
}
bool OSDWidget::event( TQEvent *e )
{
switch( e->type() )
{
case TQEvent::Paint:
bitBlt( this, 0, 0, &osdBuffer );
return true;
default:
return TQWidget::event( e );
}
}
void OSDWidget::mousePressEvent( TQMouseEvent* )
{
hide();
emit hidden();
}
void OSDWidget::show()
{
// Don't show the OSD widget when the desktop is locked
if ( isKDesktopLockRunning() == Locked )
{
minReached(); // don't queue the message
return;
}
if ( m_dirty ) renderOSDText( m_currentText );
TQWidget::show();
if ( m_duration ) //duration 0 -> stay forever
{
timer.start( m_duration, true ); //calls hide()
timerMin.start( 150 ); //calls minReached()
}
}
void OSDWidget::refresh()
{
if ( isVisible() )
{
//we need to update the buffer
renderOSDText( m_currentText );
}
else m_dirty = true; //ensure we are re-rendered before we are shown
}
void OSDWidget::reposition( TQSize newSize )
{
if( !newSize.isValid() ) newSize = size();
TQPoint newPos( MARGIN, m_y );
const TQRect screen = TQApplication::desktop()->screenGeometry( m_screen );
//TODO m_y is the middle of the OSD, and don't exceed screen margins
switch ( m_alignment )
{
case Left:
break;
case Right:
newPos.rx() = screen.width() - MARGIN - newSize.width();
break;
case Center:
newPos.ry() = (screen.height() - newSize.height()) / 2;
//FALL THROUGH
case Middle:
newPos.rx() = (screen.width() - newSize.width()) / 2;
break;
}
//ensure we don't dip below the screen
if( newPos.y()+newSize.height() > screen.height()-MARGIN ) newPos.ry() = screen.height()-MARGIN-newSize.height();
// correct for screen position
newPos += screen.topLeft();
//ensure we are painted before we move
if( isVisible() ) paintEvent( (TQPaintEvent*)0 );
//fancy X11 move+resize, reduces visual artifacts
XMoveResizeWindow( x11Display(), winId(), newPos.x(), newPos.y(), newSize.width(), newSize.height() );
}
////// OSDPreviewWidget below /////////////////////
#include <kcursor.h> //previewWidget
#include <tdelocale.h>
OSDPreviewWidget::OSDPreviewWidget( const TQString &appName, TQWidget *parent, const char *name )
: OSDWidget( appName, parent, name )
, m_dragging( false )
{
m_currentText = i18n( "OSD Preview - drag to reposition" );
m_duration = 0;
}
void OSDPreviewWidget::mousePressEvent( TQMouseEvent *event )
{
m_dragOffset = event->pos();
if ( event->button() == Qt::LeftButton && !m_dragging )
{
grabMouse( KCursor::sizeAllCursor() );
m_dragging = true;
}
}
void OSDPreviewWidget::mouseReleaseEvent( TQMouseEvent * /*event*/ )
{
if ( m_dragging )
{
m_dragging = false;
releaseMouse();
// compute current Position && offset
TQDesktopWidget *desktop = TQApplication::desktop();
int currentScreen = desktop->screenNumber( pos() );
if ( currentScreen != -1 )
{
// set new data
m_screen = currentScreen;
m_y = TQWidget::y();
emit positionChanged();
}
}
}
void OSDPreviewWidget::mouseMoveEvent( TQMouseEvent *e )
{
if ( m_dragging && this == mouseGrabber() )
{
const TQRect screen = TQApplication::desktop()->screenGeometry( m_screen );
const uint hcenter = screen.width() / 2;
const uint eGlobalPosX = e->globalPos().x() - screen.left();
const uint snapZone = screen.width() / 8;
TQPoint destination = e->globalPos() - m_dragOffset - screen.topLeft();
int maxY = screen.height() - height() - MARGIN;
if( destination.y() < MARGIN ) destination.ry() = MARGIN;
if( destination.y() > maxY ) destination.ry() = maxY;
if( eGlobalPosX < (hcenter-snapZone) )
{
m_alignment = Left;
destination.rx() = MARGIN;
}
else if( eGlobalPosX > (hcenter+snapZone) )
{
m_alignment = Right;
destination.rx() = screen.width() - MARGIN - width();
}
else
{
const uint eGlobalPosY = e->globalPos().y() - screen.top();
const uint vcenter = screen.height()/2;
destination.rx() = hcenter - width()/2;
if( eGlobalPosY >= (vcenter-snapZone) && eGlobalPosY <= (vcenter+snapZone) )
{
m_alignment = Center;
destination.ry() = vcenter - height()/2;
}
else m_alignment = Middle;
}
destination += screen.topLeft();
move( destination );
}
}
// the code was taken from pilotDaemon.cpp in KPilot
// static
OSDWidget::KDesktopLockStatus OSDWidget::isKDesktopLockRunning()
{
if (!Preferences::oSDCheckDesktopLock())
return NotLocked;
DCOPClient *dcopptr = TDEApplication::kApplication()->dcopClient();
// Can't tell, very weird
if (!dcopptr || !dcopptr->isAttached())
{
kdWarning() << k_funcinfo << ": Could not make DCOP connection." << endl;
return DCOPError;
}
TQByteArray data,returnValue;
TQCString returnType;
if (!dcopptr->call("kdesktop","KScreensaverIface","isBlanked()",
data,returnType,returnValue,true))
{
// KDesktop is not running. Maybe we are in a KDE4 desktop...
kdDebug() << k_funcinfo << ": Check for screensaver failed." << endl;
return DCOPError;
}
if (returnType == "bool")
{
bool b;
TQDataStream reply(returnValue,IO_ReadOnly);
reply >> b;
return (b ? Locked : NotLocked);
}
else
{
kdWarning() << k_funcinfo << ": Strange return value from screensaver. "
<< "Assuming screensaver is active." << endl;
// Err on the side of safety.
return Locked;
}
}
#include "osd.moc"