Updated the README file with some contributor tips.
[basket4.git] / src / systemtray.cpp
blobc749baa49f7b260fdcbedf5d137d40df81466932
1 /***************************************************************************
2 * Copyright (C) 2003 by S�astien Laot *
3 * slaout@linux62.org *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 /** KSystemTray2 */
23 // To draw the systray screenshot image:
24 #include <q3dockwindow.h>
25 #include <qmovie.h>
26 #include <qvariant.h>
27 //Added by qt3to4:
28 #include <QDragMoveEvent>
29 #include <Q3CString>
30 #include <QDropEvent>
31 #include <QDragLeaveEvent>
32 #include <QWheelEvent>
33 #include <QMouseEvent>
34 #include <QEvent>
35 #include <QDragEnterEvent>
36 #include "linklabel.h"
37 #include "note.h"
39 #include <qdesktopwidget.h>
40 #include <qmime.h>
41 #include <qpainter.h>
42 #include <qpoint.h>
43 #include <qpixmap.h>
44 // To know the program name:
45 #include <kglobal.h>
46 #include <kcomponentdata.h>
47 #include <kaboutdata.h>
48 #include <kiconeffect.h>
49 // Others:
50 #include <kmessagebox.h>
51 #include <kmanagerselection.h>
52 #include <kdeversion.h>
53 #include <kapplication.h>
54 #include <kmenu.h>
55 #include <kiconloader.h>
56 #include <kdebug.h>
57 #include "systemtray.h"
58 #include "basket.h"
59 #include "settings.h"
60 #include "global.h"
61 #include "tools.h"
63 KSystemTray2::KSystemTray2(QWidget *parent, const char *name)
64 : KSystemTray(parent, name)
68 KSystemTray2::~KSystemTray2()
72 void KSystemTray2::displayCloseMessage(QString fileMenu)
74 /* IDEAS OF IMPROVEMENTS:
75 * - Use queuedMessageBox() but it need a dontAskAgainName parameter
76 * and image in QMimeSourceFactory shouldn't be removed.
77 * - Sometimes the systray icon is covered (a passive popup...)
78 * Use XComposite extension, if available, to get the kicker pixmap.
79 * - Perhapse desaturate the area around the proper SysTray icon,
80 * helping bring it into sharper focus. Or draw the cicle with XOR
81 * brush.
82 * - Perhapse add the icon in the text (eg. "... in the
83 * system tray ([icon])."). Add some clutter to the dialog.
85 #if KDE_IS_VERSION( 3, 1, 90 )
86 // Don't do all the computations if they are unneeded:
87 if ( ! KMessageBox::shouldBeShownContinue("hideOnCloseInfo") )
88 return;
89 #endif
90 // "Default parameter". Here, to avoid a i18n() call and dependancy in the .h
91 if (fileMenu.isEmpty())
92 fileMenu = i18n("File");
94 // Some values we need:
95 QPoint g = mapToGlobal(pos());
96 int desktopWidth = kapp->desktop()->width();
97 int desktopHeight = kapp->desktop()->height();
98 int tw = width();
99 int th = height();
101 // We are triying to make a live screenshot of the systray icon to circle it
102 // and show it to the user. If no systray is used or if the icon is not visible,
103 // we should not show that screenshot but only a text!
105 // 1. Determine if the user use a system tray area or not:
106 Q3CString screenstr;
107 screenstr.setNum(qt_xscreen());
108 Q3CString trayatom = "_NET_SYSTEM_TRAY_S" + screenstr;
109 bool useSystray = (KSelectionWatcher(trayatom).owner() != 0L);
111 // 2. And then if the icon is visible too (eg. this->show() has been called):
112 useSystray = useSystray && isVisible();
114 // 3. Kicker (or another systray manager) can be visible but masked out of
115 // the screen (ie. on right or on left of it). We check if the icon isn't
116 // out of screen.
117 if (useSystray) {
118 QRect deskRect(0, 0, desktopWidth, desktopHeight);
119 if ( !deskRect.contains(g.x(), g.y()) ||
120 !deskRect.contains(g.x() + tw, g.y() + th) )
121 useSystray = false;
124 // 4. We raise the window containing the systray icon (typically the kicker) to
125 // have the most chances it is visible during the capture:
126 /* if (useSystray) {
127 // We are testing if one of the corners is hidden, and if yes, we would enter
128 // a time consuming process (raise kicker and wait some time):
129 // if (kapp->widgetAt(g) != this ||
130 // kapp->widgetAt(g + QPoint(tw-1, 0)) != this ||
131 // kapp->widgetAt(g + QPoint(0, th-1)) != this ||
132 // kapp->widgetAt(g + QPoint(tw-1, th-1)) != this) {
133 int systrayManagerWinId = topLevelWidget()->winId();
134 KWindowSystem::forceActiveWindow(systrayManagerWinId);
135 kapp->processEvents(); // Because without it the systrayManager is raised only after the messageBox is displayed
136 // KWindowSystem::activateWindow(systrayManagerWinId);
137 // kapp->processEvents(); // Because without it the systrayManager is raised only after the messageBox is displayed
138 // KWindowSystem::raiseWindow(systrayManagerWinId);
139 // kapp->processEvents(); // Because without it the systrayManager is raised only after the messageBox is displayed
140 sleep(1);
141 // TODO: Re-verify that at least one corner is now visible
142 // }
145 // KMessageBox::information(this, QString::number(g.x()) + ":" + QString::number(g.y()) + ":" +
146 // QString::number((int)(kapp->widgetAt(g+QPoint(1,1)))));
148 QString message = i18n(
149 "<p>Closing the main window will keep %1 running in the system tray. "
150 "Use <b>Quit</b> from the <b>Basket</b> menu to quit the application.</p>"
151 ).arg(KGlobal::instance()->aboutData()->programName());
152 // We are sure the systray icon is visible: ouf!
153 if (useSystray) {
154 // Compute size and position of the pixmap to be grabbed:
155 int w = desktopWidth / 4;
156 int h = desktopHeight / 9;
157 int x = g.x() + tw/2 - w/2; // Center the rectange in the systray icon
158 int y = g.y() + th/2 - h/2;
159 if (x < 0) x = 0; // Move the rectangle to stay in the desktop limits
160 if (y < 0) y = 0;
161 if (x + w > desktopWidth) x = desktopWidth - w;
162 if (y + h > desktopHeight) y = desktopHeight - h;
164 // Grab the desktop and draw a circle arround the icon:
165 QPixmap shot = QPixmap::grabWindow(QX11Info::appRootWindow(), x, y, w, h);
166 QPainter painter(&shot);
167 const int CIRCLE_MARGINS = 6;
168 const int CIRCLE_WIDTH = 3;
169 const int SHADOW_OFFSET = 1;
170 const int IMAGE_BORDER = 1;
171 int ax = g.x() - x - CIRCLE_MARGINS - 1;
172 int ay = g.y() - y - CIRCLE_MARGINS - 1;
173 painter.setPen( QPen(KApplication::palette().active().dark(), CIRCLE_WIDTH) );
174 painter.drawArc(ax + SHADOW_OFFSET, ay + SHADOW_OFFSET,
175 tw + 2*CIRCLE_MARGINS, th + 2*CIRCLE_MARGINS, 0, 16*360);
176 painter.setPen( QPen(Qt::red/*KApplication::palette().active().highlight()*/, CIRCLE_WIDTH) );
177 painter.drawArc(ax, ay, tw + 2*CIRCLE_MARGINS, th + 2*CIRCLE_MARGINS, 0, 16*360);
178 #if 1
179 // Draw the pixmap over the screenshot in case a window hide the icon:
180 painter.drawPixmap(g.x() - x, g.y() - y + 1, *pixmap());
181 #endif
182 painter.end();
184 // Then, we add a border arround the image to make it more visible:
185 QPixmap finalShot(w + 2*IMAGE_BORDER, h + 2*IMAGE_BORDER);
186 finalShot.fill(KApplication::palette().active().foreground());
187 painter.begin(&finalShot);
188 painter.drawPixmap(IMAGE_BORDER, IMAGE_BORDER, shot);
189 painter.end();
191 // Associate source to image and show the dialog:
192 Q3MimeSourceFactory::defaultFactory()->setPixmap("systray_shot", finalShot);
193 KMessageBox::information(kapp->activeWindow(),
194 message + "<p><center><img source=\"systray_shot\"></center></p>",
195 i18n("Docking in System Tray"), "hideOnCloseInfo");
196 Q3MimeSourceFactory::defaultFactory()->setData("systray_shot", 0L);
197 } else {
198 KMessageBox::information(kapp->activeWindow(),
199 message,
200 i18n("Docking in System Tray"), "hideOnCloseInfo");
204 /** SystemTray */
206 SystemTray::SystemTray(QWidget *parent, const char *name)
207 : KSystemTray2(parent, name != 0 ? name : "SystemTray"), m_showTimer(0), m_autoShowTimer(0)
209 setAcceptDrops(true);
211 m_showTimer = new QTimer(this);
212 connect( m_showTimer, SIGNAL(timeout()), Global::bnpView, SLOT(setActive()) );
214 m_autoShowTimer = new QTimer(this);
215 connect( m_autoShowTimer, SIGNAL(timeout()), Global::bnpView, SLOT(setActive()) );
217 // Create pixmaps for the icon:
218 m_iconPixmap = loadIcon("basket");
219 // FIXME: When main window is shown at start, the icon is loaded 1 pixel too high
220 // and then reloaded instantly after at the right position.
221 // setPixmap(m_iconPixmap); // Load it the sooner as possible to avoid flicker
222 QImage lockedIconImage = m_iconPixmap.convertToImage();
223 QPixmap lockOverlayPixmap = loadIcon("lockoverlay");
224 QImage lockOverlayImage = lockOverlayPixmap.convertToImage();
225 KIconEffect::overlay(lockedIconImage, lockOverlayImage);
226 m_lockedIconPixmap.convertFromImage(lockedIconImage);
228 updateToolTip(); // Set toolTip AND icon
231 SystemTray::~SystemTray()
235 void SystemTray::mousePressEvent(QMouseEvent *event)
237 if (event->button() & Qt::LeftButton) { // Prepare drag
238 m_pressPos = event->globalPos();
239 m_canDrag = true;
240 event->accept();
241 } else if (event->button() & Qt::MidButton) { // Paste
242 Global::bnpView->currentBasket()->setInsertPopupMenu();
243 Global::bnpView->currentBasket()->pasteNote(QClipboard::Selection);
244 Global::bnpView->currentBasket()->cancelInsertPopupMenu();
245 if (Settings::usePassivePopup())
246 Global::bnpView->showPassiveDropped(i18n("Pasted selection to basket <i>%1</i>"));
247 event->accept();
248 } else if (event->button() & Qt::RightButton) { // Popup menu
249 KMenu menu(this);
250 menu.insertTitle( SmallIcon("basket"), kapp->aboutData()->programName() );
252 Global::bnpView->actNewBasket->plug(&menu);
253 Global::bnpView->actNewSubBasket->plug(&menu);
254 Global::bnpView->actNewSiblingBasket->plug(&menu);
255 menu.insertSeparator();
256 Global::bnpView->m_actPaste->plug(&menu);
257 Global::bnpView->m_actGrabScreenshot->plug(&menu);
258 Global::bnpView->m_actColorPicker->plug(&menu);
260 if(!Global::bnpView->isPart())
262 KAction* action;
264 menu.insertSeparator();
266 action = Global::bnpView->actionCollection()->action("options_configure_global_keybinding");
267 if(action)
268 action->plug(&menu);
270 action = Global::bnpView->actionCollection()->action("options_configure");
271 if(action)
272 action->plug(&menu);
274 menu.insertSeparator();
276 // Minimize / restore : since we manage the popup menu by ourself, we should do that work :
277 action = Global::bnpView->actionCollection()->action("minimizeRestore");
278 if(action)
280 if (Global::mainWindow()->isVisible())
281 action->setText(i18n("&Minimize"));
282 else
283 action->setText(i18n("&Restore"));
284 action->plug(&menu);
287 action = Global::bnpView->actionCollection()->action("file_quit");
288 if(action)
289 action->plug(&menu);
292 Global::bnpView->currentBasket()->setInsertPopupMenu();
293 connect( &menu, SIGNAL(aboutToHide()), Global::bnpView->currentBasket(), SLOT(delayedCancelInsertPopupMenu()) );
294 menu.exec(event->globalPos());
295 event->accept();
296 } else
297 event->ignore();
300 void SystemTray::mouseMoveEvent(QMouseEvent *event)
302 event->ignore();
305 void SystemTray::mouseReleaseEvent(QMouseEvent *event)
307 m_canDrag = false;
308 if (event->button() == Qt::LeftButton) // Show / hide main window
309 if ( rect().contains(event->pos()) ) { // Accept only if released in systemTray
310 toggleActive();
311 emit showPart();
312 event->accept();
313 } else
314 event->ignore();
317 void SystemTray::dragEnterEvent(QDragEnterEvent *event)
319 m_showTimer->start( Settings::dropTimeToShow() * 100, true );
320 Global::bnpView->currentBasket()->showFrameInsertTo();
321 /// m_parentContainer->setStatusBarDrag(); // FIXME: move this line in Basket::showFrameInsertTo() ?
322 Basket::acceptDropEvent(event);
325 void SystemTray::dragMoveEvent(QDragMoveEvent *event)
327 Basket::acceptDropEvent(event);
330 void SystemTray::dragLeaveEvent(QDragLeaveEvent*)
332 m_showTimer->stop();
333 m_canDrag = false;
334 Global::bnpView->currentBasket()->resetInsertTo();
335 Global::bnpView->updateStatusBarHint();
338 #include <iostream>
339 #include <QX11Info>
341 void SystemTray::dropEvent(QDropEvent *event)
343 m_showTimer->stop();
345 Global::bnpView->currentBasket()->blindDrop(event);
347 /* Basket *basket = Global::bnpView->currentBasket();
348 if (!basket->isLoaded()) {
349 Global::bnpView->showPassiveLoading(basket);
350 basket->load();
352 basket->contentsDropEvent(event);
353 std::cout << (long) basket->selectedNotes() << std::endl;
355 if (Settings::usePassivePopup())
356 Global::bnpView->showPassiveDropped(i18n("Dropped to basket <i>%1</i>"));*/
359 /* This function comes directly from JuK: */
362 * This function copies the entirety of src into dest, starting in
363 * dest at x and y. This function exists because I was unable to find
364 * a function like it in either QImage or kdefx
366 static bool copyImage(QImage &dest, QImage &src, int x, int y)
368 if(dest.depth() != src.depth())
369 return false;
370 if((x + src.width()) >= dest.width())
371 return false;
372 if((y + src.height()) >= dest.height())
373 return false;
375 // We want to use KIconEffect::overlay to do this, since it handles
376 // alpha, but the images need to be the same size. We can handle that.
378 QImage large_src(dest);
380 // It would perhaps be better to create large_src based on a size, but
381 // this is the easiest way to make a new image with the same depth, size,
382 // etc.
384 large_src.detach();
386 // However, we do have to specifically ensure that setAlphaBuffer is set
387 // to false
389 large_src.setAlphaBuffer(false);
390 large_src.fill(0); // All transparent pixels
391 large_src.setAlphaBuffer(true);
393 int w = src.width();
394 int h = src.height();
395 for(int dx = 0; dx < w; dx++)
396 for(int dy = 0; dy < h; dy++)
397 large_src.setPixel(dx + x, dy + y, src.pixel(dx, dy));
399 // Apply effect to image
401 KIconEffect::overlay(dest, large_src);
403 return true;
406 void SystemTray::updateToolTip()
408 // return; /////////////////////////////////////////////////////
410 Basket *basket = Global::bnpView->currentBasket();
411 if (!basket)
412 return;
414 if (basket->icon().isEmpty() || basket->icon() == "basket" || ! Settings::showIconInSystray())
415 setPixmap(basket->isLocked() ? m_lockedIconPixmap : m_iconPixmap);
416 else {
417 // Code that comes from JuK:
418 QPixmap bgPix = loadIcon("basket");
419 QPixmap fgPix = SmallIcon(basket->icon());
421 QImage bgImage = bgPix.convertToImage(); // Probably 22x22
422 QImage fgImage = fgPix.convertToImage(); // Should be 16x16
423 QImage lockOverlayImage = loadIcon("lockoverlay").convertToImage();
425 KIconEffect::semiTransparent(bgImage);
426 copyImage(bgImage, fgImage, (bgImage.width() - fgImage.width()) / 2,
427 (bgImage.height() - fgImage.height()) / 2);
428 if (basket->isLocked())
429 KIconEffect::overlay(bgImage, lockOverlayImage);
431 bgPix.convertFromImage(bgImage);
432 setPixmap(bgPix);
435 //QTimer::singleShot( Container::c_delayTooltipTime, this, SLOT(updateToolTipDelayed()) );
436 // No need to delay: it's be called when notes are changed:
437 updateToolTipDelayed();
440 void SystemTray::updateToolTipDelayed()
442 Basket *basket = Global::bnpView->currentBasket();
444 QString tip = "<p><nobr>" + ( basket->isLocked() ? KDialog::makeStandardCaption(i18n("%1 (Locked)"))
445 : KDialog::makeStandardCaption( "%1") )
446 .arg(Tools::textToHTMLWithoutP(basket->basketName()));
448 QToolTip::add(this, tip);
451 void SystemTray::wheelEvent(QWheelEvent *event)
453 if (event->delta() > 0)
454 Global::bnpView->goToPreviousBasket();
455 else
456 Global::bnpView->goToNextBasket();
458 if (Settings::usePassivePopup())
459 Global::bnpView->showPassiveContent();
462 void SystemTray::enterEvent(QEvent*)
464 if (Settings::showOnMouseIn())
465 m_autoShowTimer->start(Settings::timeToShowOnMouseIn() * 100, true );
468 void SystemTray::leaveEvent(QEvent*)
470 m_autoShowTimer->stop();
473 #include "systemtray.moc"