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.
500 lines
14 KiB
500 lines
14 KiB
/*
|
|
* KSnapshot
|
|
*
|
|
* (c) Richard J. Moore 1997-2002
|
|
* (c) Matthias Ettrich 2000
|
|
* (c) Aaron J. Seigo 2002
|
|
* (c) Nadeem Hasan 2003
|
|
* This adaptation: (c) Boudewijn Rempt 2005
|
|
*
|
|
* Released under the GPL see file LICENSE for details.
|
|
*/
|
|
|
|
#include <kapplication.h>
|
|
#include <klocale.h>
|
|
#include <kimageio.h>
|
|
#include <tdefiledialog.h>
|
|
#include <kimagefilepreview.h>
|
|
#include <kmessagebox.h>
|
|
#include <kdebug.h>
|
|
#include <kprinter.h>
|
|
#include <tdeio/netaccess.h>
|
|
#include <ksavefile.h>
|
|
#include <ktempfile.h>
|
|
|
|
#include <tqbitmap.h>
|
|
#include <tqdragobject.h>
|
|
#include <tqimage.h>
|
|
#include <tqclipboard.h>
|
|
#include <tqvbox.h>
|
|
|
|
#include <kaccel.h>
|
|
#include <knotifyclient.h>
|
|
#include <khelpmenu.h>
|
|
#include <kpopupmenu.h>
|
|
#include <kpushbutton.h>
|
|
#include <kstartupinfo.h>
|
|
|
|
#include <tqcursor.h>
|
|
#include <tqregexp.h>
|
|
#include <tqpainter.h>
|
|
#include <tqpaintdevicemetrics.h>
|
|
#include <tqwhatsthis.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "ksnapshot.h"
|
|
#include "regiongrabber.h"
|
|
#include "ksnapshotwidget.h"
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xatom.h>
|
|
|
|
#include <config.h>
|
|
|
|
#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
|
|
#include <X11/extensions/shape.h>
|
|
#endif
|
|
|
|
#include <kglobal.h>
|
|
|
|
KSnapshot::KSnapshot(TQWidget *parent, const char *name)
|
|
: super(parent, name, false, TQString(), Ok|Cancel)
|
|
{
|
|
grabber = new TQWidget( 0, 0, WStyle_Customize | WX11BypassWM );
|
|
TQ_CHECK_PTR(grabber);
|
|
grabber->move( -1000, -1000 );
|
|
grabber->installEventFilter( this );
|
|
|
|
#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
|
|
int tmp1, tmp2;
|
|
//Check whether the extension is available
|
|
haveXShape = XShapeQueryExtension( tqt_xdisplay(), &tmp1, &tmp2 );
|
|
#endif
|
|
|
|
TQVBox *vbox = makeVBoxMainWidget();
|
|
mainWidget = new KSnapshotWidget( vbox, "mainWidget" );
|
|
TQ_CHECK_PTR(mainWidget);
|
|
|
|
mainWidget->btnSave->hide();
|
|
mainWidget->btnPrint->hide();
|
|
connect(mainWidget, TQT_SIGNAL(startImageDrag()), TQT_SLOT(slotDragSnapshot()));
|
|
|
|
connect( mainWidget, TQT_SIGNAL( newClicked() ), TQT_SLOT( slotGrab() ) );
|
|
connect( mainWidget, TQT_SIGNAL( printClicked() ), TQT_SLOT( slotPrint() ) );
|
|
|
|
grabber->show();
|
|
grabber->grabMouse( waitCursor );
|
|
|
|
snapshot = TQPixmap::grabWindow( tqt_xrootwin() );
|
|
updatePreview();
|
|
grabber->releaseMouse();
|
|
grabber->hide();
|
|
|
|
TDEConfig *conf=TDEGlobal::config();
|
|
conf->setGroup("GENERAL");
|
|
mainWidget->setDelay(conf->readNumEntry("delay",0));
|
|
mainWidget->setMode( conf->readNumEntry( "mode", 0 ) );
|
|
mainWidget->setIncludeDecorations(conf->readBoolEntry("includeDecorations",true));
|
|
|
|
connect( &grabTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( grabTimerDone() ) );
|
|
|
|
KAccel* accel = new KAccel(this);
|
|
TQ_CHECK_PTR(accel);
|
|
accel->insert(KStdAccel::Print, TQT_TQOBJECT(this), TQT_SLOT(slotPrint()));
|
|
accel->insert(KStdAccel::New, TQT_TQOBJECT(this), TQT_SLOT(slotGrab()));
|
|
|
|
accel->insert( "Print2", TQt::Key_P, TQT_TQOBJECT(this), TQT_SLOT(slotPrint()));
|
|
accel->insert( "New2", TQt::Key_N, TQT_TQOBJECT(this), TQT_SLOT(slotGrab()));
|
|
accel->insert( "New3", TQt::Key_Space, TQT_TQOBJECT(this), TQT_SLOT(slotGrab()));
|
|
|
|
mainWidget->btnNew->setFocus();
|
|
|
|
}
|
|
|
|
KSnapshot::~KSnapshot()
|
|
{
|
|
}
|
|
|
|
bool KSnapshot::save( const TQString &filename )
|
|
{
|
|
return save( KURL::fromPathOrURL( filename ));
|
|
}
|
|
|
|
bool KSnapshot::save( const KURL& url )
|
|
{
|
|
TQString type( KImageIO::type(url.path()) );
|
|
if ( type.isNull() )
|
|
type = "PNG";
|
|
|
|
bool ok = false;
|
|
|
|
if ( url.isLocalFile() ) {
|
|
KSaveFile saveFile( url.path() );
|
|
if ( saveFile.status() == 0 ) {
|
|
if ( snapshot.save( saveFile.file(), type.latin1() ) )
|
|
ok = saveFile.close();
|
|
}
|
|
}
|
|
else {
|
|
KTempFile tmpFile;
|
|
tmpFile.setAutoDelete( true );
|
|
if ( tmpFile.status() == 0 ) {
|
|
if ( snapshot.save( tmpFile.file(), type.latin1() ) ) {
|
|
if ( tmpFile.close() )
|
|
ok = TDEIO::NetAccess::upload( tmpFile.name(), url, this );
|
|
}
|
|
}
|
|
}
|
|
|
|
TQApplication::restoreOverrideCursor();
|
|
if ( !ok ) {
|
|
kdWarning() << "KSnapshot was unable to save the snapshot" << endl;
|
|
|
|
TQString caption = i18n("Unable to Save Image");
|
|
TQString text = i18n("KSnapshot was unable to save the image to\n%1.")
|
|
.arg(url.prettyURL());
|
|
KMessageBox::error(this, text, caption);
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
void KSnapshot::slotCopy()
|
|
{
|
|
TQClipboard *cb = TQApplication::clipboard();
|
|
cb->setPixmap( snapshot );
|
|
}
|
|
|
|
void KSnapshot::slotDragSnapshot()
|
|
{
|
|
TQDragObject *drobj = new TQImageDrag(snapshot.convertToImage(), this);
|
|
TQ_CHECK_PTR(drobj);
|
|
drobj->setPixmap(mainWidget->preview());
|
|
drobj->dragCopy();
|
|
}
|
|
|
|
void KSnapshot::slotGrab()
|
|
{
|
|
hide();
|
|
if ( mainWidget->mode() == Region )
|
|
{
|
|
rgnGrab = new RegionGrabber();
|
|
TQ_CHECK_PTR(rgnGrab);
|
|
connect( rgnGrab, TQT_SIGNAL( regionGrabbed( const TQPixmap & ) ),
|
|
TQT_SLOT( slotRegionGrabbed( const TQPixmap & ) ) );
|
|
}
|
|
else
|
|
{
|
|
if ( mainWidget->delay() )
|
|
grabTimer.start( mainWidget->delay() * 1000, true );
|
|
else {
|
|
grabber->show();
|
|
grabber->grabMouse( crossCursor );
|
|
}
|
|
}
|
|
}
|
|
|
|
void KSnapshot::slotPrint()
|
|
{
|
|
KPrinter printer;
|
|
if (snapshot.width() > snapshot.height())
|
|
printer.setOrientation(KPrinter::Landscape);
|
|
else
|
|
printer.setOrientation(KPrinter::Portrait);
|
|
|
|
tqApp->processEvents();
|
|
|
|
if (printer.setup(this, i18n("Print Screenshot")))
|
|
{
|
|
tqApp->processEvents();
|
|
|
|
TQPainter painter(&printer);
|
|
TQPaintDeviceMetrics metrics(painter.device());
|
|
|
|
float w = snapshot.width();
|
|
float dw = w - metrics.width();
|
|
float h = snapshot.height();
|
|
float dh = h - metrics.height();
|
|
bool scale = false;
|
|
|
|
if ( (dw > 0.0) || (dh > 0.0) )
|
|
scale = true;
|
|
|
|
if ( scale ) {
|
|
|
|
TQImage img = snapshot.convertToImage();
|
|
tqApp->processEvents();
|
|
|
|
float newh, neww;
|
|
if ( dw > dh ) {
|
|
neww = w-dw;
|
|
newh = neww/w*h;
|
|
}
|
|
else {
|
|
newh = h-dh;
|
|
neww = newh/h*w;
|
|
}
|
|
|
|
img = img.smoothScale( int(neww), int(newh), TQ_ScaleMin );
|
|
tqApp->processEvents();
|
|
|
|
int x = (metrics.width()-img.width())/2;
|
|
int y = (metrics.height()-img.height())/2;
|
|
|
|
painter.drawImage( x, y, img);
|
|
}
|
|
else {
|
|
int x = (metrics.width()-snapshot.width())/2;
|
|
int y = (metrics.height()-snapshot.height())/2;
|
|
painter.drawPixmap( x, y, snapshot );
|
|
}
|
|
}
|
|
|
|
tqApp->processEvents();
|
|
}
|
|
|
|
void KSnapshot::slotRegionGrabbed( const TQPixmap &pix )
|
|
{
|
|
if ( !pix.isNull() )
|
|
{
|
|
snapshot = pix;
|
|
updatePreview();
|
|
modified = true;
|
|
}
|
|
|
|
delete rgnGrab;
|
|
TQApplication::restoreOverrideCursor();
|
|
show();
|
|
}
|
|
|
|
bool KSnapshot::eventFilter( TQObject* o, TQEvent* e)
|
|
{
|
|
if ( TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(grabber) && e->type() == TQEvent::MouseButtonPress ) {
|
|
TQMouseEvent* me = (TQMouseEvent*) e;
|
|
if ( TQWidget::mouseGrabber() != grabber )
|
|
return false;
|
|
if ( me->button() == Qt::LeftButton )
|
|
performGrab();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void KSnapshot::updatePreview()
|
|
{
|
|
TQImage img = snapshot.convertToImage();
|
|
double r1 = ((double) snapshot.height() ) / snapshot.width();
|
|
if ( r1 * mainWidget->previewWidth() < mainWidget->previewHeight() )
|
|
img = img.smoothScale( mainWidget->previewWidth(),
|
|
int( mainWidget->previewWidth() * r1 ));
|
|
else
|
|
img = img.smoothScale( (int) (((double)mainWidget->previewHeight()) / r1),
|
|
(mainWidget->previewHeight() ) );
|
|
|
|
TQPixmap pm;
|
|
pm.convertFromImage( img );
|
|
mainWidget->setPreview( pm );
|
|
}
|
|
|
|
void KSnapshot::grabTimerDone()
|
|
{
|
|
performGrab();
|
|
KNotifyClient::beep(i18n("The screen has been successfully grabbed."));
|
|
}
|
|
|
|
static
|
|
Window findRealWindow( Window w, int depth = 0 )
|
|
{
|
|
if( depth > 5 )
|
|
return None;
|
|
static Atom wm_state = XInternAtom( tqt_xdisplay(), "WM_STATE", False );
|
|
Atom type;
|
|
int format;
|
|
unsigned long nitems, after;
|
|
unsigned char* prop;
|
|
if( XGetWindowProperty( tqt_xdisplay(), w, wm_state, 0, 0, False, AnyPropertyType,
|
|
&type, &format, &nitems, &after, &prop ) == Success ) {
|
|
if( prop != NULL )
|
|
XFree( prop );
|
|
if( type != None )
|
|
return w;
|
|
}
|
|
Window root, parent;
|
|
Window* children;
|
|
unsigned int nchildren;
|
|
Window ret = None;
|
|
if( XQueryTree( tqt_xdisplay(), w, &root, &parent, &children, &nchildren ) != 0 ) {
|
|
for( unsigned int i = 0;
|
|
i < nchildren && ret == None;
|
|
++i )
|
|
ret = findRealWindow( children[ i ], depth + 1 );
|
|
if( children != NULL )
|
|
XFree( children );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void KSnapshot::performGrab()
|
|
{
|
|
grabber->releaseMouse();
|
|
grabber->hide();
|
|
grabTimer.stop();
|
|
XGrabServer( tqt_xdisplay());
|
|
if ( mainWidget->mode() == WindowUnderCursor ) {
|
|
Window root;
|
|
Window child;
|
|
uint mask;
|
|
int rootX, rootY, winX, winY;
|
|
XQueryPointer( tqt_xdisplay(), tqt_xrootwin(), &root, &child,
|
|
&rootX, &rootY, &winX, &winY,
|
|
&mask);
|
|
if( child == None )
|
|
child = tqt_xrootwin();
|
|
if( !mainWidget->includeDecorations()) {
|
|
Window real_child = findRealWindow( child );
|
|
if( real_child != None ) // test just in case
|
|
child = real_child;
|
|
}
|
|
int x, y;
|
|
unsigned int w, h;
|
|
unsigned int border;
|
|
unsigned int depth;
|
|
XGetGeometry( tqt_xdisplay(), child, &root, &x, &y,
|
|
&w, &h, &border, &depth );
|
|
w += 2 * border;
|
|
h += 2 * border;
|
|
|
|
Window parent;
|
|
Window* children;
|
|
unsigned int nchildren;
|
|
if( XQueryTree( tqt_xdisplay(), child, &root, &parent,
|
|
&children, &nchildren ) != 0 ) {
|
|
if( children != NULL )
|
|
XFree( children );
|
|
int newx, newy;
|
|
Window dummy;
|
|
if( XTranslateCoordinates( tqt_xdisplay(), parent, tqt_xrootwin(),
|
|
x, y, &newx, &newy, &dummy )) {
|
|
x = newx;
|
|
y = newy;
|
|
}
|
|
}
|
|
|
|
snapshot = TQPixmap::grabWindow( tqt_xrootwin(), x, y, w, h );
|
|
|
|
#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
|
|
//No XShape - no work.
|
|
if (haveXShape) {
|
|
TQBitmap mask(w, h);
|
|
//As the first step, get the mask from XShape.
|
|
int count, order;
|
|
XRectangle* rects = XShapeGetRectangles( tqt_xdisplay(), child,
|
|
ShapeBounding, &count, &order);
|
|
//The ShapeBounding region is the outermost shape of the window;
|
|
//ShapeBounding - ShapeClipping is defined to be the border.
|
|
//Since the border area is part of the window, we use bounding
|
|
// to limit our work region
|
|
if (rects) {
|
|
//Create a TQRegion from the rectangles describing the bounding mask.
|
|
TQRegion contents;
|
|
for (int pos = 0; pos < count; pos++)
|
|
contents += TQRegion(rects[pos].x, rects[pos].y,
|
|
rects[pos].width, rects[pos].height);
|
|
XFree(rects);
|
|
|
|
//Create the bounding box.
|
|
TQRegion bbox(0, 0, snapshot.width(), snapshot.height());
|
|
|
|
if( border > 0 ) {
|
|
contents.translate( border, border );
|
|
contents += TQRegion( 0, 0, border, h );
|
|
contents += TQRegion( 0, 0, w, border );
|
|
contents += TQRegion( 0, h - border, w, border );
|
|
contents += TQRegion( w - border, 0, border, h );
|
|
}
|
|
|
|
//Get the masked away area.
|
|
TQRegion maskedAway = bbox - contents;
|
|
TQMemArray<TQRect> maskedAwayRects = maskedAway.rects();
|
|
|
|
//Construct a bitmap mask from the rectangles
|
|
TQPainter p(&mask);
|
|
p.fillRect(0, 0, w, h, TQt::color1);
|
|
for (uint pos = 0; pos < maskedAwayRects.count(); pos++)
|
|
p.fillRect(maskedAwayRects[pos], TQt::color0);
|
|
p.end();
|
|
|
|
snapshot.setMask(mask);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else {
|
|
snapshot = TQPixmap::grabWindow( tqt_xrootwin() );
|
|
}
|
|
XUngrabServer( tqt_xdisplay());
|
|
updatePreview();
|
|
TQApplication::restoreOverrideCursor();
|
|
modified = true;
|
|
// show();
|
|
slotOk();
|
|
}
|
|
|
|
void KSnapshot::setTime(int newTime)
|
|
{
|
|
mainWidget->setDelay(newTime);
|
|
}
|
|
|
|
void KSnapshot::setURL( const TQString &url )
|
|
{
|
|
KURL newURL = KURL::fromPathOrURL( url );
|
|
if ( newURL == filename )
|
|
return;
|
|
|
|
filename = newURL;
|
|
}
|
|
|
|
void KSnapshot::setGrabMode( int m )
|
|
{
|
|
mainWidget->setMode( m );
|
|
}
|
|
|
|
void KSnapshot::slotMovePointer(int x, int y)
|
|
{
|
|
TQCursor::setPos( x, y );
|
|
}
|
|
|
|
void KSnapshot::exit()
|
|
{
|
|
|
|
TDEConfig *conf=TDEGlobal::config();
|
|
conf->setGroup("GENERAL");
|
|
conf->writeEntry("delay",mainWidget->delay());
|
|
conf->writeEntry("mode",mainWidget->mode());
|
|
conf->writeEntry("includeDecorations",mainWidget->includeDecorations());
|
|
KURL url = filename;
|
|
url.setPass( TQString() );
|
|
conf->writePathEntry("filename",url.url());
|
|
|
|
reject();
|
|
}
|
|
|
|
void KSnapshot::slotOk()
|
|
{
|
|
|
|
TDEConfig *conf=TDEGlobal::config();
|
|
conf->setGroup("GENERAL");
|
|
conf->writeEntry("delay",mainWidget->delay());
|
|
conf->writeEntry("mode",mainWidget->mode());
|
|
conf->writeEntry("includeDecorations",mainWidget->includeDecorations());
|
|
KURL url = filename;
|
|
url.setPass( TQString() );
|
|
conf->writePathEntry("filename",url.url());
|
|
|
|
emit screenGrabbed();
|
|
|
|
accept();
|
|
}
|
|
|
|
#include "ksnapshot.moc"
|