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 | |
86 | class 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 | |
97 | KSnapshot::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 * = 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 | |
320 | KSnapshot::~KSnapshot() |
321 | { |
322 | delete mainWidget; |
323 | } |
324 | |
325 | void KSnapshot::resizeEvent( QResizeEvent * ) |
326 | { |
327 | updateTimer.setSingleShot( true ); |
328 | updateTimer.start( 200 ); |
329 | } |
330 | |
331 | void 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 | |
345 | void 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 | |
381 | void KSnapshot::slotCopy() |
382 | { |
383 | QClipboard *cb = QApplication::clipboard(); |
384 | cb->setPixmap( snapshot ); |
385 | } |
386 | |
387 | void 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 | |
401 | void 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 | |
415 | void 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 | |
429 | KUrl 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 | |
454 | void 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 | |
467 | void 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 | |
512 | void KSnapshot::() |
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 | |
583 | void 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 | |
605 | void KSnapshot::slotRegionUpdated( const QRect &selection ) |
606 | { |
607 | lastRegion = selection; |
608 | } |
609 | |
610 | void KSnapshot::slotFreeRegionUpdated( const QPolygon &selection ) |
611 | { |
612 | lastFreeRegion = selection; |
613 | } |
614 | |
615 | void 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 | |
629 | void KSnapshot::slotScreenshotReceived( qulonglong handle ) |
630 | { |
631 | #ifdef Q_WS_X11 |
632 | slotWindowGrabbed( QPixmap::fromX11Pixmap( handle ) ); |
633 | #endif |
634 | } |
635 | |
636 | void 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 | |
655 | bool 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 | |
667 | void KSnapshot::updatePreview() |
668 | { |
669 | setPreview( snapshot ); |
670 | } |
671 | |
672 | void 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 | |
682 | void 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 | |
692 | void 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 | |
706 | void 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 | |
791 | void 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 | |
820 | void KSnapshot::setTime(int newTime) |
821 | { |
822 | setDelay(newTime); |
823 | } |
824 | |
825 | int KSnapshot::timeout() const |
826 | { |
827 | return delay(); |
828 | } |
829 | |
830 | void KSnapshot::setURL( const QString &url ) |
831 | { |
832 | changeUrl( url ); |
833 | } |
834 | |
835 | void KSnapshot::setGrabMode( int m ) |
836 | { |
837 | setMode( m ); |
838 | } |
839 | |
840 | int KSnapshot::grabMode() const |
841 | { |
842 | return mode(); |
843 | } |
844 | |
845 | void KSnapshot::refreshCaption() |
846 | { |
847 | updateCaption(); |
848 | } |
849 | |
850 | void KSnapshot::updateCaption() |
851 | { |
852 | setCaption( filename.fileName(), modified ); |
853 | } |
854 | |
855 | void KSnapshot::slotMovePointer(int x, int y) |
856 | { |
857 | QCursor::setPos( x, y ); |
858 | } |
859 | |
860 | void KSnapshot::exit() |
861 | { |
862 | reject(); |
863 | } |
864 | |
865 | void 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 | |
873 | void 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 | |
883 | void KSnapshot::setDelay( int i ) |
884 | { |
885 | mainWidget->spinDelay->setValue(i); |
886 | } |
887 | |
888 | void KSnapshot::setIncludeDecorations( bool b ) |
889 | { |
890 | mainWidget->cbIncludeDecorations->setChecked(b); |
891 | } |
892 | |
893 | void KSnapshot::setIncludePointer(bool enabled) |
894 | { |
895 | mainWidget->cbIncludePointer->setChecked(enabled); |
896 | } |
897 | |
898 | bool KSnapshot::includePointer() const |
899 | { |
900 | return mainWidget->cbIncludePointer->isChecked(); |
901 | } |
902 | |
903 | void KSnapshot::setMode( int mode ) |
904 | { |
905 | mainWidget->comboMode->setCurrentIndex(mode); |
906 | slotModeChanged(mode); |
907 | } |
908 | |
909 | int KSnapshot::delay() const |
910 | { |
911 | return mainWidget->spinDelay->value(); |
912 | } |
913 | |
914 | bool KSnapshot::includeDecorations() const |
915 | { |
916 | return mainWidget->cbIncludeDecorations->isChecked(); |
917 | } |
918 | |
919 | int KSnapshot::mode() const |
920 | { |
921 | return mainWidget->comboMode->currentIndex(); |
922 | } |
923 | |
924 | QPixmap KSnapshot::preview() |
925 | { |
926 | return *mainWidget->lblImage->pixmap(); |
927 | } |
928 | |
929 | int KSnapshot::previewWidth() const |
930 | { |
931 | return mainWidget->lblImage->width(); |
932 | } |
933 | |
934 | int KSnapshot::previewHeight() const |
935 | { |
936 | return mainWidget->lblImage->height(); |
937 | } |
938 | |
939 | #include "ksnapshot.moc" |
940 | |