1/*
2 * Copyright (C) 1997-2008 Richard J. Moore <rich@kde.org>
3 * Copyright (C) 2000 Matthias Ettrich <ettrich@troll.no>
4 * Copyright (C) 2002 Aaron J. Seigo <aseigo@kde.org>
5 * Copyright (C) 2003 Nadeem Hasan <nhasan@kde.org>
6 * Copyright (C) 2004 Bernd Brandstetter <bbrand@freenet.de>
7 * Copyright (C) 2006 Urs Wolfer <uwolfer @ kde.org>
8 * Copyright (C) 2010 Martin Gräßlin <kde@martin-graesslin.com>
9 * Copyright (C) 2010, 2011 Pau Garcia i Quiles <pgquiles@elpauer.org>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 * Boston, MA 02110-1301, USA.
25 */
26
27#include "ksnapshot.h"
28
29#include <QClipboard>
30#include <QShortcut>
31#include <QMenu>
32#include <QDesktopWidget>
33#include <QVarLengthArray>
34#include <QCloseEvent>
35#include <QDrag>
36#include <QMouseEvent>
37#include <QPainter>
38#include <QtCore/QXmlStreamReader>
39#include <QtDBus/QDBusConnection>
40#include <QtDBus/QDBusConnectionInterface>
41#include <QtDBus/QDBusInterface>
42
43#include <klocale.h>
44
45#include <KDebug>
46#include <kglobal.h>
47#include <kicon.h>
48#include <kimageio.h>
49#include <kcomponentdata.h>
50#include <kfiledialog.h>
51#include <kmessagebox.h>
52#include <kio/netaccess.h>
53#include <ksavefile.h>
54#include <kstandardshortcut.h>
55#include <ktemporaryfile.h>
56#include <knotification.h>
57#include <khelpmenu.h>
58#include <kmenu.h>
59#include <kmimetypetrader.h>
60#include <kopenwithdialog.h>
61#include <krun.h>
62#include <kstandarddirs.h>
63#include <kstartupinfo.h>
64#include <kvbox.h>
65#include <qdebug.h>
66
67#include "regiongrabber.h"
68#include "freeregiongrabber.h"
69#include "windowgrabber.h"
70#include "ksnapshotpreview.h"
71#include "ui_ksnapshotwidget.h"
72
73#ifdef KIPI_FOUND
74#include <libkipi/plugin.h>
75#include <libkipi/version.h>
76#include "kipiinterface.h"
77#include <KAction>
78#endif
79
80#ifdef HAVE_X11_EXTENSIONS_XFIXES_H
81#include <X11/extensions/Xfixes.h>
82#include <X11/Xatom.h>
83#include <QX11Info>
84#endif
85
86class KSnapshotWidget : public QWidget, public Ui::KSnapshotWidget
87{
88 public:
89 KSnapshotWidget(QWidget *parent = 0)
90 : QWidget(parent)
91 {
92 setupUi(this);
93 btnNew->setIcon(KIcon("ksnapshot"));
94 }
95};
96
97KSnapshot::KSnapshot(QWidget *parent, KSnapshotObject::CaptureMode mode )
98 : KDialog(parent), KSnapshotObject(), modified(true), savedPosition(QPoint(-1, -1)), haveXFixes(false)
99{
100 // TEMPORARY Make sure "untitled" enters the string freeze for 4.6,
101 // as explained in http://lists.kde.org/?l=kde-graphics-devel&m=128942871430175&w=2
102 const QString untitled = QString(i18n("untitled"));
103
104 setCaption( "" );
105 showButtonSeparator( true );
106 setButtons(Help | Apply | User1 | User2);
107 setButtonGuiItem(Apply, KStandardGuiItem::saveAs());
108 setButtonGuiItem(User1, KGuiItem(i18n("Copy"), "edit-copy"));
109 setButtonGuiItem(User2, KGuiItem(i18n("Send To..."), "document-open"));
110 setDefaultButton(Apply);
111 grabber = new QWidget( 0, Qt::X11BypassWindowManagerHint );
112
113 // TODO X11 (Xinerama and Twinview, actually) and Windows use different coordinates for the two monitors case
114 //
115 // On Windows, there are two displays. The origin (0, 0) ('o') is the top left of display 1. If display 2 is to the left, then coordinates in display 2 are negative:
116 // .-------.
117 // | |o-----.
118 // | 2 | |
119 // | | 1 |
120 // ._______.._____.
121 //
122 // On Xinerama and Twinview, there is only one display and two screens. The origin (0, 0) ('o') is the top left of the display:
123 // o-------.
124 // | |.-----.
125 // | 2 | |
126 // | | 1 |
127 // ._______.._____.
128 //
129 // Instead of moving to (-10000, -10000), we should compute how many displays are and make sure we move to somewhere out of the total coordinates.
130 // - Windows: use GetSystemMetrics ( http://msdn.microsoft.com/en-us/library/ms724385(v=vs.85).aspx )
131
132 // If moving to a negative position, we need to count the size of the dialog; moving to a positive position avoids having to compute the size of the dialog
133
134 grabber->move( -10000, -10000 ); // FIXME Read above
135
136 grabber->installEventFilter( this );
137
138 KStartupInfo::appStarted();
139
140 KVBox *vbox = new KVBox( this );
141 vbox->setSpacing( spacingHint() );
142 setMainWidget( vbox );
143
144 mainWidget = new KSnapshotWidget( vbox );
145
146 connect(mainWidget->lblImage, SIGNAL(startDrag()), SLOT(slotDragSnapshot()));
147 connect(mainWidget->btnNew, SIGNAL(clicked()), SLOT(slotGrab()));
148 connect(this, SIGNAL(applyClicked()), SLOT(slotSaveAs()));
149 connect(this, SIGNAL(user1Clicked()), SLOT(slotCopy()));
150 connect(mainWidget->comboMode, SIGNAL(activated(int)), SLOT(slotModeChanged(int)));
151
152 if (qApp->desktop()->numScreens() < 2) {
153 mainWidget->comboMode->removeItem(CurrentScreen);
154 }
155
156 openMenu = new QMenu(this);
157 button(User2)->setMenu(openMenu);
158 connect(openMenu, SIGNAL(aboutToShow()), this, SLOT(slotPopulateOpenMenu()));
159 connect(openMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotOpen(QAction*)));
160
161 mainWidget->spinDelay->setSuffix(ki18np(" second", " seconds"));
162
163 grabber->show();
164 grabber->grabMouse();
165
166 KConfigGroup conf(KGlobal::config(), "GENERAL");
167
168#ifdef KIPI_FOUND
169#if(KIPI_VERSION >= 0x020000)
170 mPluginLoader = new KIPI::PluginLoader();
171 mPluginLoader->setInterface(new KIPIInterface(this));
172 mPluginLoader->init();
173#else
174 mPluginLoader = new KIPI::PluginLoader(QStringList(), new KIPIInterface(this), "");
175#endif
176#endif
177
178#ifdef HAVE_X11_EXTENSIONS_XFIXES_H
179 {
180 int tmp1, tmp2;
181 //Check whether the XFixes extension is available
182 Display *dpy = QX11Info::display();
183 if (!XFixesQueryExtension( dpy, &tmp1, &tmp2 )) {
184 mainWidget->cbIncludePointer->hide();
185 mainWidget->lblIncludePointer->hide();
186 } else {
187 haveXFixes = true;
188 }
189
190 // actually not depending on XFixes, but to simplify the ifdefs put here
191 // we can safely assume that XFixes is present for this functionality
192 // it's supposed to prevent that KWin animates the window in the compositor
193 // and XFixes is a requirement for the compositor. So if XFixes is not present
194 // KWin cannot be compiled at all.
195 Atom atom = XInternAtom(dpy, "_KDE_NET_WM_SKIP_CLOSE_ANIMATION", False);
196 long d = 1;
197 XChangeProperty(dpy, winId(), atom, XA_CARDINAL, 32,
198 PropModeReplace, (unsigned char *) &d, 1);
199 }
200#elif !defined(Q_WS_WIN)
201 mainWidget->cbIncludePointer->hide();
202 mainWidget->lblIncludePointer->hide();
203#endif
204 setIncludePointer(conf.readEntry("includePointer", false));
205 setMode( conf.readEntry("mode", 0) );
206
207 // check if kwin screenshot effect is available
208 includeAlpha = false;
209 if ( QDBusConnection::sessionBus().interface()->isServiceRegistered( "org.kde.kwin" ) ) {
210 QDBusInterface kwinInterface( "org.kde.kwin", "/", "org.freedesktop.DBus.Introspectable" );
211 QDBusReply<QString> reply = kwinInterface.call( "Introspect" );
212 if ( reply.isValid() ) {
213 QXmlStreamReader xml( reply.value() );
214 while ( !xml.atEnd() ) {
215 xml.readNext();
216 if ( xml.tokenType() == QXmlStreamReader::StartElement &&
217 xml.name().toString() == "node" ) {
218 if ( xml.attributes().value( "name" ).toString() == "Screenshot" ) {
219 includeAlpha = true;
220 break;
221 }
222 }
223 }
224 }
225 }
226
227 kDebug() << "Mode = " << mode;
228 if ( mode == KSnapshotObject::FullScreen ) {
229 snapshot = QPixmap::grabWindow( QApplication::desktop()->winId() );
230#ifdef HAVE_X11_EXTENSIONS_XFIXES_H
231 if ( haveXFixes && includePointer() )
232 grabPointerImage(0, 0);
233#endif
234 }
235 else if ( mode == KSnapshotObject::CurrentScreen ) {
236 kDebug() << "Desktop Geom = " << QApplication::desktop()->geometry();
237 QDesktopWidget *desktop = QApplication::desktop();
238 int screenId = desktop->screenNumber( QCursor::pos() );
239 kDebug() << "Screenid = " << screenId;
240 QRect geom = desktop->screenGeometry( screenId );
241 kDebug() << "Geometry = " << screenId;
242 snapshot = QPixmap::grabWindow( desktop->winId(),
243 geom.x(), geom.y(), geom.width(), geom.height() );
244 }
245 else {
246 setMode( mode );
247 switch(mode)
248 {
249 case KSnapshotObject::WindowUnderCursor:
250 {
251 setIncludeDecorations( true );
252 performGrab();
253 break;
254 }
255 case KSnapshotObject::ChildWindow:
256 {
257 slotGrab();
258 break;
259 }
260 case KSnapshotObject::Region:
261 {
262 grabRegion();
263 break;
264 }
265 case KSnapshotObject::FreeRegion:
266 {
267 grabFreeRegion();
268 break;
269 }
270 default:
271 break;
272 }
273 }
274
275 //When we use argument to take snapshot we mustn't hide it.
276 if (mode != KSnapshotObject::ChildWindow) {
277 grabber->releaseMouse();
278 grabber->hide();
279 }
280
281 setDelay( conf.readEntry("delay", 0) );
282 setIncludeDecorations(conf.readEntry("includeDecorations",true));
283 filename = KUrl( conf.readPathEntry( "filename", QDir::currentPath()+'/'+i18n("snapshot")+"1.png" ));
284
285 connect( &grabTimer, SIGNAL(timeout()), this, SLOT(grabTimerDone()) );
286 connect( &updateTimer, SIGNAL(timeout()), this, SLOT(updatePreview()) );
287 QTimer::singleShot( 0, this, SLOT(updateCaption()) );
288
289 KHelpMenu *helpMenu = new KHelpMenu(this, KGlobal::mainComponent().aboutData(), true);
290 setButtonMenu( Help, helpMenu->menu() );
291#if 0
292 accel->insert( "QuickSave", i18n("Quick Save Snapshot &As..."),
293 i18n("Save the snapshot to the file specified by the user without showing the file dialog."),
294 Qt::CTRL+Qt::SHIFT+Qt::Key_S, this, SLOT(slotSave()));
295 accel->insert( "SaveAs", i18n("Save Snapshot &As..."),
296 i18n("Save the snapshot to the file specified by the user."),
297 Qt::CTRL+Qt::Key_A, this, SLOT(slotSaveAs()));
298#endif
299
300 new QShortcut( KStandardShortcut::shortcut( KStandardShortcut::Quit ).primary(), this, SLOT(reject()));
301
302 new QShortcut( Qt::Key_Q, this, SLOT(slotSave()));
303
304 new QShortcut( KStandardShortcut::shortcut( KStandardShortcut::Copy ).primary(), button(User1), SLOT(animateClick()));
305
306 new QShortcut( KStandardShortcut::shortcut( KStandardShortcut::Save ).primary(), button(Apply), SLOT(animateClick()));
307 new QShortcut( Qt::Key_S, button(Apply), SLOT(animateClick()));
308
309 new QShortcut( KStandardShortcut::shortcut( KStandardShortcut::New ).primary(), mainWidget->btnNew, SLOT(animateClick()) );
310 new QShortcut( Qt::Key_N, mainWidget->btnNew, SLOT(animateClick()) );
311 new QShortcut( Qt::Key_Space, mainWidget->btnNew, SLOT(animateClick()) );
312
313 mainWidget->btnNew->setFocus();
314 setInitialSize(QSize(250, 500));
315
316 KConfigGroup cg(KGlobal::config(), "MainWindow");
317 restoreDialogSize(cg);
318}
319
320KSnapshot::~KSnapshot()
321{
322 delete mainWidget;
323}
324
325void KSnapshot::resizeEvent( QResizeEvent * )
326{
327 updateTimer.setSingleShot( true );
328 updateTimer.start( 200 );
329}
330
331void KSnapshot::slotSave()
332{
333 // Make sure the name is not already being used
334 while(KIO::NetAccess::exists( filename, KIO::NetAccess::DestinationSide, this )) {
335 autoincFilename();
336 }
337
338 if ( save(filename, this) ) {
339 modified = false;
340 autoincFilename();
341 updateCaption();
342 }
343}
344
345void KSnapshot::slotSaveAs()
346{
347 QStringList mimetypes = KImageIO::mimeTypes( KImageIO::Writing );
348
349 // Make sure the name is not already being used
350 while(KIO::NetAccess::exists( filename, KIO::NetAccess::DestinationSide, this )) {
351 autoincFilename();
352 }
353
354 QPointer<KFileDialog> dlg = new KFileDialog( filename.url(), mimetypes.join(" "), this);
355
356 dlg->setOperationMode( KFileDialog::Saving );
357 dlg->setCaption( i18n("Save As") );
358 dlg->setSelection( filename.url() );
359
360 if ( !dlg->exec() ) {
361 delete dlg;
362 return;
363 }
364
365 KUrl url = dlg->selectedUrl();
366 if ( !url.isValid() ){
367 delete dlg;
368 return;
369 }
370
371 if ( save(url,this) ) {
372 filename = url;
373 modified = false;
374 autoincFilename();
375 updateCaption();
376 }
377
378 delete dlg;
379}
380
381void KSnapshot::slotCopy()
382{
383 QClipboard *cb = QApplication::clipboard();
384 cb->setPixmap( snapshot );
385}
386
387void KSnapshot::slotDragSnapshot()
388{
389 QDrag *drag = new QDrag(this);
390
391 drag->setMimeData(new QMimeData);
392 drag->mimeData()->setImageData(snapshot);
393 drag->mimeData()->setData("application/x-kde-suggestedfilename", filename.fileName().toUtf8());
394 drag->setPixmap(preview());
395 QList<QUrl> urls;
396 urls << urlToOpen();
397 drag->mimeData()->setUrls(urls);
398 drag->start();
399}
400
401void KSnapshot::slotGrab()
402{
403 savedPosition = pos();
404 hide();
405
406 if (delay()) {
407 //kDebug() << "starting timer with time of" << delay();
408 grabTimer.start(delay());
409 }
410 else {
411 QTimer::singleShot(0, this, SLOT(startUndelayedGrab()));
412 }
413}
414
415void KSnapshot::startUndelayedGrab()
416{
417 if (mode() == Region) {
418 grabRegion();
419 }
420 else if ( mode() == FreeRegion ) {
421 grabFreeRegion();
422 }
423 else {
424 grabber->show();
425 grabber->grabMouse(Qt::CrossCursor);
426 }
427}
428
429KUrl KSnapshot::urlToOpen(bool *isTempfile)
430{
431 if (isTempfile) {
432 *isTempfile = false;
433 }
434
435 if (!modified && filename.isValid())
436 {
437 return filename;
438 }
439
440 const QString fileopen = KStandardDirs::locateLocal("tmp", filename.fileName());
441
442 if (saveEqual(fileopen, this))
443 {
444 if (isTempfile) {
445 *isTempfile = true;
446 }
447
448 return fileopen;
449 }
450
451 return KUrl();
452}
453
454void KSnapshot::slotOpen(const QString& application)
455{
456 KUrl url = urlToOpen();
457 if (!url.isValid())
458 {
459 return;
460 }
461
462 KUrl::List list;
463 list.append(url);
464 KRun::run(application, list, this);
465}
466
467void KSnapshot::slotOpen(QAction* action)
468{
469 KSnapshotServiceAction* serviceAction =
470 qobject_cast<KSnapshotServiceAction*>(action);
471
472 if (!serviceAction)
473 {
474 return;
475 }
476
477 bool isTempfile = false;
478 KUrl url = urlToOpen(&isTempfile);
479 if (!url.isValid())
480 {
481 return;
482 }
483
484 KUrl::List list;
485 list.append(url);
486
487 KService::Ptr service = serviceAction->service;
488 if (!service)
489 {
490 QPointer<KOpenWithDialog> dlg = new KOpenWithDialog(list, this);
491 if (!dlg->exec())
492 {
493 delete dlg;
494 return;
495 }
496
497 service = dlg->service();
498
499 if (!service && !dlg->text().isEmpty())
500 {
501 KRun::run(dlg->text(), list, this);
502 delete dlg;
503 return;
504 }
505 delete dlg;
506 }
507
508 // we have an action with a service, run it!
509 KRun::run(*service, list, this, isTempfile);
510}
511
512void KSnapshot::slotPopulateOpenMenu()
513{
514 QList<QAction*> currentActions = openMenu->actions();
515 foreach (QAction* currentAction, currentActions)
516 {
517 openMenu->removeAction(currentAction);
518 currentAction->deleteLater();
519 }
520
521 const KService::List services = KMimeTypeTrader::self()->query("image/png");
522 QMap<QString, KService::Ptr> apps;
523
524 foreach (const KService::Ptr &service, services)
525 {
526 apps.insert(service->name(), service);
527 }
528
529 foreach (const KService::Ptr &service, apps)
530 {
531 QString name = service->name().replace( '&', "&&" );
532 openMenu->addAction(new KSnapshotServiceAction(service,
533 KIcon(service->icon()),
534 name, this));
535 }
536
537
538#ifdef KIPI_FOUND
539 KIPI::PluginLoader::PluginList pluginList = mPluginLoader->pluginList();
540
541 Q_FOREACH(KIPI::PluginLoader::Info* pluginInfo, pluginList) {
542 if (!pluginInfo->shouldLoad()) {
543 continue;
544 }
545 KIPI::Plugin* plugin = pluginInfo->plugin();
546 if (!plugin) {
547 kWarning() << "Plugin from library" << pluginInfo->library() << "failed to load";
548 continue;
549 }
550
551 plugin->setup(this);
552
553 QList<KAction*> actions = plugin->actions();
554 QSet<KAction*> exportActions;
555 Q_FOREACH(KAction* action, actions) {
556 KIPI::Category category = plugin->category(action);
557 if(category == KIPI::ExportPlugin) {
558 exportActions << action;
559 } else if (category == KIPI::ImagesPlugin) {
560 // Horrible hack. Why are the print images and the e-mail images plugins in the same category as rotate and edit metadata!?
561 if( pluginInfo->library().contains("kipiplugin_printimages") || pluginInfo->library().contains("kipiplugin_sendimages")) {
562 exportActions << action;
563 }
564 }
565 }
566
567 Q_FOREACH(KAction* action, exportActions) {
568 openMenu->addAction(action);
569 }
570
571// FIXME: Port
572// plugin->actionCollection()->readShortcutSettings();
573 }
574#endif
575
576 openMenu->addSeparator();
577 KService::Ptr none;
578 openMenu->addAction(new KSnapshotServiceAction(none,
579 i18n("Other Application..."),
580 this));
581}
582
583void KSnapshot::slotRegionGrabbed( const QPixmap &pix )
584{
585 if ( !pix.isNull() )
586 {
587 snapshot = pix;
588 updatePreview();
589 modified = true;
590 updateCaption();
591 }
592
593 if( mode() == KSnapshotObject::Region )
594 {
595 rgnGrab->deleteLater();
596 }
597 else if( mode() == KSnapshotObject::FreeRegion ) {
598 freeRgnGrab->deleteLater();
599 }
600
601 QApplication::restoreOverrideCursor();
602 show();
603}
604
605void KSnapshot::slotRegionUpdated( const QRect &selection )
606{
607 lastRegion = selection;
608}
609
610void KSnapshot::slotFreeRegionUpdated( const QPolygon &selection )
611{
612 lastFreeRegion = selection;
613}
614
615void KSnapshot::slotWindowGrabbed( const QPixmap &pix )
616{
617 if ( !pix.isNull() )
618 {
619 snapshot = pix;
620 updatePreview();
621 modified = true;
622 updateCaption();
623 }
624
625 QApplication::restoreOverrideCursor();
626 show();
627}
628
629void KSnapshot::slotScreenshotReceived( qulonglong handle )
630{
631#ifdef Q_WS_X11
632 slotWindowGrabbed( QPixmap::fromX11Pixmap( handle ) );
633#endif
634}
635
636void KSnapshot::closeEvent( QCloseEvent * e )
637{
638 KConfigGroup conf(KGlobal::config(), "GENERAL");
639 conf.writeEntry("delay", delay());
640 conf.writeEntry("mode", mode());
641 conf.writeEntry("includeDecorations", includeDecorations());
642 conf.writeEntry("includePointer", includePointer());
643
644 KConfigGroup cg(KGlobal::config(), "MainWindow");
645 saveDialogSize(cg);
646
647 KUrl url = filename;
648 url.setPass(QString::null); //krazy:exclude=nullstrassign for old broken gcc
649 conf.writePathEntry("filename", url.url());
650
651 conf.sync();
652 e->accept();
653}
654
655bool KSnapshot::eventFilter( QObject* o, QEvent* e)
656{
657 if ( o == grabber && e->type() == QEvent::MouseButtonPress ) {
658 QMouseEvent* me = (QMouseEvent*) e;
659 if ( QWidget::mouseGrabber() != grabber )
660 return false;
661 if ( me->button() == Qt::LeftButton )
662 performGrab();
663 }
664 return false;
665}
666
667void KSnapshot::updatePreview()
668{
669 setPreview( snapshot );
670}
671
672void KSnapshot::grabRegion()
673{
674 rgnGrab = new RegionGrabber(lastRegion);
675 connect( rgnGrab, SIGNAL(regionGrabbed(QPixmap)),
676 SLOT(slotRegionGrabbed(QPixmap)) );
677 connect( rgnGrab, SIGNAL(regionUpdated(QRect)),
678 SLOT(slotRegionUpdated(QRect)) );
679
680}
681
682void KSnapshot::grabFreeRegion()
683{
684 freeRgnGrab = new FreeRegionGrabber(lastFreeRegion);
685 connect( freeRgnGrab, SIGNAL(freeRegionGrabbed(QPixmap)),
686 SLOT(slotRegionGrabbed(QPixmap)) );
687 connect( freeRgnGrab, SIGNAL(freeRegionUpdated(QPolygon)),
688 SLOT(slotFreeRegionUpdated(QPolygon)) );
689
690}
691
692void KSnapshot::grabTimerDone()
693{
694 if ( mode() == Region ) {
695 grabRegion();
696 }
697 else if ( mode() == FreeRegion ) {
698 grabFreeRegion();
699 }
700 else {
701 performGrab();
702 }
703 KNotification::beep(i18n("The screen has been successfully grabbed."));
704}
705
706void KSnapshot::performGrab()
707{
708 int x = 0;
709 int y = 0;
710
711 grabber->releaseMouse();
712 grabber->hide();
713 grabTimer.stop();
714
715 title.clear();
716 windowClass.clear();
717
718 if ( mode() == ChildWindow ) {
719 WindowGrabber wndGrab;
720 connect( &wndGrab, SIGNAL(windowGrabbed(QPixmap)),
721 SLOT(slotWindowGrabbed(QPixmap)) );
722 wndGrab.exec();
723 QPoint offset = wndGrab.lastWindowPosition();
724 x = offset.x();
725 y = offset.y();
726 qDebug() << "last window position is" << offset;
727 }
728 else if ( mode() == WindowUnderCursor ) {
729 if ( includeAlpha ) {
730 // use kwin effect
731 QDBusConnection::sessionBus().connect("org.kde.kwin", "/Screenshot",
732 "org.kde.kwin.Screenshot", "screenshotCreated",
733 this, SLOT(slotScreenshotReceived(qulonglong)));
734 QDBusInterface interface( "org.kde.kwin", "/Screenshot", "org.kde.kwin.Screenshot" );
735 int mask = 0;
736 if ( includeDecorations() )
737 {
738 mask |= 1 << 0;
739 }
740 if ( includePointer() )
741 {
742 mask |= 1 << 1;
743 }
744 interface.call( "screenshotWindowUnderCursor", mask );
745 } else {
746 snapshot = WindowGrabber::grabCurrent( includeDecorations() );
747
748 QPoint offset = WindowGrabber::lastWindowPosition();
749 x = offset.x();
750 y = offset.y();
751
752 // If we're showing decorations anyway then we'll add the title and window
753 // class to the output image meta data.
754 if ( includeDecorations() ) {
755 title = WindowGrabber::lastWindowTitle();
756 windowClass = WindowGrabber::lastWindowClass();
757 }
758 }
759 }
760 else if ( mode() == CurrentScreen ) {
761 kDebug() << "Desktop Geom2 = " << QApplication::desktop()->geometry();
762 QDesktopWidget *desktop = QApplication::desktop();
763 int screenId = desktop->screenNumber( QCursor::pos() );
764 kDebug() << "Screenid2 = " << screenId;
765 QRect geom = desktop->screenGeometry( screenId );
766 kDebug() << "Geometry2 = " << geom;
767 x = geom.x();
768 y = geom.y();
769 snapshot = QPixmap::grabWindow( desktop->winId(),
770 x, y, geom.width(), geom.height() );
771 }
772 else {
773 snapshot = QPixmap::grabWindow( QApplication::desktop()->winId() );
774 }
775#ifdef HAVE_X11_EXTENSIONS_XFIXES_H
776 if (haveXFixes && includePointer()) {
777 grabPointerImage(x, y);
778 }
779#endif // HAVE_X11_EXTENSIONS_XFIXES_H
780
781 updatePreview();
782 QApplication::restoreOverrideCursor();
783 modified = true;
784 updateCaption();
785 if (savedPosition != QPoint(-1, -1)) {
786 move(savedPosition);
787 }
788 show();
789}
790
791void KSnapshot::grabPointerImage(int offsetx, int offsety)
792// Uses the X11_EXTENSIONS_XFIXES_H extension to grab the pointer image, and overlays it onto the snapshot.
793{
794#ifdef HAVE_X11_EXTENSIONS_XFIXES_H
795 XFixesCursorImage *xcursorimg = XFixesGetCursorImage( QX11Info::display() );
796 if ( !xcursorimg )
797 return;
798
799 //Annoyingly, xfixes specifies the data to be 32bit, but places it in an unsigned long *
800 //which can be 64 bit. So we need to iterate over a 64bit structure to put it in a 32bit
801 //structure.
802 QVarLengthArray< quint32 > pixels( xcursorimg->width * xcursorimg->height );
803 for (int i = 0; i < xcursorimg->width * xcursorimg->height; ++i)
804 pixels[i] = xcursorimg->pixels[i] & 0xffffffff;
805
806 QImage qcursorimg((uchar *) pixels.data(), xcursorimg->width, xcursorimg->height,
807 QImage::Format_ARGB32_Premultiplied);
808
809 QPainter painter(&snapshot);
810 painter.drawImage(QPointF(xcursorimg->x - xcursorimg->xhot - offsetx, xcursorimg->y - xcursorimg ->yhot - offsety), qcursorimg);
811
812 XFree(xcursorimg);
813#else // HAVE_X11_EXTENSIONS_XFIXES_H
814 Q_UNUSED(offsetx);
815 Q_UNUSED(offsety);
816 return;
817#endif // HAVE_X11_EXTENSIONS_XFIXES_H
818}
819
820void KSnapshot::setTime(int newTime)
821{
822 setDelay(newTime);
823}
824
825int KSnapshot::timeout() const
826{
827 return delay();
828}
829
830void KSnapshot::setURL( const QString &url )
831{
832 changeUrl( url );
833}
834
835void KSnapshot::setGrabMode( int m )
836{
837 setMode( m );
838}
839
840int KSnapshot::grabMode() const
841{
842 return mode();
843}
844
845void KSnapshot::refreshCaption()
846{
847 updateCaption();
848}
849
850void KSnapshot::updateCaption()
851{
852 setCaption( filename.fileName(), modified );
853}
854
855void KSnapshot::slotMovePointer(int x, int y)
856{
857 QCursor::setPos( x, y );
858}
859
860void KSnapshot::exit()
861{
862 reject();
863}
864
865void KSnapshot::slotModeChanged(int mode)
866{
867 mainWidget->cbIncludePointer->setEnabled(!(mode == Region || mode == FreeRegion));
868 mainWidget->lblIncludePointer->setEnabled(!(mode == Region || mode == FreeRegion));
869 mainWidget->cbIncludeDecorations->setEnabled(mode == WindowUnderCursor);
870 mainWidget->lblIncludeDecorations->setEnabled(mode == WindowUnderCursor);
871}
872
873void KSnapshot::setPreview( const QPixmap &pm )
874{
875 mainWidget->lblImage->setToolTip(
876 i18n( "Preview of the snapshot image (%1 x %2)" ,
877 pm.width(), pm.height() ) );
878
879 mainWidget->lblImage->setPreview(pm);
880 mainWidget->lblImage->adjustSize();
881}
882
883void KSnapshot::setDelay( int i )
884{
885 mainWidget->spinDelay->setValue(i);
886}
887
888void KSnapshot::setIncludeDecorations( bool b )
889{
890 mainWidget->cbIncludeDecorations->setChecked(b);
891}
892
893void KSnapshot::setIncludePointer(bool enabled)
894{
895 mainWidget->cbIncludePointer->setChecked(enabled);
896}
897
898bool KSnapshot::includePointer() const
899{
900 return mainWidget->cbIncludePointer->isChecked();
901}
902
903void KSnapshot::setMode( int mode )
904{
905 mainWidget->comboMode->setCurrentIndex(mode);
906 slotModeChanged(mode);
907}
908
909int KSnapshot::delay() const
910{
911 return mainWidget->spinDelay->value();
912}
913
914bool KSnapshot::includeDecorations() const
915{
916 return mainWidget->cbIncludeDecorations->isChecked();
917}
918
919int KSnapshot::mode() const
920{
921 return mainWidget->comboMode->currentIndex();
922}
923
924QPixmap KSnapshot::preview()
925{
926 return *mainWidget->lblImage->pixmap();
927}
928
929int KSnapshot::previewWidth() const
930{
931 return mainWidget->lblImage->width();
932}
933
934int KSnapshot::previewHeight() const
935{
936 return mainWidget->lblImage->height();
937}
938
939#include "ksnapshot.moc"
940