- add an animated tray icon for the GTK2 version (patch by Jochen Baier)

git-svn-id: http://svn.code.sf.net/p/xqf/code/trunk@622 d2ac09be-c843-0410-8b1f-f8a84130e0ec
This commit is contained in:
Ludwig Nussel 2004-06-19 18:24:07 +00:00 committed by l-n
parent 61322fbd01
commit 28d7d5fb82
22 changed files with 1098 additions and 15 deletions

View File

@ -3,6 +3,7 @@ Jun 19, 2004: Ludwig Nussel <l-n@users.sourceforge.net>
all servers. Today's qstat cvs version required.
- add gametypes for World of Padman Q3A mod
- add support for Jedi Academy (wine game, patch by Steffen Pankratz)
- add an animated tray icon for the GTK2 version (patch by Jochen Baier)
Jun 06, 2004: Ludwig Nussel <l-n@users.sourceforge.net>
- update Danish translation (Morten Brix Pedersen)

View File

@ -48,7 +48,7 @@ if test "x$USE_GTK2" != "xno"; then
OLD_GTK_SUPPORT="-DGTK_ENABLE_BROKEN=1"
AC_SUBST(OLD_GTK_SUPPORT)
pkg_modules="gtk+-2.0 >= 2.0.0"
pkg_modules="gtk+-2.0 >= 2.0.0 gdk-pixbuf-xlib-2.0"
PKG_CHECK_MODULES(PACKAGE, [$pkg_modules])
AC_SUBST(PACKAGE_CFLAGS)
AC_SUBST(PACKAGE_LIBS)
@ -195,6 +195,7 @@ AC_CONFIG_FILES([
docs/Makefile
pixmaps/Makefile
pixmaps/flags/Makefile
pixmaps/trayicon/Makefile
xqf.spec
po/Makefile.in
])

View File

@ -1,4 +1,4 @@
SUBDIRS = flags
SUBDIRS = flags trayicon
EXTRA_DIST = $(splash_DATA) $(icon_DATA)

View File

@ -0,0 +1,4 @@
EXTRA_DIST = $(tray_DATA)
traydir = $(pkgdatadir)/default/trayicon
tray_DATA = $(wildcard *.png) $(wildcard *.ani)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,5 @@
around_frame_1.png 8
around_frame_2.png 8
around_frame_3.png 8
around_frame_4.png 8

Binary file not shown.

View File

@ -0,0 +1,16 @@
red_frame_4_4.png 50
frame_basic.png 50
red_frame_4_4.png 50
frame_basic.png 50
red_frame_4_4.png 50
frame_basic.png 50
red_frame_4_4.png 50
frame_basic.png 50
red_frame_4_4.png 50
frame_basic.png 50
red_frame_4_4.png 6000
red_frame_3_4.png 6000
red_frame_2_4.png 6000
red_frame_1_4.png 6000
frame_basic.png 1

Binary file not shown.

View File

@ -57,6 +57,7 @@ srv-list.c \
srv-prop.c \
stat.c \
statistics.c \
trayicon.c \
utils.c \
xqf.c \
xqf-ui.c \
@ -98,6 +99,7 @@ srv-list.h \
srv-prop.h \
stat.h \
statistics.h \
trayicon.h \
utils.h \
xqf-ui.h \
xqf.h \

View File

@ -101,6 +101,7 @@ int default_save_plrinfo;
int default_auto_favorites;
int default_show_splash;
int default_auto_maps;
int default_show_tray_icon;
int default_toolbar_style;
int default_toolbar_tips;
int default_refresh_sorts;
@ -170,6 +171,7 @@ static GtkWidget *save_plrinfo_check_button;
static GtkWidget *auto_favorites_check_button;
static GtkWidget *show_splash_button;
static GtkWidget *auto_maps_check_button;
static GtkWidget *tray_icon_check_button;
static GtkWidget *show_hostnames_check_button;
static GtkWidget *show_defport_check_button;
static GtkWidget *toolbar_style_radio_buttons[3];
@ -941,6 +943,12 @@ static void get_new_defaults (void) {
if (i != default_auto_maps)
config_set_bool ("search maps", default_auto_maps = i);
#ifdef USE_GTK2
i = GTK_TOGGLE_BUTTON (tray_icon_check_button)->active;
if (i != default_show_tray_icon)
config_set_bool ("showtray", default_show_tray_icon = i);
#endif
config_pop_prefix ();
/* QStat */
@ -3922,6 +3930,23 @@ static GtkWidget *general_options_page (void) {
gtk_widget_show (hbox);
#ifdef USE_GTK2
/*Tray icon*/
hbox = gtk_hbox_new (FALSE, 4);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
tray_icon_check_button = gtk_check_button_new_with_label (_("Minimize to system tray"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tray_icon_check_button), default_show_tray_icon);
gtk_tooltips_set_tip (tooltips, tray_icon_check_button,
_("Enable xqf tray icon. You need to restart xqf to take effect !"), NULL);
gtk_box_pack_start (GTK_BOX (hbox), tray_icon_check_button, FALSE, FALSE, 0);
gtk_widget_show (tray_icon_check_button);
gtk_widget_show (hbox);
#endif
gtk_widget_show (vbox);
gtk_widget_show (frame);
@ -5030,6 +5055,7 @@ int prefs_load (void) {
default_auto_favorites = config_get_bool ("refresh favorites=false");
default_show_splash = config_get_bool ("splash screen=true");
default_auto_maps = config_get_bool ("search maps=false");
default_show_tray_icon = config_get_bool ("showtray=false");
config_pop_prefix ();
@ -5319,4 +5345,3 @@ static void file_dialog(const char *title, GtkSignalFunc ok_callback, enum serve
gtk_widget_show(file_selector);
}

View File

@ -88,6 +88,7 @@ extern int default_save_srvinfo;
extern int default_save_plrinfo;
extern int default_auto_favorites;
extern int default_show_splash;
extern int default_show_tray_icon;
extern int default_always_resolve;
extern int default_toolbar_style;
extern int default_toolbar_tips;
@ -126,5 +127,3 @@ extern void free_user_info (void);
extern int prefs_load (void);
#endif /* __PREF_H__ */

View File

@ -495,6 +495,19 @@ GSList *server_clist_selected_servers (void) {
return list;
}
GSList *server_clist_get_n_servers (int amount) {
GSList *list = NULL;
struct server *server;
int row;
for (row = 0; (row < server_clist->rows && row < amount) ; row++) {
server = (struct server *) gtk_clist_get_row_data (server_clist, row);
list = server_list_prepend (list, server);
}
return g_slist_reverse (list);
}
/*
server_clist_all_servers -- Return all servers that are in the server
clist widget. It returns a new list. Note that the prepend function

View File

@ -38,6 +38,7 @@ extern int server_clist_refresh_server (struct server *s);
extern void server_clist_select_one (int row);
extern GSList *server_clist_selected_servers (void);
extern GSList *server_clist_all_servers (void);
extern GSList *server_clist_get_n_servers (int amount);
extern void server_clist_selection_visible (void);

926
xqf/src/trayicon.c Normal file
View File

@ -0,0 +1,926 @@
/* XQF - Quake server browser and launcher
*
* 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 of the License, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
.*
.*
.* trayicon.c:
.*
* Copyright (C) Jochen Baier <email@jochen-baier.de>
.*
.* based on:
.*
.* eggtrayicon.c api
.*
* Copyright (C) Anders Carlsson <andersca@gnu.org>
.*
.* magic transparent KDE icon:
.*
.* Copyright (C) Jochen Baier <email@jochen-baier.de>
.*
*/
#include <gtk/gtk.h>
#include "gnuconfig.h"
#ifndef USE_GTK2
/*dummy functions for gtk 1.x, this avoid a lot ifdef´s*/
void tray_init (GtkWidget * window) {/**/};
void tray_delete_event_hook (void) {/**/};
void tray_icon_set_tooltip (gchar *tip) {/**/};
void tray_icon_stop_animation (void) {/**/};
void tray_icon_start_animation (void) {/**/};
extern void tray_done (void) {/**/};
extern gboolean tray_icon_work(void) {return FALSE;};
#else
#include <stdio.h>
#include <gtk/gtkplug.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <string.h>
#include <X11/Xatom.h>
#include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
#include <stdlib.h>
#include "i18n.h"
#include "pref.h"
#include "xqf.h"
#include "debug.h"
#include "loadpixmap.h"
#include "srv-list.h"
#include "source.h"
#define SYSTEM_TRAY_REQUEST_DOCK 0
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
#define SYSTEM_TRAY_CANCEL_MESSAGE 2
#define EGG_TYPE_TRAY_ICON (egg_tray_icon_get_type ())
#define EGG_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TRAY_ICON, EggTrayIcon))
#define EGG_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_TRAY_ICON, EggTrayIconClass))
#define EGG_IS_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TRAY_ICON))
#define EGG_IS_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TRAY_ICON))
#define EGG_TRAY_ICON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TRAY_ICON, EggTrayIconClass))
#define EVENT_REFRESH 2
#define EVENT_UPDATE 3
#define N_SERVER 100
#define ANI_TIME 10
static const char kde_window_manger[]="KWin";
typedef struct _EggTrayIcon EggTrayIcon;
typedef struct _EggTrayIconClass EggTrayIconClass;
struct _EggTrayIcon
{
GtkPlug parent_instance;
guint stamp;
Atom selection_atom;
Atom manager_atom;
Atom system_tray_opcode_atom;
Window manager_window;
GtkWidget *box;
GtkWidget *background;
GtkWidget *image;
GdkPixbuf *default_pix;
gboolean ready;
};
struct _EggTrayIconClass
{
GtkPlugClass parent_class;
};
GType egg_tray_icon_get_type (void);
static GtkPlugClass *parent_class = NULL;
EggTrayIcon *egg_tray_icon_new (const gchar *name, GdkPixbuf *pix);
guint egg_tray_icon_send_message (EggTrayIcon *icon, gint timeout, const char *message, gint len);
void egg_tray_icon_cancel_message (EggTrayIcon *icon, guint id);
static void egg_tray_icon_init (EggTrayIcon *icon);
static void egg_tray_icon_class_init (EggTrayIconClass *klass);
static void egg_tray_icon_unrealize (GtkWidget *widget);
static void egg_tray_icon_update_manager_window (EggTrayIcon *icon);
GdkPixbuf *kde_dock_background(EggTrayIcon *icon);
GtkTooltips *tray_icon_tips = NULL;
EggTrayIcon *tray_icon = NULL;
GdkPixbuf *frame_basic;
GtkWidget *menu = NULL;
GtkWidget *refresh_item;
GtkWidget *update_item;
GtkWidget *stop_item;
GtkWidget *show_item;
GtkWidget *hide_item;
GtkWidget *exit_item;
typedef struct _frame frame;
struct _frame
{
GdkPixbuf *pix;
gint delay;
};
typedef struct _animation animation;
struct _animation
{
GArray *array;
frame current_frame;
gint frame_counter;
gint time_counter;
gboolean loop;
};
static animation *busy_ani;
static animation *ready_ani;
static GtkWidget *window; /* copy of the main window */
static gint x_pos = 50;
static gint y_pos = 50;
static gboolean animation_running = FALSE;
static gboolean refresh_update=FALSE;
static gint animation_timer=0;
static gint animation_callback (gpointer nothing);
gboolean tray_icon_work(void)
{
if (!tray_icon)
return FALSE;
return tray_icon->ready;
}
/*user close main window-> hide it*/
void tray_delete_event_hook(void)
{
gtk_window_get_position(GTK_WINDOW(window), &x_pos, &y_pos);
gtk_widget_hide(window);
}
void show_main_window_call(GtkWidget * button, void *data)
{
static gboolean first_call = TRUE;
if (first_call) {
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_widget_show(window);
first_call = FALSE;
} else {
gtk_window_move(GTK_WINDOW(window), x_pos, y_pos);
gtk_widget_show(window);
}
}
void hide_main_window_call(GtkWidget * button, void *data)
{
gtk_window_get_position(GTK_WINDOW(window), &x_pos, &y_pos);
gtk_widget_hide(window);
}
void exit_call(GtkWidget * button, void *data)
{
gtk_widget_destroy(GTK_WIDGET(tray_icon));
gtk_main_quit();
}
void set_menu_sens(void)
{
gtk_widget_set_sensitive (show_item, !GTK_WIDGET_VISIBLE(window));
gtk_widget_set_sensitive (hide_item, GTK_WIDGET_VISIBLE(window));
gtk_widget_set_sensitive (stop_item, refresh_update);
}
static void
tray_icon_pressed(GtkWidget * button, GdkEventButton * event, EggTrayIcon * icon)
{
/*right click */
if (event->button == 1) {
if (GTK_WIDGET_VISIBLE(window)) {
gtk_window_get_position(GTK_WINDOW(window), &x_pos, &y_pos);
gtk_widget_hide(window);
} else {
show_main_window_call(NULL, NULL);
}
}
/*left click */
if (event->button == 3) {
set_menu_sens ();
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0,
gtk_get_current_event_time());
}
}
void tray_icon_set_tooltip(gchar * tip)
{
if (!tray_icon || !tray_icon->ready )
return;
if(!tray_icon_tips)
tray_icon_tips = gtk_tooltips_new();
gtk_tooltips_set_tip(GTK_TOOLTIPS(tray_icon_tips),
GTK_WIDGET(tray_icon), tip, tip);
}
static gint animation_callback(gpointer _ani)
{
animation *ani= _ani;
if (ani->frame_counter==0 && ani->time_counter==0) {
ani->current_frame=g_array_index (ani->array, frame, ani->frame_counter);
gtk_image_set_from_pixbuf(GTK_IMAGE(tray_icon->image), ani->current_frame.pix);
}
if (ani->time_counter < ani->current_frame.delay) {
ani->time_counter++;
return TRUE;
}
if (ani->frame_counter == ani->array->len-1) {
ani->frame_counter= 0;
ani->time_counter =0;
if (ani->loop) {
return TRUE;
}
else {
animation_running=FALSE;
return FALSE;
}
}
ani->frame_counter++;
ani->current_frame=g_array_index (ani->array, frame, ani->frame_counter);
gtk_image_set_from_pixbuf(GTK_IMAGE(tray_icon->image), ani->current_frame.pix);
ani->time_counter=0;
return TRUE;
}
void tray_icon_start_animation(void)
{
if (!tray_icon_work())
return;
refresh_update=TRUE;
if (animation_running) {
if (animation_timer) g_source_remove(animation_timer);
}
busy_ani->frame_counter = 0;
busy_ani->time_counter = 0;
animation_timer = g_timeout_add(ANI_TIME, (GtkFunction) animation_callback, busy_ani);
animation_running = TRUE;
}
void tray_icon_stop_animation(void)
{
if (!tray_icon_work())
return;
refresh_update=FALSE;
set_menu_sens ();
if (animation_running) {
if (animation_timer)
g_source_remove(animation_timer);
ready_ani->frame_counter = 0;
ready_ani->time_counter = 0;
animation_timer = g_timeout_add(ANI_TIME, (GtkFunction) animation_callback, ready_ani);
}
}
static animation *tray_icon_load_animation (gchar *name, gboolean loop)
{
gint i=0;
gint line_number=0;
gboolean eof=FALSE;
gchar *ani_file;
gchar *content;
gchar *begin;
gchar *line;
gchar *png_start;
gchar *delay_string=NULL;
gchar *png_filename;
animation *ani;
frame tmp_frame;
if(!name) return NULL;
{
char* tmp = g_strconcat("trayicon", G_DIR_SEPARATOR_S, name, NULL);
ani_file = find_pixmap_directory(tmp);
g_free(tmp);
}
if(!ani_file || !g_file_get_contents (ani_file, &content, NULL, NULL))
{
xqf_warning("Could not load animation file '%s'", name);
g_free(ani_file);
return NULL;
}
ani=g_new(animation, 1);
ani->array = g_array_new (FALSE, FALSE, sizeof (frame));
ani->frame_counter = 0;
ani->time_counter = 0;
ani->loop = loop;
begin=content;
while (!eof) {
if ((content[i] == '\n') || (content[i] == '\0') ) {
if ( content[i-1] != '\n') {
line_number++;
do {
line= g_strndup (begin, &content[i]-begin);
if (strlen (line) <= 1) {
xqf_warning (_("Error in file: %s line: %d\n"), ani_file, line_number);
break;
}
png_start =g_strrstr (line, ".png");
if (!png_start) {
xqf_warning (_("Error in file: %s line: %d\n"), ani_file, line_number);
break;
}
delay_string=g_strdup (png_start+4);
g_strstrip(delay_string);
if (strlen (delay_string) < 1) {
xqf_warning (_("Error in file: %s line: %d\n"), ani_file, line_number);
break;
}
tmp_frame.delay= strtol(delay_string, NULL, 10);
if (tmp_frame.delay== 0) {
xqf_warning (_("Error in file: %s line: %d\n"), ani_file, line_number);
break;
}
if (tmp_frame.delay != 1)
tmp_frame.delay=tmp_frame.delay / 2;
*(png_start+4)=0; /*cut anything behind ".png" */
png_filename= g_strconcat(PACKAGE_DATA_DIR, G_DIR_SEPARATOR_S,"default",
G_DIR_SEPARATOR_S, "trayicon", G_DIR_SEPARATOR_S, line, NULL);
tmp_frame.pix = load_pixmap_as_pixbuf (png_filename);
if (tmp_frame.pix == NULL) {
xqf_warning (_("Error in file: %s line: %d\n"), ani_file, line_number);
break;
}
if (png_filename)
g_free (png_filename);
g_array_append_val (ani->array, tmp_frame);
} while (0);
if (line)
g_free(line);
if (delay_string)
g_free(delay_string);
}
if (content[i] == '\0' || (content[i] == '\n' && content[i+1] == '\0' ))
eof=TRUE;
else
begin=&content[i]+1;
}
i++;
}
if (content)
g_free(content);
if (ani_file)
g_free(ani_file);
return ani;
}
void tray_create_menu (void)
{
GtkWidget *separator1 = NULL;
menu = gtk_menu_new();
refresh_item = gtk_menu_item_new_with_label(_("Refresh"));
g_signal_connect(G_OBJECT(refresh_item), "activate",
G_CALLBACK(refresh_n_server), GINT_TO_POINTER(N_SERVER));
gtk_menu_shell_append(GTK_MENU_SHELL(menu), refresh_item);
update_item= gtk_menu_item_new_with_label(_("Update"));
g_signal_connect(G_OBJECT(update_item), "activate",
G_CALLBACK(update_source_callback), NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), update_item);
stop_item = gtk_menu_item_new_with_label(_("Stop"));
g_signal_connect(G_OBJECT(stop_item), "activate",
G_CALLBACK(stop_callback), NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), stop_item);
separator1 = gtk_menu_item_new();
gtk_widget_show(separator1);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator1);
gtk_widget_set_sensitive(separator1, FALSE);
show_item = gtk_menu_item_new_with_label(_("Show"));
g_signal_connect(G_OBJECT(show_item), "activate",
G_CALLBACK(show_main_window_call), NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), show_item);
hide_item= gtk_menu_item_new_with_label(_("Hide"));
g_signal_connect(G_OBJECT(hide_item), "activate",
G_CALLBACK(hide_main_window_call), NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), hide_item);
exit_item = gtk_menu_item_new_with_label(_("Exit"));
g_signal_connect(G_OBJECT(exit_item), "activate",
G_CALLBACK(exit_call), NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), exit_item);
gtk_widget_show_all(menu);
}
void tray_init(GtkWidget * main_window)
{
gdk_pixbuf_xlib_init (GDK_DISPLAY(), DefaultScreen (GDK_DISPLAY()));
/* local copy */
window = main_window;
gtk_window_get_position(GTK_WINDOW(window), &x_pos, &y_pos);
tray_create_menu();
busy_ani = tray_icon_load_animation ("busy.ani", TRUE);
ready_ani = tray_icon_load_animation("ready.ani", FALSE);
frame_basic = load_pixmap_as_pixbuf("trayicon/frame_basic.png");
if(frame_basic)
tray_icon = egg_tray_icon_new ("xqf", frame_basic);
if (tray_icon && tray_icon->ready)
{
g_signal_connect(tray_icon, "button_press_event",
G_CALLBACK(tray_icon_pressed),tray_icon);
gtk_widget_hide(window);
}
else
gtk_widget_show(window);
}
void tray_done (void)
{
if (busy_ani)
{
if (busy_ani->array)
g_array_free (busy_ani->array, TRUE);
if (ready_ani->array)
g_array_free (ready_ani->array, TRUE);
g_free(busy_ani);
}
g_free(ready_ani);
//FIXME
//if (tray_icon)
//gtk_widget_destroy(GTK_WIDGET(tray_icon));
}
/*eggtrayicon stuff*/
GType
egg_tray_icon_get_type (void)
{
static GType our_type = 0;
our_type = g_type_from_name ("EggTrayIcon");
if (our_type == 0) {
static const GTypeInfo our_info =
{
sizeof (EggTrayIconClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) egg_tray_icon_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (EggTrayIcon),
0, /* n_preallocs */
(GInstanceInitFunc) egg_tray_icon_init
};
our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0);
}
else if (parent_class == NULL) {
/* we're reheating the old class from a previous instance - engage ugly hack =( */
egg_tray_icon_class_init ((EggTrayIconClass *)g_type_class_peek (our_type));
}
return our_type;
}
static void
egg_tray_icon_init (EggTrayIcon *icon)
{
icon->stamp = 1;
gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
}
static void
egg_tray_icon_class_init (EggTrayIconClass *klass)
{
GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
parent_class = g_type_class_peek_parent (klass);
widget_class->unrealize = egg_tray_icon_unrealize;
}
static GdkFilterReturn
egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_data)
{
EggTrayIcon *icon = user_data;
XEvent *xev = (XEvent *)xevent;
if (xev->xany.type == ClientMessage &&
xev->xclient.message_type == icon->manager_atom &&
xev->xclient.data.l[1] == icon->selection_atom)
{
egg_tray_icon_update_manager_window (icon);
}
else if (xev->xany.window == icon->manager_window)
{
if (xev->xany.type == DestroyNotify)
{
egg_tray_icon_update_manager_window (icon);
}
}
return GDK_FILTER_CONTINUE;
}
static void
egg_tray_icon_unrealize (GtkWidget *widget)
{
EggTrayIcon *icon = EGG_TRAY_ICON (widget);
GdkWindow *root_window=NULL;
if (icon->manager_window != None)
{
gdk_window_remove_filter (root_window, egg_tray_icon_manager_filter, icon);
if (GTK_WIDGET_CLASS (parent_class)->unrealize)
(* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
}
}
static void
egg_tray_icon_send_manager_message (EggTrayIcon *icon,
long message,
Window window,
long data1,
long data2,
long data3)
{
XClientMessageEvent ev;
Display *display;
ev.type = ClientMessage;
ev.window = window;
ev.message_type = icon->system_tray_opcode_atom;
ev.format = 32;
ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
ev.data.l[1] = message;
ev.data.l[2] = data1;
ev.data.l[3] = data2;
ev.data.l[4] = data3;
display = gdk_display;
gdk_error_trap_push ();
XSendEvent (display,
icon->manager_window, False, NoEventMask, (XEvent *)&ev);
XSync (display, False);
gdk_error_trap_pop ();
icon->ready=TRUE;
}
static void
egg_tray_icon_send_dock_request (EggTrayIcon *icon)
{
egg_tray_icon_send_manager_message (icon,
SYSTEM_TRAY_REQUEST_DOCK,
icon->manager_window,
gtk_plug_get_id (GTK_PLUG (icon)),
0, 0);
}
/*from gdk-pixbuf-xlib-drawabel.c*/
static gboolean
xlib_window_is_viewable (Window w)
{
XWindowAttributes wa;
while (w != 0) {
Window parent, root, *children;
int nchildren;
XGetWindowAttributes (GDK_DISPLAY(), w, &wa);
if (wa.map_state != IsViewable) {
return FALSE;
}
if (!XQueryTree (GDK_DISPLAY(), w, &root,
&parent, &children, &nchildren))
return FALSE;
if (nchildren > 0)
XFree (children);
if ((parent == root) || (w == root))
return TRUE;
w = parent;
}
return FALSE;
}
gboolean kde_dock (EggTrayIcon *icon)
{
Window win;
Window data;
Atom kde_tray_atom = XInternAtom (GDK_DISPLAY(), "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False);
Atom kde_dock_atom = XInternAtom (GDK_DISPLAY(), "KWM_DOCKWINDOW", False); //KDE 2 support not tested
win = GDK_WINDOW_XID (GTK_WIDGET (icon)->window);
XSetWindowBackgroundPixmap(GDK_DISPLAY(), win, ParentRelative);
data = 1;
XChangeProperty (GDK_DISPLAY(), win, kde_dock_atom, XA_WINDOW, 32,
PropModeReplace, (unsigned char *)&data, 1);
data = GDK_WINDOW_XID (window->window);
XChangeProperty (GDK_DISPLAY(), win, kde_tray_atom, XA_WINDOW, 32,
PropModeReplace, (unsigned char *)&data, 1);
XMapWindow (GDK_DISPLAY(), win);
XFlush (GDK_DISPLAY());
return (xlib_window_is_viewable(win));
}
GdkPixbuf *kde_dock_background(EggTrayIcon *icon)
{
Window win;
XWindowAttributes wa;
GdkPixbuf *background;
win = GDK_WINDOW_XID (GTK_WIDGET (icon)->window);
XGetWindowAttributes (GDK_DISPLAY(),win, &wa);
background=gdk_pixbuf_xlib_get_from_drawable (NULL, win, 0, NULL,
0, 0, 0, 0, wa.width, wa.height);
return background;
}
static void
egg_tray_icon_update_manager_window (EggTrayIcon *icon)
{
static GdkPixbuf *background_pixbuf;
const gchar *window_manager=NULL;
if (icon->manager_window != None) {
GdkWindow *gdkwin;
gdkwin = gdk_window_lookup (icon->manager_window);
gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
}
XGrabServer (GDK_DISPLAY());
icon->manager_window = XGetSelectionOwner (GDK_DISPLAY(),
icon->selection_atom);
if (icon->manager_window != None)
XSelectInput (GDK_DISPLAY(), icon->manager_window, StructureNotifyMask);
XUngrabServer (GDK_DISPLAY());
XFlush (GDK_DISPLAY());
if (icon->manager_window == None)
return;
window_manager=gdk_x11_screen_get_window_manager_name (gdk_screen_get_default());
if ( !g_ascii_strcasecmp(window_manager, kde_window_manger) && kde_dock (icon)) {
if ((background_pixbuf=kde_dock_background(icon)) !=NULL) {
icon->box= gtk_fixed_new ();
gtk_fixed_set_has_window(GTK_FIXED (icon->box),TRUE);
gtk_container_add(GTK_CONTAINER(icon), icon->box);
icon->image=gtk_image_new ();
gtk_image_set_from_pixbuf(GTK_IMAGE(icon->image), icon->default_pix);
icon->background =gtk_image_new ();
gtk_image_set_from_pixbuf(GTK_IMAGE(icon->background), background_pixbuf);
gtk_fixed_put (GTK_FIXED (icon->box), GTK_WIDGET(icon->background), 0, 0);
gtk_fixed_put (GTK_FIXED (icon->box), GTK_WIDGET(icon->image), 0, 0);
gtk_widget_show (icon->background);
gtk_widget_show (icon->image);
gtk_widget_show(icon->box);
icon->ready=TRUE;
}
} else {
icon->box=gtk_event_box_new ();
gtk_container_add(GTK_CONTAINER(icon), icon->box);
icon->image=gtk_image_new ();
gtk_image_set_from_pixbuf(GTK_IMAGE(icon->image),icon->default_pix);
gtk_container_add(GTK_CONTAINER(icon->box), icon->image);
gtk_widget_show (icon->image);
gtk_widget_show(icon->box);
GdkWindow *gdkwin;
gdkwin = gdk_window_lookup (icon->manager_window);
gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon);
/* Send a request that we'd like to dock */
egg_tray_icon_send_dock_request (icon);
}
}
EggTrayIcon *
egg_tray_icon_new (const char *name, GdkPixbuf *pix)
{
EggTrayIcon *icon;
char buffer[256];
GdkWindow *root_window;
Screen *xscreen=DefaultScreenOfDisplay (GDK_DISPLAY());
g_return_val_if_fail (pix!= NULL, NULL);
icon = g_object_new (EGG_TYPE_TRAY_ICON, NULL);
gtk_window_set_title (GTK_WINDOW (icon), name);
gtk_plug_construct (GTK_PLUG (icon), 0);
gtk_widget_realize (GTK_WIDGET (icon));
icon->ready=FALSE;
icon->default_pix=pix;
/* Now see if there's a manager window around */
g_snprintf (buffer, sizeof (buffer), "_NET_SYSTEM_TRAY_S%d",
XScreenNumberOfScreen (xscreen));
icon->selection_atom = XInternAtom (DisplayOfScreen (xscreen),
buffer, False);
icon->manager_atom = XInternAtom (DisplayOfScreen (xscreen),
"MANAGER", False);
icon->system_tray_opcode_atom = XInternAtom (DisplayOfScreen (xscreen),
"_NET_SYSTEM_TRAY_OPCODE", False);
gtk_window_present (GTK_WINDOW (icon));
egg_tray_icon_update_manager_window (icon);
root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ());
/* Add a root window filter so that we get changes on MANAGER */
gdk_window_add_filter (root_window, egg_tray_icon_manager_filter, icon);
return icon;
}
guint
egg_tray_icon_send_message (EggTrayIcon *icon,
gint timeout,
const gchar *message,
gint len)
{
guint stamp;
g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0);
g_return_val_if_fail (timeout >= 0, 0);
g_return_val_if_fail (message != NULL, 0);
if (icon->manager_window == None)
return 0;
if (len < 0)
len = strlen (message);
stamp = icon->stamp++;
/* Get ready to send the message */
egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
(Window)gtk_plug_get_id (GTK_PLUG (icon)),
timeout, len, stamp);
/* Now to send the actual message */
gdk_error_trap_push ();
while (len > 0)
{
XClientMessageEvent ev;
Display *xdisplay;
xdisplay = GDK_DISPLAY();
ev.type = ClientMessage;
ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
ev.format = 8;
ev.message_type = XInternAtom (xdisplay,
"_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
if (len > 20)
{
memcpy (&ev.data, message, 20);
len -= 20;
message += 20;
}
else
{
memcpy (&ev.data, message, len);
len = 0;
}
XSendEvent (GDK_DISPLAY(),
icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev);
XSync (GDK_DISPLAY(), False);
}
gdk_error_trap_pop ();
return stamp;
}
void
egg_tray_icon_cancel_message (EggTrayIcon *icon,
guint id)
{
g_return_if_fail (EGG_IS_TRAY_ICON (icon));
g_return_if_fail (id > 0);
egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
(Window)gtk_plug_get_id (GTK_PLUG (icon)),
id, 0, 0);
}
#endif /*GTK 2*/

48
xqf/src/trayicon.h Normal file
View File

@ -0,0 +1,48 @@
/* XQF - Quake server browser and launcher
*
* 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 of the License, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
.*
.*
.* trayicon.c:
.*
* Copyright (C) Jochen Baier <email@jochen-baier.de>
.*
.* based on:
.*
.* eggtrayicon.c api
.*
* Copyright (C) Anders Carlsson <andersca@gnu.org>
.*
.* magic transparent KDE icon:
.*
.* Copyright (C) Jochen Baier <email@jochen-baier.de>
.*
*/
#ifndef _system_tray_h_
#define _system_tray_h_
#include <gtk/gtk.h>
extern void tray_init (GtkWidget * window) ;
extern void tray_done (void);
extern void tray_delete_event_hook (void);
extern void tray_icon_set_tooltip (gchar *tip);
extern gboolean tray_icon_work(void);
extern void tray_icon_stop_animation (void);
extern void tray_icon_start_animation (void);
#endif /*_system_tray_h_*/

View File

@ -34,6 +34,7 @@
#include "sort.h"
#include "pref.h"
#include "debug.h"
#include "trayicon.h"
static GSList *xqf_windows = NULL;
static GtkWidget *target_window = NULL;
@ -128,13 +129,22 @@ void print_status (GtkWidget *sbar, char *fmt, ...) {
gtk_statusbar_pop (GTK_STATUSBAR (sbar), context_id);
gtk_statusbar_push (GTK_STATUSBAR (sbar), context_id, buf);
if (default_show_tray_icon)
tray_icon_set_tooltip(buf);
}
}
int window_delete_event_callback (GtkWidget *widget, gpointer data) {
target_window = widget;
gtk_widget_destroy ((GtkWidget *) (xqf_windows->data));
if (default_show_tray_icon && tray_icon_work()) {
tray_delete_event_hook();
}
else {
target_window = widget;
gtk_widget_destroy ((GtkWidget *) (xqf_windows->data));
}
return TRUE;
}
@ -844,5 +854,3 @@ GtkWidget *create_server_type_menu (enum server_type active_type,
return option_menu;
}

View File

@ -81,6 +81,7 @@
#include "redial.h"
#include "splash.h"
#include "loadpixmap.h"
#include "trayicon.h"
#ifdef USE_GEOIP
#include "country-filter.h"
@ -961,6 +962,8 @@ static void stat_lists_state_handler (struct stat_job *job,
case STAT_UPDATE_SOURCE:
progress_bar_str = _("Updating lists...");
if (default_show_tray_icon)
tray_icon_start_animation ();
break;
case STAT_RESOLVE_NAMES:
@ -969,6 +972,8 @@ static void stat_lists_state_handler (struct stat_job *job,
case STAT_REFRESH_SERVERS:
progress_bar_str = _("Refreshing: %d/%d");
if (default_show_tray_icon)
tray_icon_start_animation ();
break;
case STAT_RESOLVE_HOSTS:
@ -1001,6 +1006,8 @@ static void stat_lists_close_handler (struct stat_job *job, int killed) {
print_status (main_status_bar, _("Waiting to redial server(s)..."));
else
*/
tray_icon_stop_animation ();
print_status (main_status_bar, _("Done."));
progress_bar_reset (main_progress_bar);
@ -1494,7 +1501,7 @@ static int srvinf_clist_compare_func (GtkCList *clist,
}
static void update_source_callback (GtkWidget *widget, gpointer data) {
void update_source_callback (GtkWidget *widget, gpointer data) {
GSList *masters = NULL;
GSList *servers = NULL;
GSList *uservers = NULL;
@ -1554,8 +1561,24 @@ static void refresh_callback (GtkWidget *widget, gpointer data) {
}
}
void refresh_n_server (GtkWidget * button, gpointer *data)
{
GSList *list;
gint number;
static void stop_callback (GtkWidget *widget, gpointer data) {
if (stat_process)
return;
event_type = EVENT_REFRESH;
number=GPOINTER_TO_INT(data);
list = server_clist_get_n_servers(number);
if (list)
stat_lists(NULL, NULL, list, NULL);
}
void stop_callback (GtkWidget *widget, gpointer data) {
event_type = 0; // To prevent sound from stopped action from playing
@ -3584,8 +3607,6 @@ void create_main_window (void) {
window_set_icon(main_window);
gtk_widget_show (main_window);
gtk_window_add_accel_group (GTK_WINDOW (main_window), accel_group);
gtk_accel_group_unref (accel_group);
@ -3595,7 +3616,6 @@ void create_main_window (void) {
gtk_tooltips_enable(tooltips);
else
gtk_tooltips_disable(tooltips);
}
void play_sound (const char *sound, const int override)
@ -3898,6 +3918,11 @@ int main (int argc, char *argv[]) {
create_main_window ();
if (default_show_tray_icon)
tray_init(main_window);
else
gtk_widget_show (main_window);
source_ctree_select_source (favorites);
filter_menu_activate_current();
@ -3912,10 +3937,15 @@ int main (int argc, char *argv[]) {
debug(1,"startup time %ds", time(NULL)-xqf_start_time);
tray_icon_set_tooltip(_("nothing yet..."));
gtk_main ();
play_sound(sound_xqf_quit, 0);
if (default_show_tray_icon)
tray_done();
unregister_window (main_window);
main_window = NULL;
@ -3998,7 +4028,7 @@ int main (int argc, char *argv[]) {
#endif
games_done();
debug( 6, "EXIT: Done.");

View File

@ -26,6 +26,7 @@
#include <arpa/inet.h> /* struct in_addr */
#include <time.h> /* time_t */
#include <gtk/gtk.h>
#include <glib.h>
#define RC_DIR ".qf"
@ -277,5 +278,8 @@ extern int event_type;
extern int dontlaunch;
extern void refresh_source_list (void);
extern void update_source_callback (GtkWidget *widget, gpointer data);
extern void refresh_n_server(GtkWidget * button, gpointer *data);
extern void stop_callback (GtkWidget *widget, gpointer data);
#endif /* __XQF_H__ */