f65184802d
* NEW: Update copyright and license string in all source files. git-svn-id: svn://localhost/gambas/trunk@2241 867c0c6c-44f3-4631-809d-bfa615b0a4ec
596 lines
12 KiB
C++
596 lines
12 KiB
C++
/***************************************************************************
|
|
|
|
gtrayicon.cpp
|
|
|
|
(c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
***************************************************************************/
|
|
|
|
#include "widgets.h"
|
|
|
|
#ifdef GDK_WINDOWING_X11
|
|
#ifndef GAMBAS_DIRECTFB
|
|
#define GOT_TRAYICON
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef GOT_TRAYICON
|
|
#include <unistd.h>
|
|
#include <gdk/gdkx.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/extensions/shape.h>
|
|
#endif
|
|
#include "gapplication.h"
|
|
#include "gmouse.h"
|
|
#include "gtrayicon.h"
|
|
|
|
#define SYSTEM_TRAY_REQUEST_DOCK 0
|
|
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
|
|
#define SYSTEM_TRAY_CANCEL_MESSAGE 2
|
|
|
|
#define OPCODE "_NET_SYSTEM_TRAY_OPCODE"
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
Default picture
|
|
|
|
******************************************************************************/
|
|
|
|
#include "gb.form.trayicon.h"
|
|
|
|
/*****************************************************************************
|
|
|
|
Low level stuff
|
|
|
|
******************************************************************************/
|
|
#ifdef GOT_TRAYICON
|
|
void XTray_RequestDock(Display *xdisplay,Window icon)
|
|
{
|
|
Window xmanager=None;
|
|
XClientMessageEvent ev;
|
|
Atom OpCodeAtom;
|
|
Screen *xscreen;
|
|
char buf[256];
|
|
Atom selection_atom;
|
|
|
|
buf[0]=0;
|
|
|
|
xscreen=DefaultScreenOfDisplay(xdisplay);
|
|
sprintf(buf,"_NET_SYSTEM_TRAY_S%d",XScreenNumberOfScreen (xscreen));
|
|
selection_atom = XInternAtom (xdisplay,buf,0);
|
|
|
|
XGrabServer (xdisplay);
|
|
|
|
xmanager = XGetSelectionOwner (xdisplay,selection_atom);
|
|
if ( xmanager != None)
|
|
XSelectInput (xdisplay,xmanager,StructureNotifyMask);
|
|
|
|
XUngrabServer (xdisplay);
|
|
XFlush (xdisplay);
|
|
|
|
|
|
/***********************************************
|
|
Dock Tray Icon
|
|
************************************************/
|
|
OpCodeAtom=XInternAtom(xdisplay,OPCODE,0);
|
|
|
|
ev.type = ClientMessage;
|
|
ev.window = xmanager;
|
|
ev.message_type = OpCodeAtom;
|
|
ev.format = 32;
|
|
ev.data.l[0] = 0;
|
|
ev.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
|
|
ev.data.l[2] = icon;
|
|
ev.data.l[3] = 0;
|
|
ev.data.l[4] = 0;
|
|
|
|
XSendEvent (xdisplay,xmanager, 0, NoEventMask, (XEvent *)&ev);
|
|
XSync (xdisplay, 0);
|
|
usleep(10000);
|
|
}
|
|
|
|
void XTray_getSize(Display *xdisplay,Window icon,unsigned int *w,unsigned int *h)
|
|
{
|
|
XWindowAttributes Attr;
|
|
|
|
XGetWindowAttributes(xdisplay,icon,&Attr);
|
|
|
|
if (w) *w=Attr.width;
|
|
if (h) *h=Attr.height;
|
|
|
|
}
|
|
|
|
void XTray_getPosition(Display *xdisplay,Window icon,unsigned int *x,unsigned int *y)
|
|
{
|
|
Window rwin;
|
|
Window pwin;
|
|
Window *cwin;
|
|
unsigned int count;
|
|
XWindowAttributes Attr;
|
|
Window w=icon;
|
|
|
|
if (x) *x=0;
|
|
if (y) *y=0;
|
|
|
|
do
|
|
{
|
|
XQueryTree(xdisplay,w,&rwin,&pwin,&cwin,&count);
|
|
if (cwin) XFree(cwin);
|
|
if (pwin)
|
|
{
|
|
XGetWindowAttributes(xdisplay, pwin,&Attr);
|
|
if (x) (*x)+=Attr.x;
|
|
if (y) (*y)+=Attr.y;
|
|
w=pwin;
|
|
}
|
|
} while (pwin);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*************************************************************************
|
|
|
|
gTrayIcon
|
|
|
|
**************************************************************************/
|
|
|
|
static bool tray_enterleave(GtkWidget *widget, GdkEventCrossing *e,gTrayIcon *data)
|
|
{
|
|
if (gApplication::loopLevel() > data->loopLevel()) return false;
|
|
if (e->type==GDK_ENTER_NOTIFY)
|
|
{
|
|
if (data->onEnter) data->onEnter(data);
|
|
}
|
|
else
|
|
{
|
|
if (data->onLeave) data->onLeave(data);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void tray_destroy (GtkWidget *object,gTrayIcon *data)
|
|
{
|
|
data->cleanUp();
|
|
}
|
|
|
|
static gboolean tray_event(GtkWidget *widget, GdkEvent *event,gTrayIcon *data)
|
|
{
|
|
if (!gApplication::userEvents()) return false;
|
|
if (gApplication::loopLevel() > data->loopLevel()) return false;
|
|
|
|
if (event->type==GDK_2BUTTON_PRESS)
|
|
{
|
|
if (data->onDoubleClick) data->onDoubleClick(data);
|
|
return false;
|
|
}
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
static gboolean tray_down (GtkWidget *widget,GdkEventButton *event,gTrayIcon *data)
|
|
{
|
|
if (!gApplication::userEvents()) return false;
|
|
if (gApplication::loopLevel() > data->loopLevel()) return false;
|
|
|
|
if (data->onMousePress)
|
|
{
|
|
gMouse::validate();
|
|
gMouse::setMouse((int)event->x, (int)event->y, event->button, event->state);
|
|
data->onMousePress(data);
|
|
gMouse::invalidate();
|
|
}
|
|
|
|
if (event->button==3)
|
|
if (data->onMenu)
|
|
data->onMenu(data);
|
|
|
|
return false;
|
|
}
|
|
|
|
static gboolean tray_up (GtkWidget *widget,GdkEventButton *event,gTrayIcon *data)
|
|
{
|
|
if (!gApplication::userEvents()) return false;
|
|
if (gApplication::loopLevel() > data->loopLevel()) return false;
|
|
|
|
if (data->onMouseRelease)
|
|
{
|
|
gMouse::validate();
|
|
gMouse::setMouse((int)event->x, (int)event->y, event->button, event->state);
|
|
data->onMouseRelease(data);
|
|
gMouse::invalidate();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static gboolean cb_menu(GtkWidget *widget, gTrayIcon *data)
|
|
{
|
|
if (!gApplication::userEvents()) return false;
|
|
if (gApplication::loopLevel() > data->loopLevel()) return false;
|
|
|
|
if (data->onMenu)
|
|
data->onMenu(data);
|
|
|
|
return false;
|
|
}
|
|
|
|
static gboolean tray_focus_In(GtkWidget *widget,GdkEventFocus *event,gTrayIcon *data)
|
|
{
|
|
if (!gApplication::allEvents()) return false;
|
|
if (gApplication::loopLevel() > data->loopLevel()) return false;
|
|
|
|
if (data->onFocusEnter) data->onFocusEnter(data);
|
|
return false;
|
|
}
|
|
|
|
static gboolean tray_focus_Out(GtkWidget *widget,GdkEventFocus *event,gTrayIcon *data)
|
|
{
|
|
if (!gApplication::allEvents()) return false;
|
|
if (gApplication::loopLevel() > data->loopLevel()) return false;
|
|
|
|
if (data->onFocusLeave) data->onFocusLeave(data);
|
|
return false;
|
|
}
|
|
|
|
static gboolean cb_scroll(GtkWidget *widget, GdkEventScroll *event, gTrayIcon *data)
|
|
{
|
|
int dt = 0;
|
|
int ort = 0;
|
|
|
|
if (!gApplication::userEvents()) return false;
|
|
if (gApplication::loopLevel() > data->loopLevel()) return false;
|
|
|
|
if (data->onMouseWheel)
|
|
{
|
|
switch (event->direction)
|
|
{
|
|
case GDK_SCROLL_UP: dt=1; ort=1; break;
|
|
case GDK_SCROLL_DOWN: dt=-1; ort=1; break;
|
|
case GDK_SCROLL_LEFT: dt=-1; ort=0; break;
|
|
case GDK_SCROLL_RIGHT: dt=1; ort=0; break;
|
|
}
|
|
|
|
gMouse::validate();
|
|
gMouse::setMouse((int)event->x, (int)event->y, 0, event->state);
|
|
gMouse::setWheel(dt, ort);
|
|
data->onMouseWheel(data);
|
|
gMouse::invalidate();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static gboolean cb_expose(GtkWidget *widget, GdkEventExpose *e, gTrayIcon *data)
|
|
{
|
|
gPicture *pic = data->getIcon();
|
|
|
|
gdk_window_clear(widget->window);
|
|
gdk_draw_pixbuf(widget->window,
|
|
widget->style->black_gc,
|
|
pic->getPixbuf(),
|
|
0,
|
|
0,
|
|
(widget->allocation.width - pic->width()) / 2,
|
|
(widget->allocation.height - pic->height()) / 2,
|
|
-1,
|
|
-1,
|
|
GDK_RGB_DITHER_NORMAL,
|
|
0, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
GList *gTrayIcon::trayicons = NULL;
|
|
gPicture *gTrayIcon::_default_icon = NULL;
|
|
|
|
#ifdef GOT_TRAYICON
|
|
|
|
gTrayIcon::gTrayIcon()
|
|
{
|
|
plug = NULL;
|
|
//icon = NULL;
|
|
buftext = NULL;
|
|
_icon = NULL;
|
|
_style = NULL;
|
|
_loopLevel = 0;
|
|
|
|
//onHide=false;
|
|
onMousePress=NULL;
|
|
onMouseRelease=NULL;
|
|
onMenu=NULL;
|
|
onFocusEnter=NULL;
|
|
onFocusLeave=NULL;
|
|
onDoubleClick=NULL;
|
|
onEnter=NULL;
|
|
onLeave=NULL;
|
|
|
|
trayicons = g_list_append(trayicons, (gpointer)this);
|
|
}
|
|
|
|
gTrayIcon::~gTrayIcon()
|
|
{
|
|
gPicture::assign(&_icon);
|
|
|
|
if (buftext)
|
|
{
|
|
g_free(buftext);
|
|
buftext = NULL;
|
|
}
|
|
|
|
if (plug)
|
|
gtk_widget_destroy(plug);
|
|
|
|
setVisible(false);
|
|
trayicons = g_list_remove(trayicons, (gpointer)this);
|
|
|
|
if (!trayicons && _default_icon)
|
|
{
|
|
delete _default_icon;
|
|
_default_icon = NULL;
|
|
}
|
|
|
|
if (_style)
|
|
g_object_unref(_style);
|
|
|
|
if (onDestroy) (*onDestroy)(this);
|
|
}
|
|
|
|
gPicture *gTrayIcon::defaultIcon()
|
|
{
|
|
if (!_default_icon)
|
|
{
|
|
GdkPixbuf *img = gdk_pixbuf_new_from_xpm_data((const char**)_default_trayicon);
|
|
_default_icon = new gPicture(img);
|
|
}
|
|
|
|
return _default_icon;
|
|
}
|
|
|
|
void gTrayIcon::refresh()
|
|
{
|
|
if (plug)
|
|
gtk_widget_queue_draw(plug);
|
|
}
|
|
|
|
void gTrayIcon::setPicture(gPicture *picture)
|
|
{
|
|
gPicture::assign(&_icon, picture);
|
|
refresh();
|
|
}
|
|
|
|
char* gTrayIcon::toolTip()
|
|
{
|
|
return buftext;
|
|
}
|
|
|
|
void gTrayIcon::updateTooltip()
|
|
{
|
|
if (!plug)
|
|
return;
|
|
|
|
gtk_tooltips_set_tip(gApplication::tipHandle(), plug, buftext, NULL);
|
|
}
|
|
|
|
void gTrayIcon::setToolTip(char* vl)
|
|
{
|
|
if (buftext)
|
|
g_free(buftext);
|
|
|
|
buftext = vl && *vl ? g_strdup(vl) : NULL;
|
|
updateTooltip();
|
|
}
|
|
|
|
bool gTrayIcon::isVisible()
|
|
{
|
|
return (bool)plug;
|
|
}
|
|
|
|
void gTrayIcon::cleanUp()
|
|
{
|
|
if (plug)
|
|
{
|
|
plug = NULL;
|
|
g_object_unref(_style);
|
|
_style = NULL;
|
|
}
|
|
}
|
|
|
|
void gTrayIcon::setVisible(bool vl)
|
|
{
|
|
if (vl)
|
|
{
|
|
Window win;
|
|
gPicture *pic;
|
|
|
|
if (!plug)
|
|
{
|
|
_loopLevel = gApplication::loopLevel() + 1;
|
|
|
|
plug = gtk_plug_new(0);
|
|
gtk_widget_set_double_buffered(plug, FALSE);
|
|
|
|
_style = gtk_style_copy(GTK_WIDGET(plug)->style);
|
|
_style->bg_pixmap[GTK_STATE_NORMAL] = (GdkPixmap*)GDK_PARENT_RELATIVE;
|
|
gtk_widget_set_style(GTK_WIDGET(plug), _style);
|
|
|
|
pic = getIcon();
|
|
gtk_widget_set_size_request(plug, pic->width(), pic->height());
|
|
|
|
//gtk_widget_realize (plug);
|
|
|
|
gtk_widget_show_all(plug);
|
|
|
|
gtk_widget_add_events(plug, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK
|
|
| GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
|
|
|
|
g_signal_connect(G_OBJECT(plug),"destroy",G_CALLBACK(tray_destroy),(gpointer)this);
|
|
g_signal_connect(G_OBJECT(plug),"event",G_CALLBACK(tray_event),(gpointer)this);
|
|
g_signal_connect(G_OBJECT(plug),"button-release-event",G_CALLBACK(tray_up),(gpointer)this);
|
|
g_signal_connect(G_OBJECT(plug),"button-press-event",G_CALLBACK(tray_down),(gpointer)this);
|
|
g_signal_connect(G_OBJECT(plug),"focus-in-event",G_CALLBACK(tray_focus_In),(gpointer)this);
|
|
g_signal_connect(G_OBJECT(plug),"focus-out-event",G_CALLBACK(tray_focus_Out),(gpointer)this);
|
|
g_signal_connect(G_OBJECT(plug),"enter-notify-event",G_CALLBACK(tray_enterleave),(gpointer)this);
|
|
g_signal_connect(G_OBJECT(plug),"leave-notify-event",G_CALLBACK(tray_enterleave),(gpointer)this);
|
|
g_signal_connect(G_OBJECT(plug),"popup-menu",G_CALLBACK(cb_menu),(gpointer)this);
|
|
g_signal_connect(G_OBJECT(plug),"scroll-event",G_CALLBACK(cb_scroll),(gpointer)this);
|
|
g_signal_connect(G_OBJECT(plug),"expose-event", G_CALLBACK(cb_expose), (gpointer)this);
|
|
|
|
win = gtk_plug_get_id(GTK_PLUG(plug));
|
|
|
|
XTray_RequestDock(gdk_display, win);
|
|
|
|
//gdk_window_set_back_pixmap(plug->window, (GdkPixmap *)GDK_PARENT_RELATIVE, FALSE);
|
|
//XSetWindowBackgroundPixmap(gdk_display, win, ParentRelative);
|
|
|
|
updateTooltip();
|
|
refresh();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (plug)
|
|
{
|
|
gtk_widget_destroy(plug);
|
|
}
|
|
}
|
|
}
|
|
|
|
long gTrayIcon::screenX()
|
|
{
|
|
unsigned int ret;
|
|
|
|
if (!plug) return 0;
|
|
XTray_getPosition(gdk_display,gtk_plug_get_id(GTK_PLUG(plug)),&ret,NULL);
|
|
|
|
return ret;
|
|
}
|
|
|
|
long gTrayIcon::screenY()
|
|
{
|
|
unsigned int ret;
|
|
|
|
if (!plug) return 0;
|
|
XTray_getPosition(gdk_display,gtk_plug_get_id(GTK_PLUG(plug)),NULL,&ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
long gTrayIcon::width()
|
|
{
|
|
unsigned int ret;
|
|
|
|
XTray_getSize(gdk_display,gtk_plug_get_id(GTK_PLUG(plug)),&ret,NULL);
|
|
return ret;
|
|
}
|
|
|
|
long gTrayIcon::height()
|
|
{
|
|
unsigned int ret;
|
|
|
|
XTray_getSize(gdk_display,gtk_plug_get_id(GTK_PLUG(plug)),NULL,&ret);
|
|
return ret;
|
|
}
|
|
|
|
void gTrayIcon::exit()
|
|
{
|
|
gTrayIcon *icon;
|
|
|
|
while((icon = get(0)))
|
|
delete icon;
|
|
}
|
|
|
|
#else
|
|
|
|
|
|
|
|
gTrayIcon::gTrayIcon()
|
|
{
|
|
stub("no-X11/gTrayIcon class");
|
|
}
|
|
|
|
gTrayIcon::~gTrayIcon()
|
|
{
|
|
stub("no-X11/gTrayIcon class");
|
|
}
|
|
|
|
|
|
void gTrayIcon::refresh()
|
|
{
|
|
stub("no-X11/gTrayIcon class");
|
|
}
|
|
|
|
void gTrayIcon::setPicture(gPicture *picture)
|
|
{
|
|
stub("no-X11/gTrayIcon class");
|
|
}
|
|
|
|
char* gTrayIcon::toolTip()
|
|
{
|
|
stub("no-X11/gTrayIcon class");
|
|
|
|
}
|
|
|
|
void gTrayIcon::setToolTip(char* vl)
|
|
{
|
|
stub("no-X11/gTrayIcon class");
|
|
|
|
}
|
|
|
|
bool gTrayIcon::isVisible()
|
|
{
|
|
stub("no-X11/gTrayIcon class");
|
|
}
|
|
|
|
void gTrayIcon::setVisible(bool vl)
|
|
{
|
|
stub("no-X11/gTrayIcon class");
|
|
}
|
|
|
|
long gTrayIcon::screenX()
|
|
{
|
|
stub("no-X11/gTrayIcon class");
|
|
}
|
|
|
|
long gTrayIcon::screenY()
|
|
{
|
|
stub("no-X11/gTrayIcon class");
|
|
}
|
|
|
|
long gTrayIcon::width()
|
|
{
|
|
stub("no-X11/gTrayIcon class");
|
|
}
|
|
|
|
long gTrayIcon::height()
|
|
{
|
|
stub("no-X11/gTrayIcon class");
|
|
}
|
|
|
|
void gTrayIcon::exit()
|
|
{
|
|
stub("no-X11/gTrayIcon class");
|
|
}
|
|
|
|
#endif
|