[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-devel
Subject:    Re: Proposal to implement autohide in systemtray.
From:       David_Hénot <ptitlutin () gmail ! com>
Date:       2004-10-22 17:43:13
Message-ID: 6a0f0e5204102210436af278e8 () mail ! gmail ! com
[Download RAW message or body]

Hi all,

On Fri, 15 Oct 2004 16:27:16 -0600, Aaron Seigo <aseigo@kde.org> wrote:
> you'd need to do it with an event filter that catches all X events for the
> geometry covering the systray. this kind of stuff is very, very ugly and
> very, very prone to breakage. i'm very, very opposed to this sort of hack. of
> course, perhaps someone else can suggest a more elegant way of managing it,
> but the basic problem is that the icons are out-of-process and the system
> tray has no real way of programatically interacting with them. ergo, the
> current systray protocol sucks. this is a known issue.

I have been thinking about this problem for some time as I like the
possibility of hiding systray icons but I think the way it is done in
XP sucks : for newbies I think it is quite confusing when the UI
changes without any user action, and for "advanced" users an
inactivity-based system is a bit frustrating. For example I may want
for a particular icon to be never hidden, so I'm able to quickly know
the application is running or not, and when I use a hidden icon I
don't want it to wait for several minutes before it is hidden again.
Impossible with a XP-like systray.

So I thought the best way for me would be to configure which icons
should be hidden regardless of their inactivity time. I have tried to
implement it and finaly I have something working. I attached a patch
with my changes. It still needs some work I think, but I find it quite
useful.

It adds a configuration dialog to the systray applet with the list of
currently active systray icons and you can put them in the hide list.
This configuration is saved so you don't have to manualy re-hide the
icon next time it is added to the systray. When some icons are hidden
you get a button to show/hide these icons.

Let me know what you think about it.

> btw, sometime before 3.2 or 3.1 i implemented the ability to hide/autohide
> icons in the system tray. but because the systray doesn't actually control
> the icons they would re-appear at the controling application's whim and do
> other crazy and annoying things.

Do you have an example which causes this ?
Maybe at least we could make sure  kde apps don't do such crazy things
and disable icon hidding for other apps ? It would still be useful for
me as 90% of my systray apps are kde apps and I guess I'm not the only
one.

David

["systray_hideicons.diff" (text/x-patch)]

Index: systemtrayapplet.cpp
===================================================================
RCS file: /home/kde/kdebase/kicker/applets/systemtray/systemtrayapplet.cpp,v
retrieving revision 1.49
diff -u -3 -p -r1.49 systemtrayapplet.cpp
--- systemtrayapplet.cpp	13 Oct 2004 01:19:33 -0000	1.49
+++ systemtrayapplet.cpp	22 Oct 2004 17:13:08 -0000
@@ -35,6 +35,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE 
 #include <kglobal.h>
 #include <krun.h>
 #include <kwinmodule.h>
+#include <kdialogbase.h>
+#include <kactionselector.h>
+#include <kiconloader.h>
 
 #include "systemtrayapplet.h"
 #include "systemtrayapplet.moc"
@@ -46,8 +49,9 @@ extern "C"
     KPanelApplet* init(QWidget *parent, const QString& configFile)
     {
         KGlobal::locale()->insertCatalogue("ksystemtrayapplet");
+        int options = KPanelApplet::Preferences;
         return new SystemTrayApplet(configFile, KPanelApplet::Normal,
-                                    0, parent, "ksystemtrayapplet");
+                                    options, parent, "ksystemtrayapplet");
     }
 }
 
@@ -58,8 +62,18 @@ SystemTrayApplet::SystemTrayApplet(const
     hideFrameTimer(0)
 {
     loadSettings();
+    m_visibleWins = NULL;
 
-    m_Wins.setAutoDelete(true);
+    expandButton = new KPushButton(this, "expandButton");
+    expandButton->hide();
+    expandButton->setIconSet( KGlobal::iconLoader()->loadIconSet("1leftarrow", \
KIcon::NoGroup, +                                                                 \
KIcon::SizeSmall, false) ); +    expandButton->resize(24, 24);
+    showHidden = false;
+    connect( expandButton, SIGNAL( clicked() ), this, SLOT( toggleExpanded() ) );
+
+    m_shownWins.setAutoDelete(true);
+    m_hiddenWins.setAutoDelete(true);
     setBackgroundOrigin(AncestorOrigin);
 
     kwin_module = new KWinModule(this);
@@ -72,7 +86,10 @@ SystemTrayApplet::SystemTrayApplet(const
         existing = true;
     }
     if (existing)
+    {
+        updateVisibleWins();
         layoutTray();
+    }
 
     // the KWinModule notifies us when tray windows are added or removed
     connect( kwin_module, SIGNAL( systemTrayWindowAdded(WId) ),
@@ -123,7 +140,9 @@ SystemTrayApplet::SystemTrayApplet(const
 
 SystemTrayApplet::~SystemTrayApplet()
 {
-    m_Wins.clear();
+    delete m_visibleWins;
+    m_shownWins.clear();
+    m_hiddenWins.clear();
     KGlobal::locale()->removeCatalogue("ksystemtrayapplet");
 }
 
@@ -135,9 +154,8 @@ bool SystemTrayApplet::x11Event( XEvent 
     if ( e->type == ClientMessage ) {
         if ( e->xclient.message_type == net_system_tray_opcode &&
              e->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
-            for (QXEmbed* emb = m_Wins.first(); emb != 0L; emb = m_Wins.next())
-                if( emb->embeddedWinId() == (WId)e->xclient.data.l[2] ) // we \
                already manage it
-                    return true;
+            if( isWinManaged( (WId)e->xclient.data.l[2] ) ) // we already manage it
+                return true;
             embedWindow( e->xclient.data.l[2], false );
             layoutTray();
             emit updateLayout();
@@ -174,6 +192,75 @@ void SystemTrayApplet::leaveEvent( QEven
     }
 }
 
+void SystemTrayApplet::preferences()
+{
+    KDialogBase dialog(this, "systrayconfig", false, i18n("Configure"), \
KDialogBase::Ok | KDialogBase::Cancel); +    KActionSelector selector(&dialog, \
"systrayselector"); +    dialog.setMainWidget(&selector);
+
+    selector.setAvailableLabel(i18n("&Show:"));
+    selector.setSelectedLabel(i18n("&Hide:"));
+    selector.setSelectedInsertionPolicy(KActionSelector::AtBottom);
+    selector.setShowUpDownButtons(false);
+    selector.setMinimumSize(300, 300);
+
+    QListBox *shownListBox = selector.availableListBox();
+    QListBox *hiddenListBox = selector.selectedListBox();
+
+    for (QXEmbed* emb = m_shownWins.first(); emb != 0L; emb = m_shownWins.next())
+    {
+        QString name = getWinName(emb->embeddedWinId());
+        if(!shownListBox->findItem(name, Qt::ExactMatch | Qt::CaseSensitive))
+            shownListBox->insertItem(name);
+    }
+    for (QXEmbed* emb = m_hiddenWins.first(); emb != 0L; emb = m_hiddenWins.next())
+    {
+        QString name = getWinName(emb->embeddedWinId());
+        if(!hiddenListBox->findItem(name, Qt::ExactMatch | Qt::CaseSensitive))
+            hiddenListBox->insertItem(name);
+    }
+
+    if(dialog.exec() == QDialog::Accepted)
+    {
+        KConfig *conf = config();
+        conf->setGroup("HiddenTrayIcons");
+        QString name;
+        for(uint i = 0; (name = shownListBox->text(i)) != QString::null; i++)
+            conf->writeEntry(name, false);
+        for(uint i = 0; (name = hiddenListBox->text(i)) != QString::null; i++)
+            conf->writeEntry(name, true);
+
+        for(TrayEmbed* emb = m_shownWins.first(); emb != 0L; )
+            if(shouldHide(emb->embeddedWinId()))
+            {
+                emb = m_shownWins.take(); // Next item becomes current
+                m_hiddenWins.append(emb);
+                emb = m_shownWins.current();
+            }
+            else
+                emb = m_shownWins.next();
+
+        for(TrayEmbed* emb = m_hiddenWins.first(); emb != 0L; )
+            if(!shouldHide(emb->embeddedWinId()))
+            {
+                emb = m_hiddenWins.take(); // Next item becomes current
+                m_shownWins.append(emb);
+                emb = m_hiddenWins.current();
+            }
+            else
+                emb = m_hiddenWins.next();
+
+        if(m_hiddenWins.isEmpty())
+            expandButton->hide();
+        else
+            expandButton->show();
+
+        updateVisibleWins();
+        layoutTray();
+        emit updateLayout();
+    }
+}
+
 void SystemTrayApplet::checkFrameVisibility()
 {
     if (!geometry().contains( mapFromGlobal( QCursor::pos() ) ))
@@ -220,10 +307,10 @@ void SystemTrayApplet::loadSettings()
 
 void SystemTrayApplet::systemTrayWindowAdded( WId w )
 {
-    for (QXEmbed* emb = m_Wins.first(); emb != 0L; emb = m_Wins.next())
-        if( emb->embeddedWinId() == w ) // we already manage it
-            return;
+    if( isWinManaged( w ) ) // we already manage it
+        return;
     embedWindow( w, true );
+    updateVisibleWins();
     layoutTray();
     emit updateLayout();
 
@@ -259,26 +346,118 @@ void SystemTrayApplet::embedWindow( WId 
         return;
     }
 
-    m_Wins.append(emb);
     connect(emb, SIGNAL(embeddedWindowDestroyed()), SLOT(updateTrayWindows()));
     emb->resize(24, 24);
-    emb->show();
+    if( shouldHide( w ) )
+    {
+        m_hiddenWins.append(emb);
+        expandButton->show();
+    }
+    else
+        m_shownWins.append(emb);
+
+    updateVisibleWins();
+}
+
+bool SystemTrayApplet::isWinManaged( WId w)
+{
+    for (QXEmbed* emb = m_shownWins.first(); emb != 0L; emb = m_shownWins.next())
+        if( emb->embeddedWinId() == w ) // we already manage it
+            return true;
+    for (QXEmbed* emb = m_hiddenWins.first(); emb != 0L; emb = m_hiddenWins.next())
+        if( emb->embeddedWinId() == w ) // we already manage it
+            return true;
+    return false;
+}
+
+bool SystemTrayApplet::shouldHide( WId w)
+{
+    KConfig *conf = config();
+    conf->setGroup("HiddenTrayIcons");
+
+    if(conf->readBoolEntry( getWinName(w), false ))
+        return true;
+
+    return false;
+}
+
+QString SystemTrayApplet::getWinName( WId w )
+{
+    char *n;
+    XFetchName( qt_xdisplay(), w, &n);
+    QString name(n);
+    XFree(n);
+    return name;
+}
+
+void SystemTrayApplet::updateVisibleWins()
+{
+    if(m_visibleWins)
+        delete m_visibleWins;
+
+    if(!showHidden)
+    {
+        m_visibleWins = new TrayEmbedList(m_shownWins);
+        for (TrayEmbed* emb = m_hiddenWins.first(); emb != 0L; emb = \
m_hiddenWins.next()) +            emb->hide();
+    }
+    else
+    {
+        m_visibleWins = new TrayEmbedList(m_hiddenWins);
+        for (TrayEmbed* emb = m_shownWins.first(); emb != 0L; emb = \
m_shownWins.next()) +            m_visibleWins->append(emb);
+    }
+
+    for (TrayEmbed* emb = m_visibleWins->first(); emb != 0L; emb = \
m_visibleWins->next()) +        emb->show();
+}
+
+void SystemTrayApplet::expand()
+{
+    showHidden = true;
+    expandButton->setIconSet( KGlobal::iconLoader()->loadIconSet("1rightarrow", \
KIcon::NoGroup, +                                                                 \
KIcon::SizeSmall, false) ); +    updateVisibleWins();
+    layoutTray();
+    emit updateLayout();
+}
+
+void SystemTrayApplet::retract()
+{
+    showHidden = false;
+    expandButton->setIconSet( KGlobal::iconLoader()->loadIconSet("1leftarrow", \
KIcon::NoGroup, +                                                                 \
KIcon::SizeSmall, false) ); +    updateVisibleWins();
+    layoutTray();
+    emit updateLayout();
 }
 
 void SystemTrayApplet::updateTrayWindows()
 {
-    TrayEmbed* emb = m_Wins.first();
-    while ((emb = m_Wins.current()) != 0L) {
+    TrayEmbed* emb = m_shownWins.first();
+    while ((emb = m_shownWins.current()) != 0L) {
         WId wid = emb->embeddedWinId();
         if ((wid == 0)
              || ( emb->kdeTray() && \
!kwin_module->systemTrayWindows().contains(wid)))  {
 //            emb->setMapAfterRelease( false ); // don't map, they went away on \
                their own
-            m_Wins.remove(emb);
+            m_shownWins.remove(emb);
         }
         else
-            m_Wins.next();
+            m_shownWins.next();
     }
+    emb = m_hiddenWins.first();
+    while ((emb = m_hiddenWins.current()) != 0L) {
+        WId wid = emb->embeddedWinId();
+        if ((wid == 0)
+             || ( emb->kdeTray() && \
!kwin_module->systemTrayWindows().contains(wid))) +            \
m_hiddenWins.remove(emb); +        else
+            m_hiddenWins.next();
+    }
+    if(m_hiddenWins.isEmpty())
+        expandButton->hide();
+    updateVisibleWins();
     layoutTray();
     emit updateLayout();
 }
@@ -286,7 +465,15 @@ void SystemTrayApplet::updateTrayWindows
 int SystemTrayApplet::maxIconWidth() const
 {
     int largest = 24;
-    for (TrayEmbedList::const_iterator emb = m_Wins.begin(); emb != m_Wins.end(); \
++emb) + 
+    if( expandButton->isVisible() )
+    {
+        int width = expandButton->size().width();
+        if (width > largest)
+            largest = width;
+    }
+
+   for (TrayEmbedList::const_iterator emb = m_visibleWins->begin(); emb != \
m_visibleWins->end(); ++emb)  {
         if (*emb == 0)
         {
@@ -306,7 +493,15 @@ int SystemTrayApplet::maxIconWidth() con
 int SystemTrayApplet::maxIconHeight() const
 {
     int largest = 24;
-    for (TrayEmbedList::const_iterator emb = m_Wins.begin(); emb != m_Wins.end(); \
++emb) +
+    if( expandButton->isVisible() )
+    {
+        int height = expandButton->size().height();
+        if (height > largest)
+            largest = height;
+    }
+
+    for (TrayEmbedList::const_iterator emb = m_visibleWins->begin(); emb != \
m_visibleWins->end(); ++emb)  {
         if (*emb == 0)
         {
@@ -326,6 +521,9 @@ int SystemTrayApplet::maxIconHeight() co
 int SystemTrayApplet::widthForHeight(int h) const
 {
     int iconWidth = maxIconWidth(), iconHeight = maxIconHeight();
+    int iconCount = m_visibleWins->count();
+    if( expandButton->isVisible() )
+        iconCount++;
 
     if (h < iconHeight)
     {
@@ -333,7 +531,7 @@ int SystemTrayApplet::widthForHeight(int
         h = iconHeight;
     }
 
-    int ret = ( ( ( m_Wins.count() - 1 ) / ( h / iconHeight ) ) + 1 ) * iconWidth + \
4; +    int ret = ( ( ( iconCount - 1 ) / ( h / iconHeight ) ) + 1 ) * iconWidth + 4;
 
     if (ret < iconWidth + 4)
     {
@@ -346,13 +544,16 @@ int SystemTrayApplet::widthForHeight(int
 int SystemTrayApplet::heightForWidth(int w) const
 {
     int iconWidth = maxIconWidth(), iconHeight = maxIconHeight();
+    int iconCount = m_visibleWins->count();
+    if( expandButton->isVisible() )
+        iconCount++;
 
     if (w < iconWidth)
     {
         // avoid div by 0 later
         w = iconWidth;
     }
-    int ret = ( ( ( m_Wins.count() - 1 ) / ( w / iconWidth ) ) + 1 ) * iconHeight + \
4; +    int ret = ( ( ( iconCount - 1 ) / ( w / iconWidth ) ) + 1 ) * iconHeight + 4;
 
     if (ret < iconHeight + 4)
     {
@@ -369,7 +570,11 @@ void SystemTrayApplet::resizeEvent( QRes
 
 void SystemTrayApplet::layoutTray()
 {
-    if (m_Wins.count() == 0)
+    int iconCount = m_visibleWins->count();
+    if( expandButton->isVisible() )
+        iconCount++;
+
+    if (iconCount == 0)
         return;
 
     /* heightWidth = height or width in pixels (depends on orientation())
@@ -388,7 +593,14 @@ void SystemTrayApplet::layoutTray()
         heightWidth = heightWidth < iconWidth ? iconWidth : heightWidth; // to avoid \
nbrOfLines=0  nbrOfLines = heightWidth / iconWidth;
         spacing = ( heightWidth - iconWidth*nbrOfLines ) / ( nbrOfLines + 1 );
-        for (emb = m_Wins.first(); emb != 0L; emb = m_Wins.next()) {
+        if( expandButton->isVisible() ) {
+            line = i % nbrOfLines;
+            expandButton->move( spacing*(line+1) + line*iconWidth, 2 + \
col*iconHeight ); +            if ( line + 1 == nbrOfLines )
+                col++;
+            i++;
+        }
+        for (emb = m_visibleWins->first(); emb != 0L; emb = m_visibleWins->next()) {
             line = i % nbrOfLines;
             emb->move( spacing*(line+1) + line*iconWidth, 2 + col*iconHeight );
             if ( line + 1 == nbrOfLines )
@@ -401,7 +613,14 @@ void SystemTrayApplet::layoutTray()
         heightWidth = heightWidth < iconHeight ? iconHeight : heightWidth; // to \
avoid nbrOfLines=0  nbrOfLines = heightWidth / iconHeight;
         spacing = ( heightWidth - iconHeight*nbrOfLines ) / ( nbrOfLines + 1 );
-        for (emb = m_Wins.first(); emb != 0L; emb = m_Wins.next()) {
+        if( expandButton->isVisible() ) {
+            line = i % nbrOfLines;
+            expandButton->move( 2 + col*iconWidth, spacing*(line+1) + \
line*iconHeight ); +            if ( line + 1 == nbrOfLines )
+                col++;
+            i++;
+        }
+        for (emb = m_visibleWins->first(); emb != 0L; emb = m_visibleWins->next()) {
             line = i % nbrOfLines; 
             emb->move( 2 + col*iconWidth, spacing*(line+1) + line*iconHeight );
             if ( line + 1 == nbrOfLines )
@@ -414,12 +633,20 @@ void SystemTrayApplet::layoutTray()
 
 void SystemTrayApplet::paletteChange(const QPalette & /* oldPalette */)
 {
-    for (QXEmbed* emb = m_Wins.first(); emb != 0L; emb = m_Wins.next()) {
+    for (QXEmbed* emb = m_shownWins.first(); emb != 0L; emb = m_shownWins.next()) {
         emb->hide();
         emb->show();
     }
 }
 
+void SystemTrayApplet::toggleExpanded()
+{
+    if(showHidden)
+        retract();
+    else
+        expand();
+}
+
 TrayEmbed::TrayEmbed( bool kdeTray, QWidget* parent )
     : QXEmbed( parent ), kde_tray( kdeTray )
 {
Index: systemtrayapplet.h
===================================================================
RCS file: /home/kde/kdebase/kicker/applets/systemtray/systemtrayapplet.h,v
retrieving revision 1.26
diff -u -3 -p -r1.26 systemtrayapplet.h
--- systemtrayapplet.h	13 Oct 2004 01:19:33 -0000	1.26
+++ systemtrayapplet.h	22 Oct 2004 17:13:08 -0000
@@ -27,11 +27,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE 
 #include <dcopobject.h>
 #include <kpanelapplet.h>
 #include <kapplication.h>
+#include <kpushbutton.h>
 
 #include <qptrlist.h>
 
 #include <qxembed.h>
-#include <qpushbutton.h>
 
 class QTimer;
 class KWinModule;
@@ -62,6 +62,7 @@ protected:
     bool x11Event( XEvent *e );
     void enterEvent( QEvent * );
     void leaveEvent( QEvent * );
+    void preferences();
 
 protected slots:
     void systemTrayWindowAdded( WId );
@@ -69,15 +70,26 @@ protected slots:
     void layoutTray();
     void paletteChange(const QPalette & /* oldPalette */);
     void checkFrameVisibility();
+    void toggleExpanded();
 
 private:
     void embedWindow( WId w, bool kde_tray );
-    TrayEmbedList m_Wins;
+    bool isWinManaged( WId w);
+    bool shouldHide( WId w);
+    QString getWinName( WId w );
+    void updateVisibleWins();
+    void expand();
+    void retract();
+    TrayEmbedList* m_visibleWins;
+    TrayEmbedList m_shownWins;
+    TrayEmbedList m_hiddenWins;
     KWinModule *kwin_module;
     Atom net_system_tray_selection;
     Atom net_system_tray_opcode;
     bool showFrame;
     QTimer* hideFrameTimer;
+    bool showHidden;
+    KPushButton *expandButton;
 };
 
 class TrayEmbed : public QXEmbed



>> Visit http://mail.kde.org/mailman/listinfo/kde-devel#unsub to unsubscribe <<


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic