KDElibs: KSystemTray: On window close dialog
Sébastien Laoût [temporar]
les83plus at free.fr
Tue Nov 23 01:27:56 GMT 2004
Hello,
Yes I'm back :-)
As usualy, I'm always waiting the last minute to do things.
Since freeze is soon, it's time to hurry up me :-/
So, I've worked to:
- Get the dialog don't show if there isn't any systray area
or it is hidden.
- But before try to show/raise this area.
Sources are joined.
I'm facing one problem: if the kicker is under other windows I'm trying
to raise it, wait 1 second for a refresh, take the screenshot and then
show the message box.
But the raising is done JUST AFTER the message box is shown!
Even if I insert kapp->processEvents(), wait, use all the available KWin
methods... It does nothing.
Is anyone has an idea of what is the problem?
It's on point 4. in the code.
Also, kapp->widgetAt() seems to don't work on systray.
I will also look at how to add a "warning" color to KDE colorsheme.
Best regards,
Sébastien Laoût.
-------------- next part --------------
/**
* Call this method when the user clicked the close button of the window
* (the [x]) to inform him that the application sit in the system tray
* and willn't be closed (as he is used to).
*
* You usualy call it from reimplemented KMainWindow::queryClose()
*
* @author Sébastien Laoût
* @since 3.4
*/
void displayCloseMessage(QString fileMenu = "");
void KSystemTray::displayCloseMessage(QString fileMenu)
{
/* IDEAS OF IMPROVEMENTS:
* - Use queuedMessageBox() but it need a dontAskAgainName parameter
* and image in QMimeSourceFactory shouldn't be removed.
* - Sometimes the systray icon is coever (a passive popup...)
* Use XComposite extension, if available, to get the kicker pixmap.
* - Perhapse desaturate the area around the proper SysTray icon,
* helping bring it into sharper focus. Or draw the cicle with XOR
* brush.
* - Perhapse add the icon in the text (eg. "... in the
* system tray ([icon])."). Add some clutter to the dialog.
*/
#if KDE_IS_VERSION( 3, 1, 90 )
// Don't do all the computations if they are unneeded:
if ( ! KMessageBox::shouldBeShownContinue("hideOnCloseInfo") )
return;
#endif
// "Default parameter". Here, to avoid a i18n() call and dependancy in the .h
if (fileMenu.isEmpty())
fileMenu = i18n("File");
// Some values we need:
QPoint g = mapToGlobal(pos());
int desktopWidth = kapp->desktop()->width();
int desktopHeight = kapp->desktop()->height();
int tw = width();
int th = height();
// We are triying to make a live screenshot of the systray icon to circle it
// and show it to the user. If no systray is used or if the icon is not visible,
// we should not show that screenshot but only a text!
// 1. Determine if the user use a system tray area or not:
QCString screenstr;
screenstr.setNum(qt_xscreen());
QCString trayatom = "_NET_SYSTEM_TRAY_S" + screenstr;
bool useSystray = (KSelectionWatcher(trayatom).owner() != None);
// 2. And then if the icon is visible too (eg. this->show() has been called):
useSystray = useSystray && isVisible();
// 3. Kicker (or another systray manager) can be visible but masked out of
// the screen (ie. on right or on left of it). We check if the icon isn't
// out of screen.
if (useSystray) {
QRect deskRect(0, 0, desktopWidth, desktopHeight);
if ( !deskRect.contains(g.x(), g.y()) ||
!deskRect.contains(g.x() + tw, g.y() + th) )
useSystray = false;
}
// 4. We raise the window containing the systray icon (typically the kicker) to
// have the most chances it is visible during the capture:
if (useSystray) {
// We are testing if one of the corners is hidden, and if yes, we would enter
// a time consuming process (raise kicker and wait some time):
// if (kapp->widgetAt(g) != this ||
// kapp->widgetAt(g + QPoint(tw-1, 0)) != this ||
// kapp->widgetAt(g + QPoint(0, th-1)) != this ||
// kapp->widgetAt(g + QPoint(tw-1, th-1)) != this) {
int systrayManagerWinId = topLevelWidget()->winId();
KWin::forceActiveWindow(systrayManagerWinId);
kapp->processEvents(); // Because without it the systrayManager is raised only after the messageBox is displayed
// KWin::activateWindow(systrayManagerWinId);
// kapp->processEvents(); // Because without it the systrayManager is raised only after the messageBox is displayed
// KWin::raiseWindow(systrayManagerWinId);
// kapp->processEvents(); // Because without it the systrayManager is raised only after the messageBox is displayed
sleep(1);
// TODO: Re-verify that at elast one corner is now visible
// }
}
// KMessageBox::information(this, QString::number(g.x()) + ":" + QString::number(g.y()) + ":" +
// QString::number((int)(kapp->widgetAt(g+QPoint(1,1)))));
QString message = i18n(
"<p>Closing the main window will keep %1 running in the system tray. "
"Use <b>Quit</b> from the <b>%2</b> menu to quit the application.</p>"
).arg(KGlobal::instance()->aboutData()->programName(), "Basket");
// We are sure the systray icon is visible: ouf!
if (useSystray) {
// Compute size and position of the pixmap to be grabbed:
int w = desktopWidth / 4;
int h = desktopHeight / 9;
int x = g.x() + tw/2 - w/2; // Center the rectange in the systray icon
int y = g.y() + th/2 - h/2;
if (x < 0) x = 0; // Move the rectangle to stay in the desktop limits
if (y < 0) y = 0;
if (x + w > desktopWidth) x = desktopWidth - w;
if (y + h > desktopHeight) y = desktopHeight - h;
// Grab the desktop and draw a circle arround the icon:
QPixmap shot = QPixmap::grabWindow(qt_xrootwin(), x, y, w, h);
QPainter painter(&shot);
const int CIRCLE_MARGINS = 6;
const int CIRCLE_WIDTH = 3;
const int SHADOW_OFFSET = 1;
const int IMAGE_BORDER = 1;
int ax = g.x() - x - CIRCLE_MARGINS - 1;
int ay = g.y() - y - CIRCLE_MARGINS - 1;
painter.setPen( QPen(KApplication::palette().active().dark(), CIRCLE_WIDTH) );
painter.drawArc(ax + SHADOW_OFFSET, ay + SHADOW_OFFSET,
tw + 2*CIRCLE_MARGINS, th + 2*CIRCLE_MARGINS, 0, 16*360);
painter.setPen( QPen(Qt::red/*KApplication::palette().active().highlight()*/, CIRCLE_WIDTH) );
painter.drawArc(ax, ay, tw + 2*CIRCLE_MARGINS, th + 2*CIRCLE_MARGINS, 0, 16*360);
painter.end();
// Then, we add a border arround the image to make it more visible:
QPixmap finalShot(w + 2*IMAGE_BORDER, h + 2*IMAGE_BORDER);
finalShot.fill(KApplication::palette().active().foreground());
painter.begin(&finalShot);
painter.drawPixmap(IMAGE_BORDER, IMAGE_BORDER, shot);
painter.end();
// Associate source to image and show the dialog:
QMimeSourceFactory::defaultFactory()->setPixmap("systray_shot", finalShot);
KMessageBox::information(this,
message + "<p><center><img source=\"systray_shot\"></center></p>",
i18n("Docking in System Tray"), "hideOnCloseInfo");
QMimeSourceFactory::defaultFactory()->setData("systray_shot", 0L);
} else {
KMessageBox::information(this,
message,
i18n("Docking in System Tray"), "hideOnCloseInfo");
}
}
More information about the kde-core-devel
mailing list