Logo Search packages:      
Sourcecode: qps version File versions  Download package

trayicon.cpp

// from psi ,  modified and cleand by fasthyun@magicn.com

/* trayicon_x11.cpp - X11 trayicon (for use with KDE and GNOME)
 * Copyright (C) 2003  Justin Karneges
 * GNOME2 Notification Area support: Tomasz Sterna
 */

#include    "trayicon.h"
#include    "qpopupmenu.h"
#include    "global.h"

#include    <qapplication.h>
#include    <qimage.h>
#include    <qpixmap.h>
#include    <qtooltip.h>
#include    <qpainter.h>

#include    <X11/Xlib.h>
#include    <X11/Xutil.h>
#include    <X11/Xatom.h>


//----------------------------------------------------------------------------
// common stuff
//----------------------------------------------------------------------------
// for Gnome2 Notification Area
static XErrorHandler old_handler = 0;
static int dock_xerror = 0;
extern "C" int dock_xerrhandler(Display* dpy, XErrorEvent* err)
{
      dock_xerror = err->error_code;
      return old_handler(dpy, err);
}

static void trap_errors()
{
      dock_xerror = 0;
      old_handler = XSetErrorHandler(dock_xerrhandler);
}

static bool untrap_errors()
{
      XSetErrorHandler(old_handler);
      return (dock_xerror == 0);
        
}

static bool send_message(
      Display* dpy,     /* display */
      Window w,   /* sender (tray icon window) */
      long message,     /* message opcode */
      long data1, /* message data 1 */
      long data2, /* message data 2 */
      long data3  /* message data 3 */
) {
      XEvent ev;

      memset(&ev, 0, sizeof(ev));
      ev.xclient.type = ClientMessage;
      ev.xclient.window = w;
      ev.xclient.message_type = XInternAtom (dpy, "_NET_SYSTEM_TRAY_OPCODE", False );
      ev.xclient.format = 32;
      ev.xclient.data.l[0] = CurrentTime;
      ev.xclient.data.l[1] = message;
      ev.xclient.data.l[2] = data1;
      ev.xclient.data.l[3] = data2;
      ev.xclient.data.l[4] = data3;

      trap_errors();
      XSendEvent(dpy, w, False, NoEventMask, &ev);
      XSync(dpy, False);
      return untrap_errors();
}

#define SYSTEM_TRAY_REQUEST_DOCK    0
#define SYSTEM_TRAY_BEGIN_MESSAGE   1
#define SYSTEM_TRAY_CANCEL_MESSAGE  2


TrayIcon::TrayIcon( const QPixmap &icon, const QString &tooltip, 
            QPopupMenu *popup, QWidget *parent, const char *name )
: QWidget(parent, name,WRepaintNoErase|Qt::WType_TopLevel ), pop(popup), pm(icon), tip(tooltip)
{
      systray_window=None;
      inTray=false;
      flag_systray_ready=false;
      v_isWMDock = false;

      //setBackgroundMode(  Qt::NoBackground); //flicker free , dont use this
      setBackgroundMode(X11ParentRelative);
      setMinimumSize(22,22);
      if ( !pm.width() || !pm.height() )

            pm = QPixmap( 45, 45 );

}

/*!
  Removes the icon from the system tray and frees all allocated resources.
*/
TrayIcon::~TrayIcon()
{
      sysRemove();
}

extern bool flag_session_start;

void TrayIcon::newTrayOwner()
{
    sysRemove();  
    sysInstall(); 
    //for GNOME-panel 
//DEL    if(flag_session_start)    qps->hide();
}


/*!
  Sets the context menu to \a popup. The context menu will pop up when the
  user clicks the system tray entry with the right mouse button.
*/
void TrayIcon::setPopup( QPopupMenu* popup )
{
    pop = popup;
}

/*!
  Returns the current popup menu.
*/
QPopupMenu* TrayIcon::popup() const
{
    return pop;
}


QPixmap TrayIcon::icon() const
{
    return pm;
}

/*!
  \property TrayIcon::toolTip
  \brief the tooltip for the system tray entry

  On some systems, the tooltip's length is limited and will be truncated as necessary.
*/
void TrayIcon::setToolTip( const QString &tooltip )
{
    tip = tooltip;
    sysUpdateToolTip();
}


void TrayIcon::mouseMoveEvent( QMouseEvent *e )
{
    e->ignore();
}

void TrayIcon::mousePressEvent( QMouseEvent *e )
{
#ifndef Q_WS_WIN
      // This is for X11, menus appear on mouse press
      // I'm not sure whether Mac should be here or below.. Somebody check?
      switch ( e->button() ) {
            case RightButton:
                  if ( pop ) {
                        pop->popup( e->globalPos() );
                        e->accept();
                  }
                  break;
            case LeftButton:
            case MidButton:
                  //emit clicked( e->globalPos(), e->button() );
                  emit clicked( e->globalPos());
                  break;
            default:
                  break;
      }
#endif
      e->ignore();
}

void TrayIcon::mouseReleaseEvent( QMouseEvent *e )
{
#ifdef Q_WS_WIN
// This is for Windows, where menus appear on mouse release
      switch ( e->button() ) {
            case RightButton:
                  if ( pop ) {
                        // Necessary to make keyboard focus
                        // and menu closing work on Windows.
                        pop->setActiveWindow();
                        pop->popup( e->globalPos() );
                        pop->setActiveWindow();
                        e->accept();
                  }
                  break;
            case LeftButton:
            case MidButton:
                  //emit clicked( e->globalPos(), e->button() );
                  emit clicked( e->globalPos());
                  break;
            default:
                  break;
      }
#endif
      e->ignore();
}

void TrayIcon::mouseDoubleClickEvent( QMouseEvent *e )
{
      if ( e->button() == LeftButton )
            emit doubleClicked( e->globalPos() );
      e->accept();
}

//----------------------------------------------------------------------------
// TrayIcon
//----------------------------------------------------------------------------

// DRAFT Code (by fasthyun@magicn.com) 
void TrayIcon::init_TrayIconFreeDesktop()
{
      //XChangeProperty( display, win,
      //XInternAtom(display, "_NET_WM_NAME", False),
      // XInternAtom(display, "UTF8_STRING", False),8, PropModeReplace, (unsigned char *) utf8_buffer,count);

      Display *dsp = x11Display(); // get the display
      //strange...
      /*
      WId win = qps->winId();        // get the window

      XClassHint classhint;
      classhint.res_name  = (char*)"qps_icon";
      classhint.res_class = (char*)"qps";
      XSetClassHint(dsp, win, &classhint);
      XWMHints *hints;  // hints
      hints = XGetWMHints(dsp, win);  // init hints
      hints->initial_state = WithdrawnState;
      hints->icon_x = 0;
      hints->icon_y = 0;
      hints->icon_window = winId();
      hints->window_group = win;  // set the window hint
      hints->flags = WindowGroupHint | IconWindowHint | IconPositionHint | StateHint; // set the window group hint
      XSetWMHints(dsp, win, hints);  // set the window hints for WM to use.
      XFree( hints );
      */
      //resize(22,22);

      Screen *screen = XDefaultScreenOfDisplay(dsp); // get the screen
      int screen_id = XScreenNumberOfScreen(screen); // and it's number
      //printf("winId()=%ld\n",winId());
      //printf("qps->winId()=%ld\n",qps->winId());
      
      // tell X that we want to see ClientMessage and Deleted events, which
      // are picked up by QApplication::x11EventFilter
      Window root_window = QApplication::desktop()->winId();
      XWindowAttributes attr;

      XGetWindowAttributes(dsp, root_window, &attr);
      // *** important : get the event that systray start -> window manager changer 
      XSelectInput(dsp, root_window, attr.your_event_mask | StructureNotifyMask); 

      char buf[32];
      snprintf(buf, sizeof(buf), "_NET_SYSTEM_TRAY_S%d", screen_id);
      //DEL printf("%s\n",buf);
      selection_atom = XInternAtom(dsp, buf, false);
      manager_atom = XInternAtom (dsp,"MANAGER", false); // make a atom for later
//    Atom orientation_atom = XInternAtom (xdisplay,"_NET_SYSTEM_TRAY_ORIENTATION",False);

      XGrabServer(dsp); 
      // X server reply the owner of the "atom", Kicker, Gnome-panel window_id
      systray_window = XGetSelectionOwner(dsp, selection_atom); 
      if ( systray_window != None )
            // *** important : get the event that systray destroy from Systray_Window
            //XSelectInput(dsp, systray_window, StructureNotifyMask|PropertyChangeMask);
            XSelectInput(dsp, systray_window, StructureNotifyMask);
      XUngrabServer(dsp);
      XFlush(dsp);

      if ( systray_window != None )
      {
            //send_message(dsp, systray_window, SYSTEM_TRAY_REQUEST_DOCK, winId(), 0, 0);
            if(send_message(dsp, systray_window, SYSTEM_TRAY_REQUEST_DOCK, winId(), 0, 0))
                  ; //printf("send_message() ok !!\n");
            else
            {
                  printf("***** send_message() Fail !!\n");
                  setSysTray(false);
                  return;
            }
      }
      else
      {
            /// printf("Qps: NO SYSTEM_TRAY .. ok ! \n");
            setSysTray(false);
            return;
      }
      
      flag_systray_ready=true;
      setSysTray(true);
      //show();
}

class TrayIconPrivate : public QWidget
{
      public:
      TrayIconPrivate(QWidget *p);
      ~TrayIconPrivate() { }
};

TrayIconPrivate::TrayIconPrivate(QWidget *p)
: QWidget(0, 0, WRepaintNoErase)
{
      setGeometry (-100, -100, 10,10);  
}

void TrayIcon::init_WindowMakerDock()
{
      Display *dsp = x11Display();
      Display *display = x11Display();
      Status          stat;
      XClassHint  *classhint;
      XWMHints    *hints;
      
      Window      window = None;
      Window      icon_window = None;
      Window      root;

      //TrayIconPrivate *t=new TrayIconPrivate(0);
      TrayIconPrivate *t=new TrayIconPrivate(this);
      //reparent(t,0,QPoint(0,0));  // dont reparent !!
      window = t->winId();
      icon_window = winId();

      classhint = XAllocClassHint();
      if (classhint == NULL) {
            fprintf(stderr, " can't allocate memory for wm hints!\n");
            exit(1);
      }

      classhint->res_class = "DockApp";
      classhint->res_name = "qpsdock";
      //XSetClassHint(display, window, classhint);
      XFree(classhint);

      hints = XAllocWMHints();
      //hints = XGetWMHints(dsp, window); 
      hints->flags = WindowGroupHint 
            | IconWindowHint 
            | StateHint;      
      hints->window_group = window;       // set the window hint
      hints->initial_state = WithdrawnState;    // initial state
      hints->icon_window = icon_window;
      XSetWMHints(dsp, window, hints);     // set the window hints for WM to use.


      t->show();
      dummy_window=t->winId();
      return;

}

void TrayIcon::sysInstall()
{
      if ( v_isWMDock )
      {
            //setFixedSize(45,45); // setIconSize
            init_WindowMakerDock();
            setSysTray(true);
      }
      else
      {
            //check hasSysTray !
            init_TrayIconFreeDesktop();
      }
      sysUpdateToolTip();
}

void TrayIcon::sysRemove()
{
      //printf("sysRemove\n");
      systray_window=None;
      inTray=false;
      boolSysTray=v_isWMDock=false;
      hide();
}

void TrayIcon::sysUpdateToolTip()
{
      if ( tip.isEmpty() )
            QToolTip::remove(this);
      else
            QToolTip::add(this, tip);
      return;
}

/*!
  \brief the system tray icon.
*/
void TrayIcon::setIcon( const QPixmap &pix )
{
      if(isShown()==false) return;
            // X Error:BadWindow (invalid Window parameter) 3,Major:20,minor:0 occur
      QWidget::setIcon(pix);  // why X Error ?
      pm = pix;
      update(); 
}

// this will be called  before shown !!
void TrayIcon::paintEvent(QPaintEvent *)
{
      QPainter p(this);
      int w,h;
      if(isShown()==false) // ** important to prevent X11 Error !!
      {
            //printf("paintEvent(): hidden\n");
            return; 
      }     
      w=width()/2 ;
      h=height()/2;
      p.drawPixmap(w - pm.width()/2, h - pm.height()/2, pm);
}

void TrayIcon::closeEvent ( QCloseEvent * e )
{
      e->accept(); //important for session logout !!
      qps->save_settings();
}

// called after size changed
void TrayIcon::resizeEvent ( QResizeEvent *e ) 
{
      int w,h;
      if(isShown()==false)
            return;  // X11 error !!

      //printf("resizeEvent(): w=%d,h=%d\n",width(),height());
      w=width();h=height();
      if ( w>60 && h>60) 
      {     
            w=w-14;h=h-14;
      }
      qps->setIconSize(w,h);
}


bool TrayIcon::x11Event(XEvent *xev)
{

      switch(xev->type)
      {
            case ReparentNotify: //what is this ? : too many called in WindowMaker !! why ?
                  //printf("*** --- ReparentNotify\n");
                  if(v_isWMDock){
                        XUnmapWindow( x11Display(), dummy_window );
                        show();
                  }
                  else
                  {
                        if(flag_systray_ready)
                        {                             
                              if(qps->isMinimized())  qps->hide();
                              show();
                        }
                  }
                  //return true;
                  break;
            case UnmapNotify:   // hide event
                  //call a hide event to qt 
                  //hide();
                  //return true;
                  break; // dont hide
      
            case MapNotify: // show event
                  //show();
                  //return true;
                  break; 
            case ClientMessage:
                  // cant' receive any thing !! 
                  break;
      }
      return false;
      
      switch(xev->type)
      {
            case ReparentNotify: //what is this ?
                  printf("*** ReparentNotify\n");
                  break;
                  //return true;
            case UnmapNotify:   // hide event
                  printf("*** UnmapNotify\n");
                  break;
                  //return true; // dont hide
            //    return false; 
            case MapRequest: 
                  printf("*** MapRequest\n");
                  return true;
            case Expose:      // ???
                  //printf("*** Expose\n");
                  return false;
            case MapNotify: // show event
                  //printf("*** MapNotify\n");
                  return false;
            case VisibilityNotify:
                  printf("*** VisibilityNotify\n");
                  return true;
            case NoExpose:
                  printf("*** NoExposure\n");
                  return true;
            case ResizeRequest:
                  printf("*** ResizeRequest\n");
                  return false;
            case SelectionNotify:
                  printf("*** SelectionNotify\n");
                  return true;
            case PropertyNotify:
                  printf("*** PropertyNotify\n");
                  break;
      
            default:
                  ;
            //    printf("X11=%d\n",(int)xev->type);

      }
      return false;
} 


// DRAFT CODE
// from eggtrayicon.c  by fasthyun@magicn.com
// if a tray show up after qps excuted
bool TrayIcon::checkNewTrayEvent ( XEvent *xev )
{
      static int count=0;
      //printf("checkNewTrayEvent\n");    
      Display *dsp = qps->x11Display();

      if (xev->type == ClientMessage &&
            xev->xclient.message_type == manager_atom &&
            xev->xclient.data.l[1] == selection_atom)
      {
            //newTrayOwner();
            sysRemove(); 
            sysInstall(); 
            printf("Qps: X11 Notify(systray) new start \n",count++);
            return true;
      }
      else if (xev->xany.window == systray_window)
      { 
            if (xev->xany.type == PropertyNotify )
                  printf("Qps: PropertyNotify  \n"); // ????

            if (xev->xany.type == DestroyNotify)
            {     

                  sysRemove();
                  qps->showNormal();
                  flag_systray_ready=false;  // *** important
                  printf("Qps: X11 Notify(systray) Destroyed \n");
            }
            return false;
      }

      //Atom xembed_atom = XInternAtom( qt_xdisplay(), "_XEMBED", FALSE );
      Atom xembed_atom = XInternAtom( x11Display(), "_XEMBED", FALSE );
      Atom atom = XInternAtom (x11Display(), "_NET_SYSTEM_TRAY_OPCODE", False );

      
      /*
      // uninitialized ?  xev->xclient.message_type 
      if (xev->xclient.message_type == xembed_atom){
            printf("*** X11 ClientMessage (_XEMBED)\n");
            inTray = true; // false !! sometimes calld ..
      }
      else 
            if (xev->xclient.message_type == atom){
                  printf("*** X11 ClientMessage (_NET_SYSTEM_TRAY_OPCODE)\n");
            }
      //else printf("*** X11 ClientMessage else %s\n",XGetAtomName(x11Display(),(Atom)xev->xclient.message_type));
      */
      
      return false;     
}



Generated by  Doxygen 1.6.0   Back to index