1 /***************************************************************************
2 * Copyright (C) 2003 by S�astien Laot *
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. *
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. *
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 ***************************************************************************/
23 // To draw the systray screenshot image:
24 #include <q3dockwindow.h>
28 #include <QDragMoveEvent>
31 #include <QDragLeaveEvent>
32 #include <QWheelEvent>
33 #include <QMouseEvent>
35 #include <QDragEnterEvent>
36 #include "linklabel.h"
39 #include <qdesktopwidget.h>
44 // To know the program name:
46 #include <kinstance.h>
47 #include <kaboutdata.h>
48 #include <kiconeffect.h>
50 #include <kmessagebox.h>
51 #include <kmanagerselection.h>
52 #include <kdeversion.h>
53 #include <kapplication.h>
54 #include <kpopupmenu.h>
55 #include <kiconloader.h>
57 #include "systemtray.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
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") )
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();
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:
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
118 QRect
deskRect(0, 0, desktopWidth
, desktopHeight
);
119 if ( !deskRect
.contains(g
.x(), g
.y()) ||
120 !deskRect
.contains(g
.x() + tw
, g
.y() + th
) )
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:
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 KWin::forceActiveWindow(systrayManagerWinId);
135 kapp->processEvents(); // Because without it the systrayManager is raised only after the messageBox is displayed
136 // KWin::activateWindow(systrayManagerWinId);
137 // kapp->processEvents(); // Because without it the systrayManager is raised only after the messageBox is displayed
138 // KWin::raiseWindow(systrayManagerWinId);
139 // kapp->processEvents(); // Because without it the systrayManager is raised only after the messageBox is displayed
141 // TODO: Re-verify that at least one corner is now visible
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!
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
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(qt_xrootwin(), 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);
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());
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
);
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);
198 KMessageBox::information(kapp
->activeWindow(),
200 i18n("Docking in System Tray"), "hideOnCloseInfo");
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();
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>"));
248 } else if (event
->button() & Qt::RightButton
) { // Popup menu
249 KPopupMenu
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())
264 menu
.insertSeparator();
266 action
= Global::bnpView
->actionCollection()->action("options_configure_global_keybinding");
270 action
= Global::bnpView
->actionCollection()->action("options_configure");
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");
280 if (Global::mainWindow()->isVisible())
281 action
->setText(i18n("&Minimize"));
283 action
->setText(i18n("&Restore"));
287 action
= Global::bnpView
->actionCollection()->action("file_quit");
292 Global::bnpView
->currentBasket()->setInsertPopupMenu();
293 connect( &menu
, SIGNAL(aboutToHide()), Global::bnpView
->currentBasket(), SLOT(delayedCancelInsertPopupMenu()) );
294 menu
.exec(event
->globalPos());
300 void SystemTray::mouseMoveEvent(QMouseEvent
*event
)
305 void SystemTray::mouseReleaseEvent(QMouseEvent
*event
)
308 if (event
->button() == Qt::LeftButton
) // Show / hide main window
309 if ( rect().contains(event
->pos()) ) { // Accept only if released in systemTray
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
*)
334 Global::bnpView
->currentBasket()->resetInsertTo();
335 Global::bnpView
->updateStatusBarHint();
340 void SystemTray::dropEvent(QDropEvent
*event
)
344 Global::bnpView
->currentBasket()->blindDrop(event
);
346 /* Basket *basket = Global::bnpView->currentBasket();
347 if (!basket->isLoaded()) {
348 Global::bnpView->showPassiveLoading(basket);
351 basket->contentsDropEvent(event);
352 std::cout << (long) basket->selectedNotes() << std::endl;
354 if (Settings::usePassivePopup())
355 Global::bnpView->showPassiveDropped(i18n("Dropped to basket <i>%1</i>"));*/
358 /* This function comes directly from JuK: */
361 * This function copies the entirety of src into dest, starting in
362 * dest at x and y. This function exists because I was unable to find
363 * a function like it in either QImage or kdefx
365 static bool copyImage(QImage
&dest
, QImage
&src
, int x
, int y
)
367 if(dest
.depth() != src
.depth())
369 if((x
+ src
.width()) >= dest
.width())
371 if((y
+ src
.height()) >= dest
.height())
374 // We want to use KIconEffect::overlay to do this, since it handles
375 // alpha, but the images need to be the same size. We can handle that.
377 QImage
large_src(dest
);
379 // It would perhaps be better to create large_src based on a size, but
380 // this is the easiest way to make a new image with the same depth, size,
385 // However, we do have to specifically ensure that setAlphaBuffer is set
388 large_src
.setAlphaBuffer(false);
389 large_src
.fill(0); // All transparent pixels
390 large_src
.setAlphaBuffer(true);
393 int h
= src
.height();
394 for(int dx
= 0; dx
< w
; dx
++)
395 for(int dy
= 0; dy
< h
; dy
++)
396 large_src
.setPixel(dx
+ x
, dy
+ y
, src
.pixel(dx
, dy
));
398 // Apply effect to image
400 KIconEffect::overlay(dest
, large_src
);
405 void SystemTray::updateToolTip()
407 // return; /////////////////////////////////////////////////////
409 Basket
*basket
= Global::bnpView
->currentBasket();
413 if (basket
->icon().isEmpty() || basket
->icon() == "basket" || ! Settings::showIconInSystray())
414 setPixmap(basket
->isLocked() ? m_lockedIconPixmap
: m_iconPixmap
);
416 // Code that comes from JuK:
417 QPixmap bgPix
= loadIcon("basket");
418 QPixmap fgPix
= SmallIcon(basket
->icon());
420 QImage bgImage
= bgPix
.convertToImage(); // Probably 22x22
421 QImage fgImage
= fgPix
.convertToImage(); // Should be 16x16
422 QImage lockOverlayImage
= loadIcon("lockoverlay").convertToImage();
424 KIconEffect::semiTransparent(bgImage
);
425 copyImage(bgImage
, fgImage
, (bgImage
.width() - fgImage
.width()) / 2,
426 (bgImage
.height() - fgImage
.height()) / 2);
427 if (basket
->isLocked())
428 KIconEffect::overlay(bgImage
, lockOverlayImage
);
430 bgPix
.convertFromImage(bgImage
);
434 //QTimer::singleShot( Container::c_delayTooltipTime, this, SLOT(updateToolTipDelayed()) );
435 // No need to delay: it's be called when notes are changed:
436 updateToolTipDelayed();
439 void SystemTray::updateToolTipDelayed()
441 Basket
*basket
= Global::bnpView
->currentBasket();
443 QString tip
= "<p><nobr>" + ( basket
->isLocked() ? kapp
->makeStdCaption(i18n("%1 (Locked)"))
444 : kapp
->makeStdCaption( "%1") )
445 .arg(Tools::textToHTMLWithoutP(basket
->basketName()));
447 QToolTip::add(this, tip
);
450 void SystemTray::wheelEvent(QWheelEvent
*event
)
452 if (event
->delta() > 0)
453 Global::bnpView
->goToPreviousBasket();
455 Global::bnpView
->goToNextBasket();
457 if (Settings::usePassivePopup())
458 Global::bnpView
->showPassiveContent();
461 void SystemTray::enterEvent(QEvent
*)
463 if (Settings::showOnMouseIn())
464 m_autoShowTimer
->start(Settings::timeToShowOnMouseIn() * 100, true );
467 void SystemTray::leaveEvent(QEvent
*)
469 m_autoShowTimer
->stop();
472 #include "systemtray.moc"