mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-03-12 16:50:19 +00:00
add xchat r1489
This commit is contained in:
32
src/fe-gtk/Makefile.am
Normal file
32
src/fe-gtk/Makefile.am
Normal file
@@ -0,0 +1,32 @@
|
||||
localedir = $(datadir)/locale
|
||||
|
||||
bin_PROGRAMS = xchat
|
||||
|
||||
INCLUDES = $(GUI_CFLAGS) -DG_DISABLE_CAST_CHECKS -DLOCALEDIR=\"$(localedir)\"
|
||||
|
||||
xchat_LDADD = ../common/libxchatcommon.a $(GUI_LIBS)
|
||||
|
||||
EXTRA_DIST = \
|
||||
about.h ascii.h banlist.h chanlist.h chanview.h chanview-tabs.c \
|
||||
chanview-tree.c custom-list.h editlist.h fe-gtk.h fkeys.h gtkutil.h joind.h \
|
||||
maingui.h menu.h mmx_cmod.S mmx_cmod.h notifygui.h palette.h pixmaps.h \
|
||||
plugin-tray.h plugingui.c plugingui.h rawlog.h search.h sexy-spell-entry.h \
|
||||
textgui.h urlgrab.h userlistgui.h xtext.h
|
||||
|
||||
if USE_MMX
|
||||
mmx_cmod_S = mmx_cmod.S
|
||||
endif
|
||||
|
||||
if DO_PLUGIN
|
||||
plugingui_c = plugingui.c
|
||||
endif
|
||||
|
||||
if USE_LIBSEXY
|
||||
sexy_spell_entry_c = sexy-spell-entry.c
|
||||
endif
|
||||
|
||||
xchat_SOURCES = about.c ascii.c banlist.c chanlist.c chanview.c custom-list.c \
|
||||
dccgui.c editlist.c fe-gtk.c fkeys.c gtkutil.c ignoregui.c joind.c menu.c \
|
||||
maingui.c $(mmx_cmod_S) notifygui.c palette.c pixmaps.c plugin-tray.c $(plugingui_c) \
|
||||
rawlog.c search.c servlistgui.c setup.c $(sexy_spell_entry_c) textgui.c \
|
||||
urlgrab.c userlistgui.c xtext.c
|
||||
161
src/fe-gtk/about.c
Normal file
161
src/fe-gtk/about.c
Normal file
@@ -0,0 +1,161 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkmain.h>
|
||||
#include <gtk/gtkcontainer.h>
|
||||
#include <gtk/gtkdialog.h>
|
||||
#include <gtk/gtkvbox.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkimage.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkwindow.h>
|
||||
|
||||
#ifdef USE_XLIB
|
||||
#include <gdk/gdkx.h>
|
||||
#endif
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/util.h"
|
||||
#include "palette.h"
|
||||
#include "pixmaps.h"
|
||||
#include "gtkutil.h"
|
||||
#include "about.h"
|
||||
|
||||
|
||||
#if 0 /*def USE_GNOME*/
|
||||
|
||||
void
|
||||
menu_about (GtkWidget * wid, gpointer sess)
|
||||
{
|
||||
char buf[512];
|
||||
const gchar *author[] = { "Peter Zelezny <zed@xchat.org>", 0 };
|
||||
|
||||
(snprintf) (buf, sizeof (buf),
|
||||
"An IRC Client for UNIX.\n\n"
|
||||
"This binary was compiled on "__DATE__"\n"
|
||||
"Using GTK %d.%d.%d X %d\n"
|
||||
"Running on %s",
|
||||
gtk_major_version, gtk_minor_version, gtk_micro_version,
|
||||
#ifdef USE_XLIB
|
||||
VendorRelease (GDK_DISPLAY ()), get_cpu_str());
|
||||
#else
|
||||
666, get_cpu_str());
|
||||
#endif
|
||||
|
||||
gtk_widget_show (gnome_about_new ("X-Chat", PACKAGE_VERSION,
|
||||
"(C) 1998-2005 Peter Zelezny", author, buf, 0));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static GtkWidget *about = 0;
|
||||
|
||||
static int
|
||||
about_close (void)
|
||||
{
|
||||
about = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
menu_about (GtkWidget * wid, gpointer sess)
|
||||
{
|
||||
GtkWidget *vbox, *label, *hbox;
|
||||
char buf[512];
|
||||
const char *locale = NULL;
|
||||
extern GtkWindow *parent_window; /* maingui.c */
|
||||
|
||||
if (about)
|
||||
{
|
||||
gtk_window_present (GTK_WINDOW (about));
|
||||
return;
|
||||
}
|
||||
|
||||
about = gtk_dialog_new ();
|
||||
gtk_window_set_position (GTK_WINDOW (about), GTK_WIN_POS_CENTER);
|
||||
gtk_window_set_resizable (GTK_WINDOW (about), FALSE);
|
||||
gtk_window_set_title (GTK_WINDOW (about), _("About "DISPLAY_NAME));
|
||||
if (parent_window)
|
||||
gtk_window_set_transient_for (GTK_WINDOW (about), parent_window);
|
||||
g_signal_connect (G_OBJECT (about), "destroy",
|
||||
G_CALLBACK (about_close), 0);
|
||||
|
||||
vbox = GTK_DIALOG (about)->vbox;
|
||||
|
||||
wid = gtk_image_new_from_pixbuf (pix_xchat);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), wid);
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_label_set_selectable (GTK_LABEL (label), TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), label);
|
||||
g_get_charset (&locale);
|
||||
(snprintf) (buf, sizeof (buf),
|
||||
"<span size=\"x-large\"><b>"DISPLAY_NAME" "PACKAGE_VERSION"</b></span>\n\n"
|
||||
"%s\n\n"
|
||||
#ifdef WIN32
|
||||
/* leave this message to avoid time wasting bug reports! */
|
||||
"This version is unofficial and comes with no support.\n\n"
|
||||
#endif
|
||||
"%s\n"
|
||||
"<b>Charset</b>: %s "
|
||||
#ifdef WIN32
|
||||
"<b>GTK+</b>: %i.%i.%i\n"
|
||||
#else
|
||||
"<b>Renderer</b>: %s\n"
|
||||
#endif
|
||||
"<b>Compiled</b>: "__DATE__"\n\n"
|
||||
"<small>\302\251 1998-2010 Peter \305\275elezn\303\275 <zed@xchat.org></small>",
|
||||
_("A multiplatform IRC Client"),
|
||||
get_cpu_str(),
|
||||
locale,
|
||||
#ifdef WIN32
|
||||
gtk_major_version,
|
||||
gtk_minor_version,
|
||||
gtk_micro_version
|
||||
#else
|
||||
#ifdef USE_XFT
|
||||
"Xft"
|
||||
#else
|
||||
"Pango"
|
||||
#endif
|
||||
#endif
|
||||
);
|
||||
gtk_label_set_markup (GTK_LABEL (label), buf);
|
||||
gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
|
||||
|
||||
hbox = gtk_hbox_new (0, 2);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), hbox);
|
||||
|
||||
wid = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
|
||||
GTK_WIDGET_SET_FLAGS (GTK_WIDGET (wid), GTK_CAN_DEFAULT);
|
||||
gtk_box_pack_end (GTK_BOX (GTK_DIALOG (about)->action_area), wid, 0, 0, 0);
|
||||
gtk_widget_grab_default (wid);
|
||||
g_signal_connect (G_OBJECT (wid), "clicked",
|
||||
G_CALLBACK (gtkutil_destroy), about);
|
||||
|
||||
gtk_widget_show_all (about);
|
||||
}
|
||||
#endif
|
||||
1
src/fe-gtk/about.h
Normal file
1
src/fe-gtk/about.h
Normal file
@@ -0,0 +1 @@
|
||||
void menu_about (GtkWidget * wid, gpointer sess);
|
||||
177
src/fe-gtk/ascii.c
Normal file
177
src/fe-gtk/ascii.c
Normal file
@@ -0,0 +1,177 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkeditable.h>
|
||||
#include <gtk/gtkframe.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtkbutton.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "gtkutil.h"
|
||||
#include "ascii.h"
|
||||
#include "maingui.h"
|
||||
|
||||
static const unsigned char table[]=
|
||||
{
|
||||
/* Line 1 */ '\n',
|
||||
0xc2,0xa1,0xc2,0xbf,0xc2,0xa2,0xc2,0xa3,0xe2,0x82,0xac,0xc2,0xa5,0xc2,0xa6,0xc2,
|
||||
0xa7,0xc2,0xa8,0xc2,0xa9,0xc2,0xae,0xc2,0xaa,0xc2,0xab,0xc2,0xbb,0xc2,0xac,0xc2,
|
||||
0xad,0xc2,0xaf,0xe2,0x99,0xaa,
|
||||
/* Line 2 */ '\n',
|
||||
0xc2,0xba,0xc2,0xb9,0xc2,0xb2,0xc2,0xb3,0xc2,0xb4,0xc2,0xb5,0xc3,0x9e,0xc3,0xbe,
|
||||
0xc2,0xb6,0xc2,0xb7,0xc2,0xb8,0xc2,0xb0,0xc2,0xbc,0xc2,0xbd,0xc2,0xbe,0xc3,0x97,
|
||||
0xc2,0xb1,0xc3,0xb7,
|
||||
/* Line 3 */ '\n',
|
||||
0xc3,0x80,0xc3,0x81,0xc3,0x82,0xc3,0x83,0xc3,0x84,0xc3,0x85,0xc3,0x86,0xc4,0x82,
|
||||
0xc4,0x84,0x20,0xc3,0x87,0xc4,0x86,0xc4,0x8c,0xc5,0x92,0x20,0xc4,0x8e,0xc4,0x90,
|
||||
0x20,
|
||||
/* Line 4 */ '\n',
|
||||
0xc3,0xa0,0xc3,0xa1,0xc3,0xa2,0xc3,0xa3,0xc3,0xa4,0xc3,0xa5,0xc3,0xa6,0xc4,0x83,
|
||||
0xc4,0x85,0x20,0xc3,0xa7,0xc4,0x87,0xc4,0x8d,0xc5,0x93,0x20,0xc4,0x8f,0xc4,0x91,
|
||||
0x20,
|
||||
/* Line 5 */ '\n',
|
||||
0xc3,0x88,0xc3,0x89,0xc3,0x8a,0xc3,0x8b,0xc4,0x98,0xc4,0x9a,0x20,0xc4,0x9e,0x20,
|
||||
0xc3,0x8c,0xc3,0x8d,0xc3,0x8e,0xc3,0x8f,0xc4,0xb0,0x20,0xc4,0xb9,0xc4,0xbd,0xc5,
|
||||
0x81,
|
||||
/* Line 6 */ '\n',
|
||||
0xc3,0xa8,0xc3,0xa9,0xc3,0xaa,0xc3,0xab,0xc4,0x99,0xc4,0x9b,0x20,0xc4,0x9f,0x20,
|
||||
0xc3,0xac,0xc3,0xad,0xc3,0xae,0xc3,0xaf,0xc4,0xb1,0x20,0xc4,0xba,0xc4,0xbe,0xc5,
|
||||
0x82,
|
||||
/* Line 7 */ '\n',
|
||||
0xc3,0x91,0xc5,0x83,0xc5,0x87,0x20,0xc3,0x92,0xc3,0x93,0xc3,0x94,0xc3,0x95,0xc3,
|
||||
0x96,0xc3,0x98,0xc5,0x90,0x20,0xc5,0x94,0xc5,0x98,0x20,0xc5,0x9a,0xc5,0x9e,0xc5,
|
||||
0xa0,
|
||||
/* Line 8 */ '\n',
|
||||
0xc3,0xb1,0xc5,0x84,0xc5,0x88,0x20,0xc3,0xb2,0xc3,0xb3,0xc3,0xb4,0xc3,0xb5,0xc3,
|
||||
0xb6,0xc3,0xb8,0xc5,0x91,0x20,0xc5,0x95,0xc5,0x99,0x20,0xc5,0x9b,0xc5,0x9f,0xc5,
|
||||
0xa1,
|
||||
/* Line 9 */ '\n',
|
||||
0x20,0xc5,0xa2,0xc5,0xa4,0x20,0xc3,0x99,0xc3,0x9a,0xc3,0x9b,0xc5,0xb2,0xc3,0x9c,
|
||||
0xc5,0xae,0xc5,0xb0,0x20,0xc3,0x9d,0xc3,0x9f,0x20,0xc5,0xb9,0xc5,0xbb,0xc5,0xbd,
|
||||
/* Line 10 */ '\n',
|
||||
0x20,0xc5,0xa3,0xc5,0xa5,0x20,0xc3,0xb9,0xc3,0xba,0xc3,0xbb,0xc5,0xb3,0xc3,0xbc,
|
||||
0xc5,0xaf,0xc5,0xb1,0x20,0xc3,0xbd,0xc3,0xbf,0x20,0xc5,0xba,0xc5,0xbc,0xc5,0xbe,
|
||||
/* Line 11 */ '\n',
|
||||
0xd0,0x90,0xd0,0x91,0xd0,0x92,0xd0,0x93,0xd0,0x94,0xd0,0x95,0xd0,0x81,0xd0,0x96,
|
||||
0xd0,0x97,0xd0,0x98,0xd0,0x99,0xd0,0x9a,0xd0,0x9b,0xd0,0x9c,0xd0,0x9d,0xd0,0x9e,
|
||||
0xd0,0x9f,0xd0,0xa0,
|
||||
/* Line 12 */ '\n',
|
||||
0xd0,0xb0,0xd0,0xb1,0xd0,0xb2,0xd0,0xb3,0xd0,0xb4,0xd0,0xb5,0xd1,0x91,0xd0,0xb6,
|
||||
0xd0,0xb7,0xd0,0xb8,0xd0,0xb9,0xd0,0xba,0xd0,0xbb,0xd0,0xbc,0xd0,0xbd,0xd0,0xbe,
|
||||
0xd0,0xbf,0xd1,0x80,
|
||||
/* Line 13 */ '\n',
|
||||
0xd0,0xa1,0xd0,0xa2,0xd0,0xa3,0xd0,0xa4,0xd0,0xa5,0xd0,0xa6,0xd0,0xa7,0xd0,0xa8,
|
||||
0xd0,0xa9,0xd0,0xaa,0xd0,0xab,0xd0,0xac,0xd0,0xad,0xd0,0xae,0xd0,0xaf,
|
||||
/* Line 14 */ '\n',
|
||||
0xd1,0x81,0xd1,0x82,0xd1,0x83,0xd1,0x84,0xd1,0x85,0xd1,0x86,0xd1,0x87,0xd1,0x88,
|
||||
0xd1,0x89,0xd1,0x8a,0xd1,0x8b,0xd1,0x8c,0xd1,0x8d,0xd1,0x8e,0xd1,0x8f,0
|
||||
};
|
||||
|
||||
|
||||
static gboolean
|
||||
ascii_enter (GtkWidget * wid, GdkEventCrossing *event, GtkWidget *label)
|
||||
{
|
||||
char buf[64];
|
||||
const char *text;
|
||||
gunichar u;
|
||||
|
||||
text = gtk_button_get_label (GTK_BUTTON (wid));
|
||||
u = g_utf8_get_char (text);
|
||||
sprintf (buf, "%s U+%04X", text, u);
|
||||
gtk_label_set_text (GTK_LABEL (label), buf);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
ascii_click (GtkWidget * wid, gpointer userdata)
|
||||
{
|
||||
int tmp_pos;
|
||||
const char *text;
|
||||
|
||||
if (current_sess)
|
||||
{
|
||||
text = gtk_button_get_label (GTK_BUTTON (wid));
|
||||
wid = current_sess->gui->input_box;
|
||||
tmp_pos = SPELL_ENTRY_GET_POS (wid);
|
||||
SPELL_ENTRY_INSERT (wid, text, -1, &tmp_pos);
|
||||
SPELL_ENTRY_SET_POS (wid, tmp_pos);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ascii_open (void)
|
||||
{
|
||||
int i, len;
|
||||
const unsigned char *table_pos;
|
||||
char name[8];
|
||||
GtkWidget *frame, *label, *but, *hbox = NULL, *vbox, *win;
|
||||
|
||||
win = mg_create_generic_tab ("charmap", _("Character Chart"), TRUE, TRUE,
|
||||
NULL, NULL, 0, 0, &vbox, NULL);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (win), 5);
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
|
||||
table_pos = table;
|
||||
i = 0;
|
||||
while (table_pos[0] != 0)
|
||||
{
|
||||
if (table_pos[0] == '\n' || i == 0)
|
||||
{
|
||||
table_pos++;
|
||||
hbox = gtk_hbox_new (0, 0);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), hbox);
|
||||
gtk_widget_show (hbox);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
i++;
|
||||
len = g_utf8_skip[table_pos[0]];
|
||||
memcpy (name, table_pos, len);
|
||||
name[len] = 0;
|
||||
|
||||
but = gtk_button_new_with_label (name);
|
||||
gtk_widget_set_size_request (but, 28, -1);
|
||||
g_signal_connect (G_OBJECT (but), "clicked",
|
||||
G_CALLBACK (ascii_click), NULL);
|
||||
g_signal_connect (G_OBJECT (but), "enter_notify_event",
|
||||
G_CALLBACK (ascii_enter), label);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), but, 0, 0, 0);
|
||||
gtk_widget_show (but);
|
||||
|
||||
table_pos += len;
|
||||
}
|
||||
|
||||
frame = gtk_frame_new ("");
|
||||
gtk_container_add (GTK_CONTAINER (hbox), frame);
|
||||
gtk_container_add (GTK_CONTAINER (frame), label);
|
||||
gtk_widget_show (label);
|
||||
gtk_widget_show (frame);
|
||||
|
||||
gtk_widget_show (win);
|
||||
}
|
||||
1
src/fe-gtk/ascii.h
Normal file
1
src/fe-gtk/ascii.h
Normal file
@@ -0,0 +1 @@
|
||||
void ascii_open (void);
|
||||
418
src/fe-gtk/banlist.c
Normal file
418
src/fe-gtk/banlist.c
Normal file
@@ -0,0 +1,418 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkvbox.h>
|
||||
#include <gtk/gtkhbbox.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gtk/gtkmessagedialog.h>
|
||||
#include <gtk/gtktreeview.h>
|
||||
#include <gtk/gtktreeselection.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/fe.h"
|
||||
#include "../common/modes.h"
|
||||
#include "../common/outbound.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "gtkutil.h"
|
||||
#include "maingui.h"
|
||||
#include "banlist.h"
|
||||
|
||||
/* model for the banlist tree */
|
||||
enum
|
||||
{
|
||||
MASK_COLUMN,
|
||||
FROM_COLUMN,
|
||||
DATE_COLUMN,
|
||||
N_COLUMNS
|
||||
};
|
||||
|
||||
static GtkTreeView *
|
||||
get_view (struct session *sess)
|
||||
{
|
||||
return GTK_TREE_VIEW (sess->res->banlist_treeview);
|
||||
}
|
||||
|
||||
static GtkListStore *
|
||||
get_store (struct session *sess)
|
||||
{
|
||||
return GTK_LIST_STORE (gtk_tree_view_get_model (get_view (sess)));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
supports_exempt (server *serv)
|
||||
{
|
||||
char *cm = serv->chanmodes;
|
||||
|
||||
if (serv->have_except)
|
||||
return TRUE;
|
||||
|
||||
if (!cm)
|
||||
return FALSE;
|
||||
|
||||
while (*cm)
|
||||
{
|
||||
if (*cm == ',')
|
||||
break;
|
||||
if (*cm == 'e')
|
||||
return TRUE;
|
||||
cm++;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
fe_add_ban_list (struct session *sess, char *mask, char *who, char *when, int is_exempt)
|
||||
{
|
||||
GtkListStore *store;
|
||||
GtkTreeIter iter;
|
||||
char buf[512];
|
||||
|
||||
store = get_store (sess);
|
||||
gtk_list_store_append (store, &iter);
|
||||
|
||||
if (is_exempt)
|
||||
{
|
||||
snprintf (buf, sizeof (buf), "(EX) %s", mask);
|
||||
gtk_list_store_set (store, &iter, 0, buf, 1, who, 2, when, -1);
|
||||
} else
|
||||
{
|
||||
gtk_list_store_set (store, &iter, 0, mask, 1, who, 2, when, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fe_ban_list_end (struct session *sess, int is_exemption)
|
||||
{
|
||||
gtk_widget_set_sensitive (sess->res->banlist_butRefresh, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* * Performs the actual refresh operations.
|
||||
* */
|
||||
static void
|
||||
banlist_do_refresh (struct session *sess)
|
||||
{
|
||||
char tbuf[256];
|
||||
if (sess->server->connected)
|
||||
{
|
||||
GtkListStore *store;
|
||||
|
||||
gtk_widget_set_sensitive (sess->res->banlist_butRefresh, FALSE);
|
||||
|
||||
snprintf (tbuf, sizeof tbuf, "XChat: Ban List (%s, %s)",
|
||||
sess->channel, sess->server->servername);
|
||||
mg_set_title (sess->res->banlist_window, tbuf);
|
||||
|
||||
store = get_store (sess);
|
||||
gtk_list_store_clear (store);
|
||||
|
||||
handle_command (sess, "ban", FALSE);
|
||||
#ifdef WIN32
|
||||
if (0)
|
||||
#else
|
||||
if (supports_exempt (sess->server))
|
||||
#endif
|
||||
{
|
||||
snprintf (tbuf, sizeof (tbuf), "quote mode %s +e", sess->channel);
|
||||
handle_command (sess, tbuf, FALSE);
|
||||
}
|
||||
|
||||
} else
|
||||
{
|
||||
fe_message (_("Not connected."), FE_MSG_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
banlist_refresh (GtkWidget * wid, struct session *sess)
|
||||
{
|
||||
/* JG NOTE: Didn't see actual use of wid here, so just forwarding
|
||||
* * this to chanlist_do_refresh because I use it without any widget
|
||||
* * param in chanlist_build_gui_list when the user presses enter
|
||||
* * or apply for the first time if the list has not yet been
|
||||
* * received.
|
||||
* */
|
||||
banlist_do_refresh (sess);
|
||||
}
|
||||
|
||||
static int
|
||||
banlist_unban_inner (gpointer none, struct session *sess, int do_exempts)
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
GtkTreeSelection *sel;
|
||||
GtkTreeIter iter;
|
||||
char tbuf[2048];
|
||||
char **masks, *tmp, *space;
|
||||
int num_sel, i;
|
||||
|
||||
/* grab the list of selected items */
|
||||
model = GTK_TREE_MODEL (get_store (sess));
|
||||
sel = gtk_tree_view_get_selection (get_view (sess));
|
||||
num_sel = 0;
|
||||
if (gtk_tree_model_get_iter_first (model, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
if (gtk_tree_selection_iter_is_selected (sel, &iter))
|
||||
num_sel++;
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
|
||||
if (num_sel < 1)
|
||||
return 0;
|
||||
|
||||
/* create an array of all the masks */
|
||||
masks = calloc (1, num_sel * sizeof (char *));
|
||||
|
||||
i = 0;
|
||||
gtk_tree_model_get_iter_first (model, &iter);
|
||||
do
|
||||
{
|
||||
if (gtk_tree_selection_iter_is_selected (sel, &iter))
|
||||
{
|
||||
gtk_tree_model_get (model, &iter, MASK_COLUMN, &masks[i], -1);
|
||||
space = strchr (masks[i], ' ');
|
||||
|
||||
if (do_exempts)
|
||||
{
|
||||
if (space)
|
||||
{
|
||||
/* remove the "(EX) " */
|
||||
tmp = masks[i];
|
||||
masks[i] = g_strdup (space + 1);
|
||||
g_free (tmp);
|
||||
i++;
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (!space)
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
|
||||
/* and send to server */
|
||||
if (do_exempts)
|
||||
send_channel_modes (sess, tbuf, masks, 0, i, '-', 'e', 0);
|
||||
else
|
||||
send_channel_modes (sess, tbuf, masks, 0, i, '-', 'b', 0);
|
||||
|
||||
/* now free everything, and refresh banlist */
|
||||
for (i=0; i < num_sel; i++)
|
||||
g_free (masks[i]);
|
||||
free (masks);
|
||||
|
||||
return num_sel;
|
||||
}
|
||||
|
||||
static void
|
||||
banlist_unban (GtkWidget * wid, struct session *sess)
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
num += banlist_unban_inner (wid, sess, FALSE);
|
||||
num += banlist_unban_inner (wid, sess, TRUE);
|
||||
|
||||
if (num < 1)
|
||||
{
|
||||
fe_message (_("You must select some bans."), FE_MSG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
banlist_do_refresh (sess);
|
||||
}
|
||||
|
||||
static void
|
||||
banlist_clear_cb (GtkDialog *dialog, gint response, gpointer sess)
|
||||
{
|
||||
GtkTreeSelection *sel;
|
||||
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
|
||||
if (response == GTK_RESPONSE_OK)
|
||||
{
|
||||
sel = gtk_tree_view_get_selection (get_view (sess));
|
||||
gtk_tree_selection_select_all (sel);
|
||||
banlist_unban (NULL, sess);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
banlist_clear (GtkWidget * wid, struct session *sess)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
|
||||
dialog = gtk_message_dialog_new (NULL, 0,
|
||||
GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
|
||||
_("Are you sure you want to remove all bans in %s?"), sess->channel);
|
||||
g_signal_connect (G_OBJECT (dialog), "response",
|
||||
G_CALLBACK (banlist_clear_cb), sess);
|
||||
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
|
||||
gtk_widget_show (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
banlist_add_selected_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
|
||||
{
|
||||
GSList **lp = data;
|
||||
GSList *list = NULL;
|
||||
GtkTreeIter *copy;
|
||||
|
||||
if (!lp) return;
|
||||
list = *lp;
|
||||
copy = g_malloc (sizeof (GtkTreeIter));
|
||||
g_return_if_fail (copy != NULL);
|
||||
*copy = *iter;
|
||||
|
||||
list = g_slist_append (list, copy);
|
||||
*(GSList **)data = list;
|
||||
}
|
||||
|
||||
static void
|
||||
banlist_crop (GtkWidget * wid, struct session *sess)
|
||||
{
|
||||
GtkTreeSelection *select;
|
||||
GSList *list = NULL, *node;
|
||||
int num_sel;
|
||||
|
||||
/* remember which bans are selected */
|
||||
select = gtk_tree_view_get_selection (get_view (sess));
|
||||
/* gtk_tree_selected_get_selected_rows() isn't present in gtk 2.0.x */
|
||||
gtk_tree_selection_selected_foreach (select, banlist_add_selected_cb,
|
||||
&list);
|
||||
|
||||
num_sel = g_slist_length (list);
|
||||
/* select all, then unselect those that we remembered */
|
||||
if (num_sel)
|
||||
{
|
||||
gtk_tree_selection_select_all (select);
|
||||
|
||||
for (node = list; node; node = node->next)
|
||||
gtk_tree_selection_unselect_iter (select, node->data);
|
||||
|
||||
g_slist_foreach (list, (GFunc)g_free, NULL);
|
||||
g_slist_free (list);
|
||||
|
||||
banlist_unban (NULL, sess);
|
||||
} else
|
||||
fe_message (_("You must select some bans."), FE_MSG_ERROR);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
banlist_treeview_new (GtkWidget *box)
|
||||
{
|
||||
GtkListStore *store;
|
||||
GtkWidget *view;
|
||||
GtkTreeSelection *select;
|
||||
GtkTreeViewColumn *col;
|
||||
|
||||
store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
|
||||
G_TYPE_STRING);
|
||||
g_return_val_if_fail (store != NULL, NULL);
|
||||
view = gtkutil_treeview_new (box, GTK_TREE_MODEL (store), NULL,
|
||||
MASK_COLUMN, _("Mask"),
|
||||
FROM_COLUMN, _("From"),
|
||||
DATE_COLUMN, _("Date"), -1);
|
||||
|
||||
col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), MASK_COLUMN);
|
||||
gtk_tree_view_column_set_alignment (col, 0.5);
|
||||
gtk_tree_view_column_set_min_width (col, 300);
|
||||
gtk_tree_view_column_set_sort_column_id (col, MASK_COLUMN);
|
||||
|
||||
col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), FROM_COLUMN);
|
||||
gtk_tree_view_column_set_alignment (col, 0.5);
|
||||
gtk_tree_view_column_set_sort_column_id (col, FROM_COLUMN);
|
||||
|
||||
col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), DATE_COLUMN);
|
||||
gtk_tree_view_column_set_alignment (col, 0.5);
|
||||
|
||||
select = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
|
||||
gtk_tree_selection_set_mode (select, GTK_SELECTION_MULTIPLE);
|
||||
|
||||
gtk_widget_show (view);
|
||||
return view;
|
||||
}
|
||||
|
||||
static void
|
||||
banlist_closegui (GtkWidget *wid, session *sess)
|
||||
{
|
||||
if (is_session (sess))
|
||||
sess->res->banlist_window = 0;
|
||||
}
|
||||
|
||||
void
|
||||
banlist_opengui (struct session *sess)
|
||||
{
|
||||
GtkWidget *vbox1;
|
||||
GtkWidget *bbox;
|
||||
char tbuf[256];
|
||||
|
||||
if (sess->res->banlist_window)
|
||||
{
|
||||
mg_bring_tofront (sess->res->banlist_window);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sess->type != SESS_CHANNEL)
|
||||
{
|
||||
fe_message (_("You can only open the Ban List window while in a channel tab."), FE_MSG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf (tbuf, sizeof tbuf, _("XChat: Ban List (%s)"),
|
||||
sess->server->servername);
|
||||
|
||||
sess->res->banlist_window = mg_create_generic_tab ("BanList", tbuf, FALSE,
|
||||
TRUE, banlist_closegui, sess, 550, 200, &vbox1, sess->server);
|
||||
|
||||
/* create banlist view */
|
||||
sess->res->banlist_treeview = banlist_treeview_new (vbox1);
|
||||
|
||||
bbox = gtk_hbutton_box_new ();
|
||||
gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_SPREAD);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (bbox), 5);
|
||||
gtk_box_pack_end (GTK_BOX (vbox1), bbox, 0, 0, 0);
|
||||
gtk_widget_show (bbox);
|
||||
|
||||
gtkutil_button (bbox, GTK_STOCK_REMOVE, 0, banlist_unban, sess,
|
||||
_("Remove"));
|
||||
gtkutil_button (bbox, GTK_STOCK_REMOVE, 0, banlist_crop, sess,
|
||||
_("Crop"));
|
||||
gtkutil_button (bbox, GTK_STOCK_CLEAR, 0, banlist_clear, sess,
|
||||
_("Clear"));
|
||||
|
||||
sess->res->banlist_butRefresh = gtkutil_button (bbox, GTK_STOCK_REFRESH, 0, banlist_refresh, sess, _("Refresh"));
|
||||
|
||||
banlist_do_refresh (sess);
|
||||
|
||||
gtk_widget_show (sess->res->banlist_window);
|
||||
}
|
||||
1
src/fe-gtk/banlist.h
Normal file
1
src/fe-gtk/banlist.h
Normal file
@@ -0,0 +1 @@
|
||||
void banlist_opengui (session *sess);
|
||||
943
src/fe-gtk/chanlist.c
Normal file
943
src/fe-gtk/chanlist.c
Normal file
@@ -0,0 +1,943 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998-2006 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkalignment.h>
|
||||
#include <gtk/gtkcellrenderertext.h>
|
||||
#include <gtk/gtkcheckbutton.h>
|
||||
#include <gtk/gtkcombobox.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gtk/gtkscrolledwindow.h>
|
||||
#include <gtk/gtkspinbutton.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtktable.h>
|
||||
#include <gtk/gtktreeselection.h>
|
||||
#include <gtk/gtkvbox.h>
|
||||
#include <gtk/gtkvseparator.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "../common/cfgfiles.h"
|
||||
#include "../common/outbound.h"
|
||||
#include "../common/util.h"
|
||||
#include "../common/fe.h"
|
||||
#include "../common/server.h"
|
||||
#include "gtkutil.h"
|
||||
#include "maingui.h"
|
||||
|
||||
|
||||
#include "custom-list.h"
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
COL_CHANNEL,
|
||||
COL_USERS,
|
||||
COL_TOPIC,
|
||||
N_COLUMNS
|
||||
};
|
||||
|
||||
#ifndef CUSTOM_LIST
|
||||
typedef struct /* this is now in custom-list.h */
|
||||
{
|
||||
char *topic;
|
||||
char *collation_key;
|
||||
guint32 pos;
|
||||
guint32 users;
|
||||
/* channel string lives beyond "users" */
|
||||
#define GET_CHAN(row) (((char *)row)+sizeof(chanlistrow))
|
||||
}
|
||||
chanlistrow;
|
||||
#endif
|
||||
|
||||
#define GET_MODEL(xserv) (gtk_tree_view_get_model(GTK_TREE_VIEW(xserv->gui->chanlist_list)))
|
||||
|
||||
|
||||
static gboolean
|
||||
chanlist_match (server *serv, const char *str)
|
||||
{
|
||||
switch (serv->gui->chanlist_search_type)
|
||||
{
|
||||
case 1:
|
||||
return match (GTK_ENTRY (serv->gui->chanlist_wild)->text, str);
|
||||
#ifndef WIN32
|
||||
case 2:
|
||||
if (!serv->gui->have_regex)
|
||||
return 0;
|
||||
/* regex returns 0 if it's a match: */
|
||||
return !regexec (&serv->gui->chanlist_match_regex, str, 1, NULL, REG_NOTBOL);
|
||||
#endif
|
||||
default: /* case 0: */
|
||||
return nocasestrstr (str, GTK_ENTRY (serv->gui->chanlist_wild)->text) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the caption to reflect the number of users and channels
|
||||
*/
|
||||
static void
|
||||
chanlist_update_caption (server *serv)
|
||||
{
|
||||
gchar tbuf[256];
|
||||
|
||||
snprintf (tbuf, sizeof tbuf,
|
||||
_("Displaying %d/%d users on %d/%d channels."),
|
||||
serv->gui->chanlist_users_shown_count,
|
||||
serv->gui->chanlist_users_found_count,
|
||||
serv->gui->chanlist_channels_shown_count,
|
||||
serv->gui->chanlist_channels_found_count);
|
||||
|
||||
gtk_label_set_text (GTK_LABEL (serv->gui->chanlist_label), tbuf);
|
||||
serv->gui->chanlist_caption_is_stale = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_update_buttons (server *serv)
|
||||
{
|
||||
if (serv->gui->chanlist_channels_shown_count)
|
||||
{
|
||||
gtk_widget_set_sensitive (serv->gui->chanlist_join, TRUE);
|
||||
gtk_widget_set_sensitive (serv->gui->chanlist_savelist, TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_set_sensitive (serv->gui->chanlist_join, FALSE);
|
||||
gtk_widget_set_sensitive (serv->gui->chanlist_savelist, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_reset_counters (server *serv)
|
||||
{
|
||||
serv->gui->chanlist_users_found_count = 0;
|
||||
serv->gui->chanlist_users_shown_count = 0;
|
||||
serv->gui->chanlist_channels_found_count = 0;
|
||||
serv->gui->chanlist_channels_shown_count = 0;
|
||||
|
||||
chanlist_update_caption (serv);
|
||||
chanlist_update_buttons (serv);
|
||||
}
|
||||
|
||||
/* free up our entire linked list and all the nodes */
|
||||
|
||||
static void
|
||||
chanlist_data_free (server *serv)
|
||||
{
|
||||
GSList *rows;
|
||||
chanlistrow *data;
|
||||
|
||||
if (serv->gui->chanlist_data_stored_rows)
|
||||
{
|
||||
for (rows = serv->gui->chanlist_data_stored_rows; rows != NULL;
|
||||
rows = rows->next)
|
||||
{
|
||||
data = rows->data;
|
||||
g_free (data->topic);
|
||||
g_free (data->collation_key);
|
||||
free (data);
|
||||
}
|
||||
|
||||
g_slist_free (serv->gui->chanlist_data_stored_rows);
|
||||
serv->gui->chanlist_data_stored_rows = NULL;
|
||||
}
|
||||
|
||||
g_slist_free (serv->gui->chanlist_pending_rows);
|
||||
serv->gui->chanlist_pending_rows = NULL;
|
||||
}
|
||||
|
||||
/* add any rows we received from the server in the last 0.25s to the GUI */
|
||||
|
||||
static void
|
||||
chanlist_flush_pending (server *serv)
|
||||
{
|
||||
GSList *list = serv->gui->chanlist_pending_rows;
|
||||
GtkTreeModel *model;
|
||||
chanlistrow *row;
|
||||
|
||||
if (!list)
|
||||
{
|
||||
if (serv->gui->chanlist_caption_is_stale)
|
||||
chanlist_update_caption (serv);
|
||||
return;
|
||||
}
|
||||
model = GET_MODEL (serv);
|
||||
|
||||
while (list)
|
||||
{
|
||||
row = list->data;
|
||||
custom_list_append (CUSTOM_LIST (model), row);
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
g_slist_free (serv->gui->chanlist_pending_rows);
|
||||
serv->gui->chanlist_pending_rows = NULL;
|
||||
chanlist_update_caption (serv);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
chanlist_timeout (server *serv)
|
||||
{
|
||||
chanlist_flush_pending (serv);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Places a data row into the gui GtkTreeView, if and only if the row matches
|
||||
* the user and regex/search requirements.
|
||||
*/
|
||||
static void
|
||||
chanlist_place_row_in_gui (server *serv, chanlistrow *next_row, gboolean force)
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
|
||||
/* First, update the 'found' counter values */
|
||||
serv->gui->chanlist_users_found_count += next_row->users;
|
||||
serv->gui->chanlist_channels_found_count++;
|
||||
|
||||
if (serv->gui->chanlist_channels_shown_count == 1)
|
||||
/* join & save buttons become live */
|
||||
chanlist_update_buttons (serv);
|
||||
|
||||
if (next_row->users < serv->gui->chanlist_minusers)
|
||||
{
|
||||
serv->gui->chanlist_caption_is_stale = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (next_row->users > serv->gui->chanlist_maxusers
|
||||
&& serv->gui->chanlist_maxusers > 0)
|
||||
{
|
||||
serv->gui->chanlist_caption_is_stale = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (GTK_ENTRY (serv->gui->chanlist_wild)->text[0])
|
||||
{
|
||||
/* Check what the user wants to match. If both buttons or _neither_
|
||||
* button is checked, look for match in both by default.
|
||||
*/
|
||||
if (serv->gui->chanlist_match_wants_channel ==
|
||||
serv->gui->chanlist_match_wants_topic)
|
||||
{
|
||||
if (!chanlist_match (serv, GET_CHAN (next_row))
|
||||
&& !chanlist_match (serv, next_row->topic))
|
||||
{
|
||||
serv->gui->chanlist_caption_is_stale = TRUE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
else if (serv->gui->chanlist_match_wants_channel)
|
||||
{
|
||||
if (!chanlist_match (serv, GET_CHAN (next_row)))
|
||||
{
|
||||
serv->gui->chanlist_caption_is_stale = TRUE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
else if (serv->gui->chanlist_match_wants_topic)
|
||||
{
|
||||
if (!chanlist_match (serv, next_row->topic))
|
||||
{
|
||||
serv->gui->chanlist_caption_is_stale = TRUE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (force || serv->gui->chanlist_channels_shown_count < 20)
|
||||
{
|
||||
model = GET_MODEL (serv);
|
||||
/* makes it appear fast :) */
|
||||
custom_list_append (CUSTOM_LIST (model), next_row);
|
||||
chanlist_update_caption (serv);
|
||||
}
|
||||
else
|
||||
/* add it to GUI at the next update interval */
|
||||
serv->gui->chanlist_pending_rows = g_slist_prepend (serv->gui->chanlist_pending_rows, next_row);
|
||||
|
||||
/* Update the 'shown' counter values */
|
||||
serv->gui->chanlist_users_shown_count += next_row->users;
|
||||
serv->gui->chanlist_channels_shown_count++;
|
||||
}
|
||||
|
||||
/* Performs the LIST download from the IRC server. */
|
||||
|
||||
static void
|
||||
chanlist_do_refresh (server *serv)
|
||||
{
|
||||
if (serv->gui->chanlist_flash_tag)
|
||||
{
|
||||
g_source_remove (serv->gui->chanlist_flash_tag);
|
||||
serv->gui->chanlist_flash_tag = 0;
|
||||
}
|
||||
|
||||
if (!serv->connected)
|
||||
{
|
||||
fe_message (_("Not connected."), FE_MSG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
custom_list_clear ((CustomList *)GET_MODEL (serv));
|
||||
gtk_widget_set_sensitive (serv->gui->chanlist_refresh, FALSE);
|
||||
|
||||
chanlist_data_free (serv);
|
||||
chanlist_reset_counters (serv);
|
||||
|
||||
/* can we request a list with minusers arg? */
|
||||
if (serv->use_listargs)
|
||||
{
|
||||
/* yes - it will download faster */
|
||||
serv->p_list_channels (serv, "", serv->gui->chanlist_minusers);
|
||||
/* don't allow the spin button below this value from now on */
|
||||
serv->gui->chanlist_minusers_downloaded = serv->gui->chanlist_minusers;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* download all, filter minusers locally only */
|
||||
serv->p_list_channels (serv, "", 1);
|
||||
serv->gui->chanlist_minusers_downloaded = 1;
|
||||
}
|
||||
|
||||
/* gtk_spin_button_set_range ((GtkSpinButton *)serv->gui->chanlist_min_spin,
|
||||
serv->gui->chanlist_minusers_downloaded, 999999);*/
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_refresh (GtkWidget * wid, server *serv)
|
||||
{
|
||||
chanlist_do_refresh (serv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the gui GtkTreeView with stored items from the GSList.
|
||||
*/
|
||||
static void
|
||||
chanlist_build_gui_list (server *serv)
|
||||
{
|
||||
GSList *rows;
|
||||
|
||||
/* first check if the list is present */
|
||||
if (serv->gui->chanlist_data_stored_rows == NULL)
|
||||
{
|
||||
/* start a download */
|
||||
chanlist_do_refresh (serv);
|
||||
return;
|
||||
}
|
||||
|
||||
custom_list_clear ((CustomList *)GET_MODEL (serv));
|
||||
|
||||
/* discard pending rows FIXME: free the structs? */
|
||||
g_slist_free (serv->gui->chanlist_pending_rows);
|
||||
serv->gui->chanlist_pending_rows = NULL;
|
||||
|
||||
/* Reset the counters */
|
||||
chanlist_reset_counters (serv);
|
||||
|
||||
/* Refill the list */
|
||||
for (rows = serv->gui->chanlist_data_stored_rows; rows != NULL;
|
||||
rows = rows->next)
|
||||
{
|
||||
chanlist_place_row_in_gui (serv, rows->data, TRUE);
|
||||
}
|
||||
|
||||
custom_list_resort ((CustomList *)GET_MODEL (serv));
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts incoming channel data from inbound.c, allocates new space for a
|
||||
* chanlistrow, adds it to our linked list and calls chanlist_place_row_in_gui.
|
||||
*/
|
||||
void
|
||||
fe_add_chan_list (server *serv, char *chan, char *users, char *topic)
|
||||
{
|
||||
chanlistrow *next_row;
|
||||
int len = strlen (chan) + 1;
|
||||
|
||||
/* we allocate the struct and channel string in one go */
|
||||
next_row = malloc (sizeof (chanlistrow) + len);
|
||||
memcpy (((char *)next_row) + sizeof (chanlistrow), chan, len);
|
||||
next_row->topic = strip_color (topic, -1, STRIP_ALL);
|
||||
next_row->collation_key = g_utf8_collate_key (chan, len-1);
|
||||
if (!(next_row->collation_key))
|
||||
next_row->collation_key = g_strdup (chan);
|
||||
next_row->users = atoi (users);
|
||||
|
||||
/* add this row to the data */
|
||||
serv->gui->chanlist_data_stored_rows =
|
||||
g_slist_prepend (serv->gui->chanlist_data_stored_rows, next_row);
|
||||
|
||||
/* _possibly_ add the row to the gui */
|
||||
chanlist_place_row_in_gui (serv, next_row, FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
fe_chan_list_end (server *serv)
|
||||
{
|
||||
/* download complete */
|
||||
chanlist_flush_pending (serv);
|
||||
gtk_widget_set_sensitive (serv->gui->chanlist_refresh, TRUE);
|
||||
custom_list_resort ((CustomList *)GET_MODEL (serv));
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_search_pressed (GtkButton * button, server *serv)
|
||||
{
|
||||
chanlist_build_gui_list (serv);
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_find_cb (GtkWidget * wid, server *serv)
|
||||
{
|
||||
#ifndef WIN32
|
||||
/* recompile the regular expression. */
|
||||
if (serv->gui->have_regex)
|
||||
{
|
||||
serv->gui->have_regex = 0;
|
||||
regfree (&serv->gui->chanlist_match_regex);
|
||||
}
|
||||
|
||||
if (regcomp (&serv->gui->chanlist_match_regex, GTK_ENTRY (wid)->text,
|
||||
REG_ICASE | REG_EXTENDED | REG_NOSUB) == 0)
|
||||
serv->gui->have_regex = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_match_channel_button_toggled (GtkWidget * wid, server *serv)
|
||||
{
|
||||
serv->gui->chanlist_match_wants_channel = GTK_TOGGLE_BUTTON (wid)->active;
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_match_topic_button_toggled (GtkWidget * wid, server *serv)
|
||||
{
|
||||
serv->gui->chanlist_match_wants_topic = GTK_TOGGLE_BUTTON (wid)->active;
|
||||
}
|
||||
|
||||
static char *
|
||||
chanlist_get_selected (server *serv, gboolean get_topic)
|
||||
{
|
||||
char *chan;
|
||||
GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (serv->gui->chanlist_list));
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!gtk_tree_selection_get_selected (sel, &model, &iter))
|
||||
return NULL;
|
||||
|
||||
gtk_tree_model_get (model, &iter, get_topic ? COL_TOPIC : COL_CHANNEL, &chan, -1);
|
||||
return chan;
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_join (GtkWidget * wid, server *serv)
|
||||
{
|
||||
char tbuf[CHANLEN + 6];
|
||||
char *chan = chanlist_get_selected (serv, FALSE);
|
||||
if (chan)
|
||||
{
|
||||
if (serv->connected && (strcmp (chan, "*") != 0))
|
||||
{
|
||||
snprintf (tbuf, sizeof (tbuf), "join %s", chan);
|
||||
handle_command (serv->server_session, tbuf, FALSE);
|
||||
} else
|
||||
gdk_beep ();
|
||||
g_free (chan);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_filereq_done (server *serv, char *file)
|
||||
{
|
||||
time_t t = time (0);
|
||||
int fh, users;
|
||||
char *chan, *topic;
|
||||
char buf[1024];
|
||||
GtkTreeModel *model = GET_MODEL (serv);
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
fh = xchat_open_file (file, O_TRUNC | O_WRONLY | O_CREAT, 0600,
|
||||
XOF_DOMODE | XOF_FULLPATH);
|
||||
if (fh == -1)
|
||||
return;
|
||||
|
||||
snprintf (buf, sizeof buf, "XChat Channel List: %s - %s\n",
|
||||
serv->servername, ctime (&t));
|
||||
write (fh, buf, strlen (buf));
|
||||
|
||||
if (gtk_tree_model_get_iter_first (model, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (model, &iter,
|
||||
COL_CHANNEL, &chan,
|
||||
COL_USERS, &users,
|
||||
COL_TOPIC, &topic, -1);
|
||||
snprintf (buf, sizeof buf, "%-16s %-5d%s\n", chan, users, topic);
|
||||
g_free (chan);
|
||||
g_free (topic);
|
||||
write (fh, buf, strlen (buf));
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
|
||||
close (fh);
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_save (GtkWidget * wid, server *serv)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GtkTreeModel *model = GET_MODEL (serv);
|
||||
|
||||
if (gtk_tree_model_get_iter_first (model, &iter))
|
||||
gtkutil_file_req (_("Select an output filename"), chanlist_filereq_done,
|
||||
serv, NULL, FRF_WRITE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
chanlist_flash (server *serv)
|
||||
{
|
||||
if (serv->gui->chanlist_refresh->state != GTK_STATE_ACTIVE)
|
||||
gtk_widget_set_state (serv->gui->chanlist_refresh, GTK_STATE_ACTIVE);
|
||||
else
|
||||
gtk_widget_set_state (serv->gui->chanlist_refresh, GTK_STATE_PRELIGHT);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_minusers (GtkSpinButton *wid, server *serv)
|
||||
{
|
||||
serv->gui->chanlist_minusers = gtk_spin_button_get_value_as_int (wid);
|
||||
|
||||
if (serv->gui->chanlist_minusers < serv->gui->chanlist_minusers_downloaded)
|
||||
{
|
||||
if (serv->gui->chanlist_flash_tag == 0)
|
||||
serv->gui->chanlist_flash_tag = g_timeout_add (500, (GSourceFunc)chanlist_flash, serv);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (serv->gui->chanlist_flash_tag)
|
||||
{
|
||||
g_source_remove (serv->gui->chanlist_flash_tag);
|
||||
serv->gui->chanlist_flash_tag = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_maxusers (GtkSpinButton *wid, server *serv)
|
||||
{
|
||||
serv->gui->chanlist_maxusers = gtk_spin_button_get_value_as_int (wid);
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_dclick_cb (GtkTreeView *view, GtkTreePath *path,
|
||||
GtkTreeViewColumn *column, gpointer data)
|
||||
{
|
||||
chanlist_join (0, (server *) data); /* double clicked a row */
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_menu_destroy (GtkWidget *menu, gpointer userdata)
|
||||
{
|
||||
gtk_widget_destroy (menu);
|
||||
g_object_unref (menu);
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_copychannel (GtkWidget *item, server *serv)
|
||||
{
|
||||
char *chan = chanlist_get_selected (serv, FALSE);
|
||||
if (chan)
|
||||
{
|
||||
gtkutil_copy_to_clipboard (item, NULL, chan);
|
||||
g_free (chan);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_copytopic (GtkWidget *item, server *serv)
|
||||
{
|
||||
char *topic = chanlist_get_selected (serv, TRUE);
|
||||
if (topic)
|
||||
{
|
||||
gtkutil_copy_to_clipboard (item, NULL, topic);
|
||||
g_free (topic);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
chanlist_button_cb (GtkTreeView *tree, GdkEventButton *event, server *serv)
|
||||
{
|
||||
GtkWidget *menu;
|
||||
GtkTreeSelection *sel;
|
||||
GtkTreePath *path;
|
||||
char *chan;
|
||||
|
||||
if (event->button != 3)
|
||||
return FALSE;
|
||||
|
||||
if (!gtk_tree_view_get_path_at_pos (tree, event->x, event->y, &path, 0, 0, 0))
|
||||
return FALSE;
|
||||
|
||||
/* select what they right-clicked on */
|
||||
sel = gtk_tree_view_get_selection (tree);
|
||||
gtk_tree_selection_unselect_all (sel);
|
||||
gtk_tree_selection_select_path (sel, path);
|
||||
gtk_tree_path_free (path);
|
||||
|
||||
menu = gtk_menu_new ();
|
||||
if (event->window)
|
||||
gtk_menu_set_screen (GTK_MENU (menu), gdk_drawable_get_screen (event->window));
|
||||
g_object_ref (menu);
|
||||
g_object_ref_sink (menu);
|
||||
g_object_unref (menu);
|
||||
g_signal_connect (G_OBJECT (menu), "selection-done",
|
||||
G_CALLBACK (chanlist_menu_destroy), NULL);
|
||||
mg_create_icon_item (_("_Join Channel"), GTK_STOCK_JUMP_TO, menu,
|
||||
chanlist_join, serv);
|
||||
mg_create_icon_item (_("_Copy Channel Name"), GTK_STOCK_COPY, menu,
|
||||
chanlist_copychannel, serv);
|
||||
mg_create_icon_item (_("Copy _Topic Text"), GTK_STOCK_COPY, menu,
|
||||
chanlist_copytopic, serv);
|
||||
|
||||
chan = chanlist_get_selected (serv, FALSE);
|
||||
menu_addfavoritemenu (serv, menu, chan);
|
||||
g_free (chan);
|
||||
|
||||
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, event->time);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_destroy_widget (GtkWidget *wid, server *serv)
|
||||
{
|
||||
custom_list_clear ((CustomList *)GET_MODEL (serv));
|
||||
chanlist_data_free (serv);
|
||||
|
||||
if (serv->gui->chanlist_flash_tag)
|
||||
{
|
||||
g_source_remove (serv->gui->chanlist_flash_tag);
|
||||
serv->gui->chanlist_flash_tag = 0;
|
||||
}
|
||||
|
||||
if (serv->gui->chanlist_tag)
|
||||
{
|
||||
g_source_remove (serv->gui->chanlist_tag);
|
||||
serv->gui->chanlist_tag = 0;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
if (serv->gui->have_regex)
|
||||
{
|
||||
regfree (&serv->gui->chanlist_match_regex);
|
||||
serv->gui->have_regex = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_closegui (GtkWidget *wid, server *serv)
|
||||
{
|
||||
if (is_server (serv))
|
||||
serv->gui->chanlist_window = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_add_column (GtkWidget *tree, int textcol, int size, char *title, gboolean right_justified)
|
||||
{
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeViewColumn *col;
|
||||
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
if (right_justified)
|
||||
g_object_set (G_OBJECT (renderer), "xalign", (gfloat) 1.0, NULL);
|
||||
g_object_set (G_OBJECT (renderer), "ypad", (gint) 0, NULL);
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree), -1, title,
|
||||
renderer, "text", textcol, NULL);
|
||||
gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
|
||||
|
||||
col = gtk_tree_view_get_column (GTK_TREE_VIEW (tree), textcol);
|
||||
gtk_tree_view_column_set_sort_column_id (col, textcol);
|
||||
gtk_tree_view_column_set_resizable (col, TRUE);
|
||||
if (textcol != COL_TOPIC)
|
||||
{
|
||||
gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
|
||||
gtk_tree_view_column_set_fixed_width (col, size);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chanlist_combo_cb (GtkWidget *combo, server *serv)
|
||||
{
|
||||
serv->gui->chanlist_search_type = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
|
||||
}
|
||||
|
||||
void
|
||||
chanlist_opengui (server *serv, int do_refresh)
|
||||
{
|
||||
GtkWidget *vbox, *hbox, *table, *wid, *view;
|
||||
char tbuf[256];
|
||||
GtkListStore *store;
|
||||
|
||||
if (serv->gui->chanlist_window)
|
||||
{
|
||||
mg_bring_tofront (serv->gui->chanlist_window);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf (tbuf, sizeof tbuf, _("XChat: Channel List (%s)"),
|
||||
server_get_network (serv, TRUE));
|
||||
|
||||
serv->gui->chanlist_pending_rows = NULL;
|
||||
serv->gui->chanlist_tag = 0;
|
||||
serv->gui->chanlist_flash_tag = 0;
|
||||
serv->gui->chanlist_data_stored_rows = NULL;
|
||||
|
||||
if (!serv->gui->chanlist_minusers)
|
||||
serv->gui->chanlist_minusers = 5;
|
||||
|
||||
if (!serv->gui->chanlist_maxusers)
|
||||
serv->gui->chanlist_maxusers = 9999;
|
||||
|
||||
serv->gui->chanlist_window =
|
||||
mg_create_generic_tab ("ChanList", tbuf, FALSE, TRUE, chanlist_closegui,
|
||||
serv, 640, 480, &vbox, serv);
|
||||
|
||||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
|
||||
gtk_box_set_spacing (GTK_BOX (vbox), 12);
|
||||
|
||||
/* make a label to store the user/channel info */
|
||||
wid = gtk_label_new (NULL);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
serv->gui->chanlist_label = wid;
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
store = (GtkListStore *) custom_list_new();
|
||||
view = gtkutil_treeview_new (vbox, GTK_TREE_MODEL (store), NULL, -1);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (view->parent),
|
||||
GTK_SHADOW_IN);
|
||||
serv->gui->chanlist_list = view;
|
||||
|
||||
g_signal_connect (G_OBJECT (view), "row_activated",
|
||||
G_CALLBACK (chanlist_dclick_cb), serv);
|
||||
g_signal_connect (G_OBJECT (view), "button-press-event",
|
||||
G_CALLBACK (chanlist_button_cb), serv);
|
||||
|
||||
chanlist_add_column (view, COL_CHANNEL, 96, _("Channel"), FALSE);
|
||||
chanlist_add_column (view, COL_USERS, 50, _("Users"), TRUE);
|
||||
chanlist_add_column (view, COL_TOPIC, 50, _("Topic"), FALSE);
|
||||
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
|
||||
/* this is a speed up, but no horizontal scrollbar :( */
|
||||
/*gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE);*/
|
||||
gtk_widget_show (view);
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
table = gtk_table_new (4, 4, FALSE);
|
||||
gtk_table_set_col_spacings (GTK_TABLE (table), 12);
|
||||
gtk_table_set_row_spacings (GTK_TABLE (table), 3);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), table, 0, 1, 0);
|
||||
gtk_widget_show (table);
|
||||
|
||||
wid = gtkutil_button (NULL, GTK_STOCK_FIND, 0, chanlist_search_pressed, serv,
|
||||
_("_Search"));
|
||||
serv->gui->chanlist_search = wid;
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 3, 4, 3, 4,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
|
||||
wid = gtkutil_button (NULL, GTK_STOCK_REFRESH, 0, chanlist_refresh, serv,
|
||||
_("_Download List"));
|
||||
serv->gui->chanlist_refresh = wid;
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 3, 4, 2, 3,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
|
||||
wid = gtkutil_button (NULL, GTK_STOCK_SAVE_AS, 0, chanlist_save, serv,
|
||||
_("Save _List..."));
|
||||
serv->gui->chanlist_savelist = wid;
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 3, 4, 1, 2,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
|
||||
wid = gtkutil_button (NULL, GTK_STOCK_JUMP_TO, 0, chanlist_join, serv,
|
||||
_("_Join Channel"));
|
||||
serv->gui->chanlist_join = wid;
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 3, 4, 0, 1,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
wid = gtk_label_new (_("Show only:"));
|
||||
gtk_misc_set_alignment (GTK_MISC (wid), 0, 0.5);
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 0, 1, 3, 4,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
hbox = gtk_hbox_new (0, 0);
|
||||
gtk_box_set_spacing (GTK_BOX (hbox), 9);
|
||||
gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 3, 4,
|
||||
GTK_FILL, GTK_FILL, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
wid = gtk_label_new (_("channels with"));
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
wid = gtk_spin_button_new_with_range (1, 999999, 1);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (wid),
|
||||
serv->gui->chanlist_minusers);
|
||||
g_signal_connect (G_OBJECT (wid), "value_changed",
|
||||
G_CALLBACK (chanlist_minusers), serv);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
serv->gui->chanlist_min_spin = wid;
|
||||
|
||||
wid = gtk_label_new (_("to"));
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
wid = gtk_spin_button_new_with_range (1, 999999, 1);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (wid),
|
||||
serv->gui->chanlist_maxusers);
|
||||
g_signal_connect (G_OBJECT (wid), "value_changed",
|
||||
G_CALLBACK (chanlist_maxusers), serv);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
wid = gtk_label_new (_("users."));
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
wid = gtk_label_new (_("Look in:"));
|
||||
gtk_misc_set_alignment (GTK_MISC (wid), 0, 0.5);
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 0, 1, 2, 3,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
hbox = gtk_hbox_new (0, 0);
|
||||
gtk_box_set_spacing (GTK_BOX (hbox), 12);
|
||||
gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 2, 3,
|
||||
GTK_FILL, GTK_FILL, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
wid = gtk_check_button_new_with_label (_("Channel name"));
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid), TRUE);
|
||||
gtk_signal_connect (GTK_OBJECT (wid), "toggled",
|
||||
GTK_SIGNAL_FUNC
|
||||
(chanlist_match_channel_button_toggled), serv);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
wid = gtk_check_button_new_with_label (_("Topic"));
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid), TRUE);
|
||||
gtk_signal_connect (GTK_OBJECT (wid), "toggled",
|
||||
GTK_SIGNAL_FUNC (chanlist_match_topic_button_toggled),
|
||||
serv);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
serv->gui->chanlist_match_wants_channel = 1;
|
||||
serv->gui->chanlist_match_wants_topic = 1;
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
wid = gtk_label_new (_("Search type:"));
|
||||
gtk_misc_set_alignment (GTK_MISC (wid), 0, 0.5);
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 0, 1, 1, 2,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
wid = gtk_combo_box_new_text ();
|
||||
gtk_combo_box_append_text (GTK_COMBO_BOX (wid), _("Simple Search"));
|
||||
gtk_combo_box_append_text (GTK_COMBO_BOX (wid), _("Pattern Match (Wildcards)"));
|
||||
#ifndef WIN32
|
||||
gtk_combo_box_append_text (GTK_COMBO_BOX (wid), _("Regular Expression"));
|
||||
#endif
|
||||
gtk_combo_box_set_active (GTK_COMBO_BOX (wid), serv->gui->chanlist_search_type);
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 1, 2, 1, 2,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
g_signal_connect (G_OBJECT (wid), "changed",
|
||||
G_CALLBACK (chanlist_combo_cb), serv);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
wid = gtk_label_new (_("Find:"));
|
||||
gtk_misc_set_alignment (GTK_MISC (wid), 0, 0.5);
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 0, 1, 0, 1,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
wid = gtk_entry_new_with_max_length (255);
|
||||
gtk_signal_connect (GTK_OBJECT (wid), "changed",
|
||||
GTK_SIGNAL_FUNC (chanlist_find_cb), serv);
|
||||
gtk_signal_connect (GTK_OBJECT (wid), "activate",
|
||||
GTK_SIGNAL_FUNC (chanlist_search_pressed),
|
||||
(gpointer) serv);
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 1, 2, 0, 1,
|
||||
GTK_EXPAND | GTK_FILL, 0, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
serv->gui->chanlist_wild = wid;
|
||||
|
||||
chanlist_find_cb (wid, serv);
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
wid = gtk_vseparator_new ();
|
||||
gtk_table_attach (GTK_TABLE (table), wid, 2, 3, 0, 5,
|
||||
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
g_signal_connect (G_OBJECT (serv->gui->chanlist_window), "destroy",
|
||||
G_CALLBACK (chanlist_destroy_widget), serv);
|
||||
|
||||
/* reset the counters. */
|
||||
chanlist_reset_counters (serv);
|
||||
|
||||
serv->gui->chanlist_tag = g_timeout_add (250, (GSourceFunc)chanlist_timeout, serv);
|
||||
|
||||
if (do_refresh)
|
||||
chanlist_do_refresh (serv);
|
||||
|
||||
chanlist_update_buttons (serv);
|
||||
gtk_widget_show (serv->gui->chanlist_window);
|
||||
gtk_widget_grab_focus (serv->gui->chanlist_refresh);
|
||||
}
|
||||
1
src/fe-gtk/chanlist.h
Normal file
1
src/fe-gtk/chanlist.h
Normal file
@@ -0,0 +1 @@
|
||||
void chanlist_opengui (server *serv, int do_refresh);
|
||||
779
src/fe-gtk/chanview-tabs.c
Normal file
779
src/fe-gtk/chanview-tabs.c
Normal file
@@ -0,0 +1,779 @@
|
||||
/* file included in chanview.c */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkWidget *outer; /* outer box */
|
||||
GtkWidget *inner; /* inner box */
|
||||
GtkWidget *b1; /* button1 */
|
||||
GtkWidget *b2; /* button2 */
|
||||
} tabview;
|
||||
|
||||
static void chanview_populate (chanview *cv);
|
||||
|
||||
/* ignore "toggled" signal? */
|
||||
static int ignore_toggle = FALSE;
|
||||
static int tab_left_is_moving = 0;
|
||||
static int tab_right_is_moving = 0;
|
||||
|
||||
/* userdata for gobjects used here:
|
||||
*
|
||||
* tab (togglebuttons inside boxes):
|
||||
* "u" userdata passed to tab-focus callback function (sess)
|
||||
* "c" the tab's (chan *)
|
||||
*
|
||||
* box (family box)
|
||||
* "f" family
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* GtkViewports request at least as much space as their children do.
|
||||
* If we don't intervene here, the GtkViewport will be granted its
|
||||
* request, even at the expense of resizing the top-level window.
|
||||
*/
|
||||
static void
|
||||
cv_tabs_sizerequest (GtkWidget *viewport, GtkRequisition *requisition, chanview *cv)
|
||||
{
|
||||
if (!cv->vertical)
|
||||
requisition->width = 1;
|
||||
else
|
||||
requisition->height = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_sizealloc (GtkWidget *widget, GtkAllocation *allocation, chanview *cv)
|
||||
{
|
||||
GtkAdjustment *adj;
|
||||
GtkWidget *inner;
|
||||
gint viewport_size;
|
||||
|
||||
inner = ((tabview *)cv)->inner;
|
||||
|
||||
if (cv->vertical)
|
||||
{
|
||||
adj = gtk_viewport_get_vadjustment (GTK_VIEWPORT (inner->parent));
|
||||
gdk_window_get_geometry (inner->parent->window, 0, 0, 0, &viewport_size, 0);
|
||||
} else
|
||||
{
|
||||
adj = gtk_viewport_get_hadjustment (GTK_VIEWPORT (inner->parent));
|
||||
gdk_window_get_geometry (inner->parent->window, 0, 0, &viewport_size, 0, 0);
|
||||
}
|
||||
|
||||
if (adj->upper <= viewport_size)
|
||||
{
|
||||
gtk_widget_hide (((tabview *)cv)->b1);
|
||||
gtk_widget_hide (((tabview *)cv)->b2);
|
||||
} else
|
||||
{
|
||||
gtk_widget_show (((tabview *)cv)->b1);
|
||||
gtk_widget_show (((tabview *)cv)->b2);
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
tab_search_offset (GtkWidget *inner, gint start_offset,
|
||||
gboolean forward, gboolean vertical)
|
||||
{
|
||||
GList *boxes;
|
||||
GList *tabs;
|
||||
GtkWidget *box;
|
||||
GtkWidget *button;
|
||||
gint found;
|
||||
|
||||
boxes = GTK_BOX (inner)->children;
|
||||
if (!forward && boxes)
|
||||
boxes = g_list_last (boxes);
|
||||
|
||||
while (boxes)
|
||||
{
|
||||
box = ((GtkBoxChild *)boxes->data)->widget;
|
||||
boxes = (forward ? boxes->next : boxes->prev);
|
||||
|
||||
tabs = GTK_BOX (box)->children;
|
||||
if (!forward && tabs)
|
||||
tabs = g_list_last (tabs);
|
||||
|
||||
while (tabs)
|
||||
{
|
||||
button = ((GtkBoxChild *)tabs->data)->widget;
|
||||
tabs = (forward ? tabs->next : tabs->prev);
|
||||
|
||||
if (!GTK_IS_TOGGLE_BUTTON (button))
|
||||
continue;
|
||||
|
||||
found = (vertical ? button->allocation.y : button->allocation.x);
|
||||
if ((forward && found > start_offset) ||
|
||||
(!forward && found < start_offset))
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
tab_scroll_left_up_clicked (GtkWidget *widget, chanview *cv)
|
||||
{
|
||||
GtkAdjustment *adj;
|
||||
gint viewport_size;
|
||||
gfloat new_value;
|
||||
GtkWidget *inner;
|
||||
gfloat i;
|
||||
|
||||
inner = ((tabview *)cv)->inner;
|
||||
|
||||
if (cv->vertical)
|
||||
{
|
||||
adj = gtk_viewport_get_vadjustment (GTK_VIEWPORT (inner->parent));
|
||||
gdk_window_get_geometry (inner->parent->window, 0, 0, 0, &viewport_size, 0);
|
||||
} else
|
||||
{
|
||||
adj = gtk_viewport_get_hadjustment (GTK_VIEWPORT (inner->parent));
|
||||
gdk_window_get_geometry (inner->parent->window, 0, 0, &viewport_size, 0, 0);
|
||||
}
|
||||
|
||||
new_value = tab_search_offset (inner, adj->value, 0, cv->vertical);
|
||||
|
||||
if (new_value + viewport_size > adj->upper)
|
||||
new_value = adj->upper - viewport_size;
|
||||
|
||||
if (!tab_left_is_moving)
|
||||
{
|
||||
tab_left_is_moving = 1;
|
||||
|
||||
for (i = adj->value; ((i > new_value) && (tab_left_is_moving)); i -= 0.1)
|
||||
{
|
||||
gtk_adjustment_set_value (adj, i);
|
||||
while (g_main_pending ())
|
||||
g_main_iteration (TRUE);
|
||||
}
|
||||
|
||||
gtk_adjustment_set_value (adj, new_value);
|
||||
|
||||
tab_left_is_moving = 0; /* hSP: set to false in case we didnt get stopped (the normal case) */
|
||||
}
|
||||
else
|
||||
{
|
||||
tab_left_is_moving = 0; /* hSP: jump directly to next element if user is clicking faster than we can scroll.. */
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tab_scroll_right_down_clicked (GtkWidget *widget, chanview *cv)
|
||||
{
|
||||
GtkAdjustment *adj;
|
||||
gint viewport_size;
|
||||
gfloat new_value;
|
||||
GtkWidget *inner;
|
||||
gfloat i;
|
||||
|
||||
inner = ((tabview *)cv)->inner;
|
||||
|
||||
if (cv->vertical)
|
||||
{
|
||||
adj = gtk_viewport_get_vadjustment (GTK_VIEWPORT (inner->parent));
|
||||
gdk_window_get_geometry (inner->parent->window, 0, 0, 0, &viewport_size, 0);
|
||||
} else
|
||||
{
|
||||
adj = gtk_viewport_get_hadjustment (GTK_VIEWPORT (inner->parent));
|
||||
gdk_window_get_geometry (inner->parent->window, 0, 0, &viewport_size, 0, 0);
|
||||
}
|
||||
|
||||
new_value = tab_search_offset (inner, adj->value, 1, cv->vertical);
|
||||
|
||||
if (new_value == 0 || new_value + viewport_size > adj->upper)
|
||||
new_value = adj->upper - viewport_size;
|
||||
|
||||
if (!tab_right_is_moving)
|
||||
{
|
||||
tab_right_is_moving = 1;
|
||||
|
||||
for (i = adj->value; ((i < new_value) && (tab_right_is_moving)); i += 0.1)
|
||||
{
|
||||
gtk_adjustment_set_value (adj, i);
|
||||
while (g_main_pending ())
|
||||
g_main_iteration (TRUE);
|
||||
}
|
||||
|
||||
gtk_adjustment_set_value (adj, new_value);
|
||||
|
||||
tab_right_is_moving = 0; /* hSP: set to false in case we didnt get stopped (the normal case) */
|
||||
}
|
||||
else
|
||||
{
|
||||
tab_right_is_moving = 0; /* hSP: jump directly to next element if user is clicking faster than we can scroll.. */
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
tab_scroll_cb (GtkWidget *widget, GdkEventScroll *event, gpointer cv)
|
||||
{
|
||||
/* mouse wheel scrolling */
|
||||
if (event->direction == GDK_SCROLL_UP)
|
||||
tab_scroll_left_up_clicked (widget, cv);
|
||||
else if (event->direction == GDK_SCROLL_DOWN)
|
||||
tab_scroll_right_down_clicked (widget, cv);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_xclick_cb (GtkWidget *button, chanview *cv)
|
||||
{
|
||||
cv->cb_xbutton (cv, cv->focused, cv->focused->tag, cv->focused->userdata);
|
||||
}
|
||||
|
||||
/* make a Scroll (arrow) button */
|
||||
|
||||
static GtkWidget *
|
||||
make_sbutton (GtkArrowType type, void *click_cb, void *userdata)
|
||||
{
|
||||
GtkWidget *button, *arrow;
|
||||
|
||||
button = gtk_button_new ();
|
||||
arrow = gtk_arrow_new (type, GTK_SHADOW_NONE);
|
||||
gtk_container_add (GTK_CONTAINER (button), arrow);
|
||||
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
|
||||
g_signal_connect (G_OBJECT (button), "clicked",
|
||||
G_CALLBACK (click_cb), userdata);
|
||||
g_signal_connect (G_OBJECT (button), "scroll_event",
|
||||
G_CALLBACK (tab_scroll_cb), userdata);
|
||||
gtk_widget_show (arrow);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_init (chanview *cv)
|
||||
{
|
||||
GtkWidget *box, *hbox = NULL;
|
||||
GtkWidget *viewport;
|
||||
GtkWidget *outer;
|
||||
GtkWidget *button;
|
||||
|
||||
if (cv->vertical)
|
||||
outer = gtk_vbox_new (0, 0);
|
||||
else
|
||||
outer = gtk_hbox_new (0, 0);
|
||||
((tabview *)cv)->outer = outer;
|
||||
g_signal_connect (G_OBJECT (outer), "size_allocate",
|
||||
G_CALLBACK (cv_tabs_sizealloc), cv);
|
||||
/* gtk_container_set_border_width (GTK_CONTAINER (outer), 2);*/
|
||||
gtk_widget_show (outer);
|
||||
|
||||
viewport = gtk_viewport_new (0, 0);
|
||||
gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
|
||||
g_signal_connect (G_OBJECT (viewport), "size_request",
|
||||
G_CALLBACK (cv_tabs_sizerequest), cv);
|
||||
g_signal_connect (G_OBJECT (viewport), "scroll_event",
|
||||
G_CALLBACK (tab_scroll_cb), cv);
|
||||
gtk_box_pack_start (GTK_BOX (outer), viewport, 1, 1, 0);
|
||||
gtk_widget_show (viewport);
|
||||
|
||||
if (cv->vertical)
|
||||
box = gtk_vbox_new (FALSE, 0);
|
||||
else
|
||||
box = gtk_hbox_new (FALSE, 0);
|
||||
((tabview *)cv)->inner = box;
|
||||
gtk_container_add (GTK_CONTAINER (viewport), box);
|
||||
gtk_widget_show (box);
|
||||
|
||||
/* if vertical, the buttons can be side by side */
|
||||
if (cv->vertical)
|
||||
{
|
||||
hbox = gtk_hbox_new (FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (outer), hbox, 0, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
}
|
||||
|
||||
/* make the Scroll buttons */
|
||||
((tabview *)cv)->b2 = make_sbutton (cv->vertical ?
|
||||
GTK_ARROW_UP : GTK_ARROW_LEFT,
|
||||
tab_scroll_left_up_clicked,
|
||||
cv);
|
||||
|
||||
((tabview *)cv)->b1 = make_sbutton (cv->vertical ?
|
||||
GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
|
||||
tab_scroll_right_down_clicked,
|
||||
cv);
|
||||
|
||||
if (hbox)
|
||||
{
|
||||
gtk_container_add (GTK_CONTAINER (hbox), ((tabview *)cv)->b2);
|
||||
gtk_container_add (GTK_CONTAINER (hbox), ((tabview *)cv)->b1);
|
||||
} else
|
||||
{
|
||||
gtk_box_pack_start (GTK_BOX (outer), ((tabview *)cv)->b2, 0, 0, 0);
|
||||
gtk_box_pack_start (GTK_BOX (outer), ((tabview *)cv)->b1, 0, 0, 0);
|
||||
}
|
||||
|
||||
button = gtkutil_button (outer, GTK_STOCK_CLOSE, NULL, cv_tabs_xclick_cb,
|
||||
cv, 0);
|
||||
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
|
||||
GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (cv->box), outer);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_postinit (chanview *cv)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
tab_add_sorted (chanview *cv, GtkWidget *box, GtkWidget *tab, chan *ch)
|
||||
{
|
||||
GList *list;
|
||||
GtkBoxChild *child;
|
||||
int i = 0;
|
||||
void *b;
|
||||
|
||||
if (!cv->sorted)
|
||||
{
|
||||
gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
|
||||
gtk_widget_show (tab);
|
||||
return;
|
||||
}
|
||||
|
||||
/* sorting TODO:
|
||||
* - move tab if renamed (dialogs) */
|
||||
|
||||
/* userdata, passed to mg_tabs_compare() */
|
||||
b = ch->userdata;
|
||||
|
||||
list = GTK_BOX (box)->children;
|
||||
while (list)
|
||||
{
|
||||
child = list->data;
|
||||
if (!GTK_IS_SEPARATOR (child->widget))
|
||||
{
|
||||
void *a = g_object_get_data (G_OBJECT (child->widget), "u");
|
||||
|
||||
if (ch->tag == 0 && cv->cb_compare (a, b) > 0)
|
||||
{
|
||||
gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
|
||||
gtk_box_reorder_child (GTK_BOX (box), tab, i);
|
||||
gtk_widget_show (tab);
|
||||
return;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
/* append */
|
||||
gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
|
||||
gtk_box_reorder_child (GTK_BOX (box), tab, i);
|
||||
gtk_widget_show (tab);
|
||||
}
|
||||
|
||||
/* remove empty boxes and separators */
|
||||
|
||||
static void
|
||||
cv_tabs_prune (chanview *cv)
|
||||
{
|
||||
GList *boxes, *children;
|
||||
GtkWidget *box, *inner;
|
||||
GtkBoxChild *child;
|
||||
int empty;
|
||||
|
||||
inner = ((tabview *)cv)->inner;
|
||||
boxes = GTK_BOX (inner)->children;
|
||||
while (boxes)
|
||||
{
|
||||
child = boxes->data;
|
||||
box = child->widget;
|
||||
boxes = boxes->next;
|
||||
|
||||
/* check if the box is empty (except a vseperator) */
|
||||
empty = TRUE;
|
||||
children = GTK_BOX (box)->children;
|
||||
while (children)
|
||||
{
|
||||
if (!GTK_IS_SEPARATOR (((GtkBoxChild *)children->data)->widget))
|
||||
{
|
||||
empty = FALSE;
|
||||
break;
|
||||
}
|
||||
children = children->next;
|
||||
}
|
||||
|
||||
if (empty)
|
||||
gtk_widget_destroy (box);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tab_add_real (chanview *cv, GtkWidget *tab, chan *ch)
|
||||
{
|
||||
GList *boxes, *children;
|
||||
GtkWidget *sep, *box, *inner;
|
||||
GtkBoxChild *child;
|
||||
int empty;
|
||||
|
||||
inner = ((tabview *)cv)->inner;
|
||||
/* see if a family for this tab already exists */
|
||||
boxes = GTK_BOX (inner)->children;
|
||||
while (boxes)
|
||||
{
|
||||
child = boxes->data;
|
||||
box = child->widget;
|
||||
|
||||
if (g_object_get_data (G_OBJECT (box), "f") == ch->family)
|
||||
{
|
||||
tab_add_sorted (cv, box, tab, ch);
|
||||
gtk_widget_queue_resize (inner->parent);
|
||||
return;
|
||||
}
|
||||
|
||||
boxes = boxes->next;
|
||||
|
||||
/* check if the box is empty (except a vseperator) */
|
||||
empty = TRUE;
|
||||
children = GTK_BOX (box)->children;
|
||||
while (children)
|
||||
{
|
||||
if (!GTK_IS_SEPARATOR (((GtkBoxChild *)children->data)->widget))
|
||||
{
|
||||
empty = FALSE;
|
||||
break;
|
||||
}
|
||||
children = children->next;
|
||||
}
|
||||
|
||||
if (empty)
|
||||
gtk_widget_destroy (box);
|
||||
}
|
||||
|
||||
/* create a new family box */
|
||||
if (cv->vertical)
|
||||
{
|
||||
/* vertical */
|
||||
box = gtk_vbox_new (FALSE, 0);
|
||||
sep = gtk_hseparator_new ();
|
||||
} else
|
||||
{
|
||||
/* horiz */
|
||||
box = gtk_hbox_new (FALSE, 0);
|
||||
sep = gtk_vseparator_new ();
|
||||
}
|
||||
|
||||
gtk_box_pack_end (GTK_BOX (box), sep, 0, 0, 4);
|
||||
gtk_widget_show (sep);
|
||||
gtk_box_pack_start (GTK_BOX (inner), box, 0, 0, 0);
|
||||
g_object_set_data (G_OBJECT (box), "f", ch->family);
|
||||
gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
|
||||
gtk_widget_show (tab);
|
||||
gtk_widget_show (box);
|
||||
gtk_widget_queue_resize (inner->parent);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
tab_ignore_cb (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* called when a tab is clicked (button down) */
|
||||
|
||||
static void
|
||||
tab_pressed_cb (GtkToggleButton *tab, chan *ch)
|
||||
{
|
||||
chan *old_tab;
|
||||
int is_switching = TRUE;
|
||||
chanview *cv = ch->cv;
|
||||
|
||||
ignore_toggle = TRUE;
|
||||
/* de-activate the old tab */
|
||||
old_tab = cv->focused;
|
||||
if (old_tab && old_tab->impl)
|
||||
{
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (old_tab->impl), FALSE);
|
||||
if (old_tab == ch)
|
||||
is_switching = FALSE;
|
||||
}
|
||||
gtk_toggle_button_set_active (tab, TRUE);
|
||||
ignore_toggle = FALSE;
|
||||
cv->focused = ch;
|
||||
|
||||
if (/*tab->active*/is_switching)
|
||||
/* call the focus callback */
|
||||
cv->cb_focus (cv, ch, ch->tag, ch->userdata);
|
||||
}
|
||||
|
||||
/* called for keyboard tab toggles only */
|
||||
static void
|
||||
tab_toggled_cb (GtkToggleButton *tab, chan *ch)
|
||||
{
|
||||
if (ignore_toggle)
|
||||
return;
|
||||
|
||||
/* activated a tab via keyboard */
|
||||
tab_pressed_cb (tab, ch);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
tab_click_cb (GtkWidget *wid, GdkEventButton *event, chan *ch)
|
||||
{
|
||||
return ch->cv->cb_contextmenu (ch->cv, ch, ch->tag, ch->userdata, event);
|
||||
}
|
||||
|
||||
static void *
|
||||
cv_tabs_add (chanview *cv, chan *ch, char *name, GtkTreeIter *parent)
|
||||
{
|
||||
GtkWidget *but;
|
||||
|
||||
but = gtk_toggle_button_new_with_label (name);
|
||||
gtk_widget_set_name (but, "xchat-tab");
|
||||
g_object_set_data (G_OBJECT (but), "c", ch);
|
||||
/* used to trap right-clicks */
|
||||
g_signal_connect (G_OBJECT (but), "button_press_event",
|
||||
G_CALLBACK (tab_click_cb), ch);
|
||||
/* avoid prelights */
|
||||
g_signal_connect (G_OBJECT (but), "enter_notify_event",
|
||||
G_CALLBACK (tab_ignore_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (but), "leave_notify_event",
|
||||
G_CALLBACK (tab_ignore_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (but), "pressed",
|
||||
G_CALLBACK (tab_pressed_cb), ch);
|
||||
/* for keyboard */
|
||||
g_signal_connect (G_OBJECT (but), "toggled",
|
||||
G_CALLBACK (tab_toggled_cb), ch);
|
||||
g_object_set_data (G_OBJECT (but), "u", ch->userdata);
|
||||
|
||||
tab_add_real (cv, but, ch);
|
||||
|
||||
return but;
|
||||
}
|
||||
|
||||
/* traverse all the family boxes of tabs
|
||||
*
|
||||
* A "group" is basically:
|
||||
* GtkV/HBox
|
||||
* `-GtkViewPort
|
||||
* `-GtkV/HBox (inner box)
|
||||
* `- GtkBox (family box)
|
||||
* `- GtkToggleButton
|
||||
* `- GtkToggleButton
|
||||
* `- ...
|
||||
* `- GtkBox
|
||||
* `- GtkToggleButton
|
||||
* `- GtkToggleButton
|
||||
* `- ...
|
||||
* `- ...
|
||||
*
|
||||
* */
|
||||
|
||||
static int
|
||||
tab_group_for_each_tab (chanview *cv,
|
||||
int (*callback) (GtkWidget *tab, int num, int usernum),
|
||||
int usernum)
|
||||
{
|
||||
GList *tabs;
|
||||
GList *boxes;
|
||||
GtkBoxChild *child;
|
||||
GtkBox *innerbox;
|
||||
int i;
|
||||
|
||||
innerbox = (GtkBox *) ((tabview *)cv)->inner;
|
||||
boxes = innerbox->children;
|
||||
i = 0;
|
||||
while (boxes)
|
||||
{
|
||||
child = boxes->data;
|
||||
tabs = GTK_BOX (child->widget)->children;
|
||||
|
||||
while (tabs)
|
||||
{
|
||||
child = tabs->data;
|
||||
|
||||
if (!GTK_IS_SEPARATOR (child->widget))
|
||||
{
|
||||
if (callback (child->widget, i, usernum) != -1)
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
tabs = tabs->next;
|
||||
}
|
||||
|
||||
boxes = boxes->next;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int
|
||||
tab_check_focus_cb (GtkWidget *tab, int num, int unused)
|
||||
{
|
||||
if (GTK_TOGGLE_BUTTON (tab)->active)
|
||||
return num;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* returns the currently focused tab number */
|
||||
|
||||
static int
|
||||
tab_group_get_cur_page (chanview *cv)
|
||||
{
|
||||
return tab_group_for_each_tab (cv, tab_check_focus_cb, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_focus (chan *ch)
|
||||
{
|
||||
if (ch->impl)
|
||||
/* focus the new one (tab_pressed_cb defocuses the old one) */
|
||||
tab_pressed_cb (GTK_TOGGLE_BUTTON (ch->impl), ch);
|
||||
}
|
||||
|
||||
static int
|
||||
tab_focus_num_cb (GtkWidget *tab, int num, int want)
|
||||
{
|
||||
if (num == want)
|
||||
{
|
||||
cv_tabs_focus (g_object_get_data (G_OBJECT (tab), "c"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_change_orientation (chanview *cv)
|
||||
{
|
||||
/* cleanup the old one */
|
||||
if (cv->func_cleanup)
|
||||
cv->func_cleanup (cv);
|
||||
|
||||
/* now rebuild a new tabbar or tree */
|
||||
cv->func_init (cv);
|
||||
chanview_populate (cv);
|
||||
}
|
||||
|
||||
/* switch to the tab number specified */
|
||||
|
||||
static void
|
||||
cv_tabs_move_focus (chanview *cv, gboolean relative, int num)
|
||||
{
|
||||
int i, max;
|
||||
|
||||
if (relative)
|
||||
{
|
||||
max = cv->size;
|
||||
i = tab_group_get_cur_page (cv) + num;
|
||||
/* make it wrap around at both ends */
|
||||
if (i < 0)
|
||||
i = max - 1;
|
||||
if (i >= max)
|
||||
i = 0;
|
||||
tab_group_for_each_tab (cv, tab_focus_num_cb, i);
|
||||
return;
|
||||
}
|
||||
|
||||
tab_group_for_each_tab (cv, tab_focus_num_cb, num);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_remove (chan *ch)
|
||||
{
|
||||
gtk_widget_destroy (ch->impl);
|
||||
ch->impl = NULL;
|
||||
|
||||
cv_tabs_prune (ch->cv);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_move (chan *ch, int delta)
|
||||
{
|
||||
int i, pos = 0;
|
||||
GList *list;
|
||||
GtkWidget *parent = ((GtkWidget *)ch->impl)->parent;
|
||||
|
||||
i = 0;
|
||||
for (list = GTK_BOX (parent)->children; list; list = list->next)
|
||||
{
|
||||
GtkBoxChild *child_entry;
|
||||
|
||||
child_entry = list->data;
|
||||
if (child_entry->widget == ch->impl)
|
||||
pos = i;
|
||||
i++;
|
||||
}
|
||||
|
||||
pos = (pos - delta) % i;
|
||||
gtk_box_reorder_child (GTK_BOX (parent), ch->impl, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_move_family (chan *ch, int delta)
|
||||
{
|
||||
int i, pos = 0;
|
||||
GList *list;
|
||||
GtkWidget *box = NULL;
|
||||
|
||||
/* find position of tab's family */
|
||||
i = 0;
|
||||
for (list = GTK_BOX (((tabview *)ch->cv)->inner)->children; list; list = list->next)
|
||||
{
|
||||
GtkBoxChild *child_entry;
|
||||
void *fam;
|
||||
|
||||
child_entry = list->data;
|
||||
fam = g_object_get_data (G_OBJECT (child_entry->widget), "f");
|
||||
if (fam == ch->family)
|
||||
{
|
||||
box = child_entry->widget;
|
||||
pos = i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
pos = (pos - delta) % i;
|
||||
gtk_box_reorder_child (GTK_BOX (box->parent), box, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_cleanup (chanview *cv)
|
||||
{
|
||||
if (cv->box)
|
||||
gtk_widget_destroy (((tabview *)cv)->outer);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_set_color (chan *ch, PangoAttrList *list)
|
||||
{
|
||||
gtk_label_set_attributes (GTK_LABEL (GTK_BIN (ch->impl)->child), list);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tabs_rename (chan *ch, char *name)
|
||||
{
|
||||
PangoAttrList *attr;
|
||||
GtkWidget *tab = ch->impl;
|
||||
|
||||
attr = gtk_label_get_attributes (GTK_LABEL (GTK_BIN (tab)->child));
|
||||
if (attr)
|
||||
pango_attr_list_ref (attr);
|
||||
|
||||
gtk_button_set_label (GTK_BUTTON (tab), name);
|
||||
gtk_widget_queue_resize (tab->parent->parent->parent);
|
||||
|
||||
if (attr)
|
||||
{
|
||||
gtk_label_set_attributes (GTK_LABEL (GTK_BIN (tab)->child), attr);
|
||||
pango_attr_list_unref (attr);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cv_tabs_is_collapsed (chan *ch)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static chan *
|
||||
cv_tabs_get_parent (chan *ch)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
364
src/fe-gtk/chanview-tree.c
Normal file
364
src/fe-gtk/chanview-tree.c
Normal file
@@ -0,0 +1,364 @@
|
||||
/* file included in chanview.c */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkTreeView *tree;
|
||||
GtkWidget *scrollw; /* scrolledWindow */
|
||||
} treeview;
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "fe-gtk.h"
|
||||
#include "maingui.h"
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtktreeview.h>
|
||||
|
||||
static void /* row-activated, when a row is double clicked */
|
||||
cv_tree_activated_cb (GtkTreeView *view, GtkTreePath *path,
|
||||
GtkTreeViewColumn *column, gpointer data)
|
||||
{
|
||||
if (gtk_tree_view_row_expanded (view, path))
|
||||
gtk_tree_view_collapse_row (view, path);
|
||||
else
|
||||
gtk_tree_view_expand_row (view, path, FALSE);
|
||||
}
|
||||
|
||||
static void /* row selected callback */
|
||||
cv_tree_sel_cb (GtkTreeSelection *sel, chanview *cv)
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
chan *ch;
|
||||
|
||||
if (gtk_tree_selection_get_selected (sel, &model, &iter))
|
||||
{
|
||||
gtk_tree_model_get (model, &iter, COL_CHAN, &ch, -1);
|
||||
|
||||
cv->focused = ch;
|
||||
cv->cb_focus (cv, ch, ch->tag, ch->userdata);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cv_tree_click_cb (GtkTreeView *tree, GdkEventButton *event, chanview *cv)
|
||||
{
|
||||
chan *ch;
|
||||
GtkTreeSelection *sel;
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter iter;
|
||||
int ret = FALSE;
|
||||
|
||||
if (event->button != 3 && event->state == 0)
|
||||
return FALSE;
|
||||
|
||||
sel = gtk_tree_view_get_selection (tree);
|
||||
if (gtk_tree_view_get_path_at_pos (tree, event->x, event->y, &path, 0, 0, 0))
|
||||
{
|
||||
if (event->button == 2)
|
||||
{
|
||||
gtk_tree_selection_unselect_all (sel);
|
||||
gtk_tree_selection_select_path (sel, path);
|
||||
}
|
||||
if (gtk_tree_model_get_iter (GTK_TREE_MODEL (cv->store), &iter, path))
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &iter, COL_CHAN, &ch, -1);
|
||||
ret = cv->cb_contextmenu (cv, ch, ch->tag, ch->userdata, event);
|
||||
}
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_init (chanview *cv)
|
||||
{
|
||||
GtkWidget *view, *win;
|
||||
GtkCellRenderer *renderer;
|
||||
static const GtkTargetEntry dnd_src_target[] =
|
||||
{
|
||||
{"XCHAT_CHANVIEW", GTK_TARGET_SAME_APP, 75 }
|
||||
};
|
||||
static const GtkTargetEntry dnd_dest_target[] =
|
||||
{
|
||||
{"XCHAT_USERLIST", GTK_TARGET_SAME_APP, 75 }
|
||||
};
|
||||
|
||||
win = gtk_scrolled_window_new (0, 0);
|
||||
/*gtk_container_set_border_width (GTK_CONTAINER (win), 1);*/
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (win),
|
||||
GTK_SHADOW_IN);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (win),
|
||||
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
||||
gtk_container_add (GTK_CONTAINER (cv->box), win);
|
||||
gtk_widget_show (win);
|
||||
|
||||
view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (cv->store));
|
||||
gtk_widget_set_name (view, "xchat-tree");
|
||||
if (cv->style)
|
||||
gtk_widget_set_style (view, cv->style);
|
||||
/*gtk_widget_modify_base (view, GTK_STATE_NORMAL, &colors[COL_BG]);*/
|
||||
GTK_WIDGET_UNSET_FLAGS (view, GTK_CAN_FOCUS);
|
||||
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
|
||||
#if GTK_CHECK_VERSION(2,10,0)
|
||||
if (!(prefs.gui_tweaks & 8))
|
||||
gtk_tree_view_set_enable_tree_lines (GTK_TREE_VIEW (view), TRUE);
|
||||
#endif
|
||||
gtk_container_add (GTK_CONTAINER (win), view);
|
||||
|
||||
/* icon column */
|
||||
if (cv->use_icons)
|
||||
{
|
||||
renderer = gtk_cell_renderer_pixbuf_new ();
|
||||
if (prefs.gui_tweaks & 32)
|
||||
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
|
||||
-1, NULL, renderer,
|
||||
"pixbuf", COL_PIXBUF, NULL);
|
||||
}
|
||||
|
||||
/* main column */
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
if (prefs.gui_tweaks & 32)
|
||||
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
|
||||
gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
|
||||
-1, NULL, renderer,
|
||||
"text", COL_NAME, "attributes", COL_ATTR, NULL);
|
||||
|
||||
g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (view))),
|
||||
"changed", G_CALLBACK (cv_tree_sel_cb), cv);
|
||||
g_signal_connect (G_OBJECT (view), "button-press-event",
|
||||
G_CALLBACK (cv_tree_click_cb), cv);
|
||||
g_signal_connect (G_OBJECT (view), "row-activated",
|
||||
G_CALLBACK (cv_tree_activated_cb), NULL);
|
||||
|
||||
gtk_drag_dest_set (view, GTK_DEST_DEFAULT_ALL, dnd_dest_target, 1,
|
||||
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
|
||||
gtk_drag_source_set (view, GDK_BUTTON1_MASK, dnd_src_target, 1, GDK_ACTION_COPY);
|
||||
|
||||
#ifndef WIN32
|
||||
g_signal_connect (G_OBJECT (view), "drag_begin",
|
||||
G_CALLBACK (mg_drag_begin_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (view), "drag_drop",
|
||||
G_CALLBACK (mg_drag_drop_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (view), "drag_motion",
|
||||
G_CALLBACK (mg_drag_motion_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (view), "drag_end",
|
||||
G_CALLBACK (mg_drag_end_cb), NULL);
|
||||
#endif
|
||||
|
||||
((treeview *)cv)->tree = GTK_TREE_VIEW (view);
|
||||
((treeview *)cv)->scrollw = win;
|
||||
gtk_widget_show (view);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_postinit (chanview *cv)
|
||||
{
|
||||
gtk_tree_view_expand_all (((treeview *)cv)->tree);
|
||||
}
|
||||
|
||||
static void *
|
||||
cv_tree_add (chanview *cv, chan *ch, char *name, GtkTreeIter *parent)
|
||||
{
|
||||
GtkTreePath *path;
|
||||
|
||||
if (parent)
|
||||
{
|
||||
/* expand the parent node */
|
||||
path = gtk_tree_model_get_path (GTK_TREE_MODEL (cv->store), parent);
|
||||
if (path)
|
||||
{
|
||||
gtk_tree_view_expand_row (((treeview *)cv)->tree, path, FALSE);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_change_orientation (chanview *cv)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_focus (chan *ch)
|
||||
{
|
||||
GtkTreeView *tree = ((treeview *)ch->cv)->tree;
|
||||
GtkTreeModel *model = gtk_tree_view_get_model (tree);
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter parent;
|
||||
GdkRectangle cell_rect;
|
||||
GdkRectangle vis_rect;
|
||||
gint dest_y;
|
||||
|
||||
/* expand the parent node */
|
||||
if (gtk_tree_model_iter_parent (model, &parent, &ch->iter))
|
||||
{
|
||||
path = gtk_tree_model_get_path (model, &parent);
|
||||
if (path)
|
||||
{
|
||||
/*if (!gtk_tree_view_row_expanded (tree, path))
|
||||
{
|
||||
gtk_tree_path_free (path);
|
||||
return;
|
||||
}*/
|
||||
gtk_tree_view_expand_row (tree, path, FALSE);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
}
|
||||
|
||||
path = gtk_tree_model_get_path (model, &ch->iter);
|
||||
if (path)
|
||||
{
|
||||
/* This full section does what
|
||||
* gtk_tree_view_scroll_to_cell (tree, path, NULL, TRUE, 0.5, 0.5);
|
||||
* does, except it only scrolls the window if the provided cell is
|
||||
* not visible. Basic algorithm taken from gtktreeview.c */
|
||||
|
||||
/* obtain information to see if the cell is visible */
|
||||
gtk_tree_view_get_background_area (tree, path, NULL, &cell_rect);
|
||||
gtk_tree_view_get_visible_rect (tree, &vis_rect);
|
||||
|
||||
/* The cordinates aren't offset correctly */
|
||||
gtk_tree_view_widget_to_tree_coords( tree, cell_rect.x, cell_rect.y, NULL, &cell_rect.y );
|
||||
|
||||
/* only need to scroll if out of bounds */
|
||||
if (cell_rect.y < vis_rect.y ||
|
||||
cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
|
||||
{
|
||||
dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * 0.5);
|
||||
if (dest_y < 0)
|
||||
dest_y = 0;
|
||||
gtk_tree_view_scroll_to_point (tree, -1, dest_y);
|
||||
}
|
||||
/* theft done, now make it focused like */
|
||||
gtk_tree_view_set_cursor (tree, path, NULL, FALSE);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_move_focus (chanview *cv, gboolean relative, int num)
|
||||
{
|
||||
chan *ch;
|
||||
|
||||
if (relative)
|
||||
{
|
||||
num += cv_find_number_of_chan (cv, cv->focused);
|
||||
num %= cv->size;
|
||||
/* make it wrap around at both ends */
|
||||
if (num < 0)
|
||||
num = cv->size - 1;
|
||||
}
|
||||
|
||||
ch = cv_find_chan_by_number (cv, num);
|
||||
if (ch)
|
||||
cv_tree_focus (ch);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_remove (chan *ch)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
move_row (chan *ch, int delta, GtkTreeIter *parent)
|
||||
{
|
||||
GtkTreeStore *store = ch->cv->store;
|
||||
GtkTreeIter *src = &ch->iter;
|
||||
GtkTreeIter dest = ch->iter;
|
||||
GtkTreePath *dest_path;
|
||||
|
||||
if (delta < 0) /* down */
|
||||
{
|
||||
if (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &dest))
|
||||
gtk_tree_store_swap (store, src, &dest);
|
||||
else /* move to top */
|
||||
gtk_tree_store_move_after (store, src, NULL);
|
||||
|
||||
} else
|
||||
{
|
||||
dest_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &dest);
|
||||
if (gtk_tree_path_prev (dest_path))
|
||||
{
|
||||
gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &dest, dest_path);
|
||||
gtk_tree_store_swap (store, src, &dest);
|
||||
} else
|
||||
{ /* move to bottom */
|
||||
gtk_tree_store_move_before (store, src, NULL);
|
||||
}
|
||||
|
||||
gtk_tree_path_free (dest_path);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_move (chan *ch, int delta)
|
||||
{
|
||||
GtkTreeIter parent;
|
||||
|
||||
/* do nothing if this is a server row */
|
||||
if (gtk_tree_model_iter_parent (GTK_TREE_MODEL (ch->cv->store), &parent, &ch->iter))
|
||||
move_row (ch, delta, &parent);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_move_family (chan *ch, int delta)
|
||||
{
|
||||
move_row (ch, delta, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_cleanup (chanview *cv)
|
||||
{
|
||||
if (cv->box)
|
||||
/* kill the scrolled window */
|
||||
gtk_widget_destroy (((treeview *)cv)->scrollw);
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_set_color (chan *ch, PangoAttrList *list)
|
||||
{
|
||||
/* nothing to do, it's already set in the store */
|
||||
}
|
||||
|
||||
static void
|
||||
cv_tree_rename (chan *ch, char *name)
|
||||
{
|
||||
/* nothing to do, it's already renamed in the store */
|
||||
}
|
||||
|
||||
static chan *
|
||||
cv_tree_get_parent (chan *ch)
|
||||
{
|
||||
chan *parent_ch = NULL;
|
||||
GtkTreeIter parent;
|
||||
|
||||
if (gtk_tree_model_iter_parent (GTK_TREE_MODEL (ch->cv->store), &parent, &ch->iter))
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (ch->cv->store), &parent, COL_CHAN, &parent_ch, -1);
|
||||
}
|
||||
|
||||
return parent_ch;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cv_tree_is_collapsed (chan *ch)
|
||||
{
|
||||
chan *parent = cv_tree_get_parent (ch);
|
||||
GtkTreePath *path = NULL;
|
||||
gboolean ret;
|
||||
|
||||
if (parent == NULL)
|
||||
return FALSE;
|
||||
|
||||
path = gtk_tree_model_get_path (GTK_TREE_MODEL (parent->cv->store),
|
||||
&parent->iter);
|
||||
ret = !gtk_tree_view_row_expanded (((treeview *)parent->cv)->tree, path);
|
||||
gtk_tree_path_free (path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
643
src/fe-gtk/chanview.c
Normal file
643
src/fe-gtk/chanview.c
Normal file
@@ -0,0 +1,643 @@
|
||||
/* abstract channel view: tabs or tree or anything you like */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "chanview.h"
|
||||
#include "gtkutil.h"
|
||||
|
||||
|
||||
/* treeStore columns */
|
||||
|
||||
#define COL_NAME 0 /* (char *) */
|
||||
#define COL_CHAN 1 /* (chan *) */
|
||||
#define COL_ATTR 2 /* (PangoAttrList *) */
|
||||
#define COL_PIXBUF 3 /* (GdkPixbuf *) */
|
||||
|
||||
struct _chanview
|
||||
{
|
||||
/* impl scratch area */
|
||||
char implscratch[sizeof (void *) * 8];
|
||||
|
||||
GtkTreeStore *store;
|
||||
int size; /* number of channels in view */
|
||||
|
||||
GtkWidget *box; /* the box we destroy when changing implementations */
|
||||
GtkStyle *style; /* style used for tree */
|
||||
chan *focused; /* currently focused channel */
|
||||
int trunc_len;
|
||||
|
||||
/* callbacks */
|
||||
void (*cb_focus) (chanview *, chan *, int tag, void *userdata);
|
||||
void (*cb_xbutton) (chanview *, chan *, int tag, void *userdata);
|
||||
gboolean (*cb_contextmenu) (chanview *, chan *, int tag, void *userdata, GdkEventButton *);
|
||||
int (*cb_compare) (void *a, void *b);
|
||||
|
||||
/* impl */
|
||||
void (*func_init) (chanview *);
|
||||
void (*func_postinit) (chanview *);
|
||||
void *(*func_add) (chanview *, chan *, char *, GtkTreeIter *);
|
||||
void (*func_move_focus) (chanview *, gboolean, int);
|
||||
void (*func_change_orientation) (chanview *);
|
||||
void (*func_remove) (chan *);
|
||||
void (*func_move) (chan *, int delta);
|
||||
void (*func_move_family) (chan *, int delta);
|
||||
void (*func_focus) (chan *);
|
||||
void (*func_set_color) (chan *, PangoAttrList *);
|
||||
void (*func_rename) (chan *, char *);
|
||||
gboolean (*func_is_collapsed) (chan *);
|
||||
chan *(*func_get_parent) (chan *);
|
||||
void (*func_cleanup) (chanview *);
|
||||
|
||||
unsigned int sorted:1;
|
||||
unsigned int vertical:1;
|
||||
unsigned int use_icons:1;
|
||||
};
|
||||
|
||||
struct _chan
|
||||
{
|
||||
chanview *cv; /* our owner */
|
||||
GtkTreeIter iter;
|
||||
void *userdata; /* session * */
|
||||
void *family; /* server * or null */
|
||||
void *impl; /* togglebutton or null */
|
||||
GdkPixbuf *icon;
|
||||
short allow_closure; /* allow it to be closed when it still has children? */
|
||||
short tag;
|
||||
};
|
||||
|
||||
static chan *cv_find_chan_by_number (chanview *cv, int num);
|
||||
static int cv_find_number_of_chan (chanview *cv, chan *find_ch);
|
||||
|
||||
|
||||
/* ======= TABS ======= */
|
||||
|
||||
#include "chanview-tabs.c"
|
||||
|
||||
|
||||
/* ======= TREE ======= */
|
||||
|
||||
#include "chanview-tree.c"
|
||||
|
||||
|
||||
/* ==== ABSTRACT CHANVIEW ==== */
|
||||
|
||||
static char *
|
||||
truncate_tab_name (char *name, int max)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
if (max > 2 && g_utf8_strlen (name, -1) > max)
|
||||
{
|
||||
/* truncate long channel names */
|
||||
buf = malloc (strlen (name) + 4);
|
||||
strcpy (buf, name);
|
||||
g_utf8_offset_to_pointer (buf, max)[0] = 0;
|
||||
strcat (buf, "..");
|
||||
return buf;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/* iterate through a model, into 1 depth of children */
|
||||
|
||||
static void
|
||||
model_foreach_1 (GtkTreeModel *model, void (*func)(void *, GtkTreeIter *),
|
||||
void *userdata)
|
||||
{
|
||||
GtkTreeIter iter, inner;
|
||||
|
||||
if (gtk_tree_model_get_iter_first (model, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
func (userdata, &iter);
|
||||
if (gtk_tree_model_iter_children (model, &inner, &iter))
|
||||
{
|
||||
do
|
||||
func (userdata, &inner);
|
||||
while (gtk_tree_model_iter_next (model, &inner));
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chanview_pop_cb (chanview *cv, GtkTreeIter *iter)
|
||||
{
|
||||
chan *ch;
|
||||
char *name;
|
||||
PangoAttrList *attr;
|
||||
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), iter,
|
||||
COL_NAME, &name, COL_CHAN, &ch, COL_ATTR, &attr, -1);
|
||||
ch->impl = cv->func_add (cv, ch, name, NULL);
|
||||
if (attr)
|
||||
{
|
||||
cv->func_set_color (ch, attr);
|
||||
pango_attr_list_unref (attr);
|
||||
}
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
static void
|
||||
chanview_populate (chanview *cv)
|
||||
{
|
||||
model_foreach_1 (GTK_TREE_MODEL (cv->store), (void *)chanview_pop_cb, cv);
|
||||
}
|
||||
|
||||
void
|
||||
chanview_set_impl (chanview *cv, int type)
|
||||
{
|
||||
/* cleanup the old one */
|
||||
if (cv->func_cleanup)
|
||||
cv->func_cleanup (cv);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 0:
|
||||
cv->func_init = cv_tabs_init;
|
||||
cv->func_postinit = cv_tabs_postinit;
|
||||
cv->func_add = cv_tabs_add;
|
||||
cv->func_move_focus = cv_tabs_move_focus;
|
||||
cv->func_change_orientation = cv_tabs_change_orientation;
|
||||
cv->func_remove = cv_tabs_remove;
|
||||
cv->func_move = cv_tabs_move;
|
||||
cv->func_move_family = cv_tabs_move_family;
|
||||
cv->func_focus = cv_tabs_focus;
|
||||
cv->func_set_color = cv_tabs_set_color;
|
||||
cv->func_rename = cv_tabs_rename;
|
||||
cv->func_is_collapsed = cv_tabs_is_collapsed;
|
||||
cv->func_get_parent = cv_tabs_get_parent;
|
||||
cv->func_cleanup = cv_tabs_cleanup;
|
||||
break;
|
||||
|
||||
default:
|
||||
cv->func_init = cv_tree_init;
|
||||
cv->func_postinit = cv_tree_postinit;
|
||||
cv->func_add = cv_tree_add;
|
||||
cv->func_move_focus = cv_tree_move_focus;
|
||||
cv->func_change_orientation = cv_tree_change_orientation;
|
||||
cv->func_remove = cv_tree_remove;
|
||||
cv->func_move = cv_tree_move;
|
||||
cv->func_move_family = cv_tree_move_family;
|
||||
cv->func_focus = cv_tree_focus;
|
||||
cv->func_set_color = cv_tree_set_color;
|
||||
cv->func_rename = cv_tree_rename;
|
||||
cv->func_is_collapsed = cv_tree_is_collapsed;
|
||||
cv->func_get_parent = cv_tree_get_parent;
|
||||
cv->func_cleanup = cv_tree_cleanup;
|
||||
break;
|
||||
}
|
||||
|
||||
/* now rebuild a new tabbar or tree */
|
||||
cv->func_init (cv);
|
||||
|
||||
chanview_populate (cv);
|
||||
|
||||
cv->func_postinit (cv);
|
||||
|
||||
/* force re-focus */
|
||||
if (cv->focused)
|
||||
cv->func_focus (cv->focused);
|
||||
}
|
||||
|
||||
static void
|
||||
chanview_free_ch (chanview *cv, GtkTreeIter *iter)
|
||||
{
|
||||
chan *ch;
|
||||
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), iter, COL_CHAN, &ch, -1);
|
||||
free (ch);
|
||||
}
|
||||
|
||||
static void
|
||||
chanview_destroy_store (chanview *cv) /* free every (chan *) in the store */
|
||||
{
|
||||
model_foreach_1 (GTK_TREE_MODEL (cv->store), (void *)chanview_free_ch, cv);
|
||||
g_object_unref (cv->store);
|
||||
}
|
||||
|
||||
static void
|
||||
chanview_destroy (chanview *cv)
|
||||
{
|
||||
if (cv->func_cleanup)
|
||||
cv->func_cleanup (cv);
|
||||
|
||||
if (cv->box)
|
||||
gtk_widget_destroy (cv->box);
|
||||
|
||||
chanview_destroy_store (cv);
|
||||
free (cv);
|
||||
}
|
||||
|
||||
static void
|
||||
chanview_box_destroy_cb (GtkWidget *box, chanview *cv)
|
||||
{
|
||||
cv->box = NULL;
|
||||
chanview_destroy (cv);
|
||||
}
|
||||
|
||||
chanview *
|
||||
chanview_new (int type, int trunc_len, gboolean sort, gboolean use_icons,
|
||||
GtkStyle *style)
|
||||
{
|
||||
chanview *cv;
|
||||
|
||||
cv = calloc (1, sizeof (chanview));
|
||||
cv->store = gtk_tree_store_new (4, G_TYPE_STRING, G_TYPE_POINTER,
|
||||
PANGO_TYPE_ATTR_LIST, GDK_TYPE_PIXBUF);
|
||||
cv->style = style;
|
||||
cv->box = gtk_hbox_new (0, 0);
|
||||
cv->trunc_len = trunc_len;
|
||||
cv->sorted = sort;
|
||||
cv->use_icons = use_icons;
|
||||
gtk_widget_show (cv->box);
|
||||
chanview_set_impl (cv, type);
|
||||
|
||||
g_signal_connect (G_OBJECT (cv->box), "destroy",
|
||||
G_CALLBACK (chanview_box_destroy_cb), cv);
|
||||
|
||||
return cv;
|
||||
}
|
||||
|
||||
/* too lazy for signals */
|
||||
|
||||
void
|
||||
chanview_set_callbacks (chanview *cv,
|
||||
void (*cb_focus) (chanview *, chan *, int tag, void *userdata),
|
||||
void (*cb_xbutton) (chanview *, chan *, int tag, void *userdata),
|
||||
gboolean (*cb_contextmenu) (chanview *, chan *, int tag, void *userdata, GdkEventButton *),
|
||||
int (*cb_compare) (void *a, void *b))
|
||||
{
|
||||
cv->cb_focus = cb_focus;
|
||||
cv->cb_xbutton = cb_xbutton;
|
||||
cv->cb_contextmenu = cb_contextmenu;
|
||||
cv->cb_compare = cb_compare;
|
||||
}
|
||||
|
||||
/* find a place to insert this new entry, based on the compare function */
|
||||
|
||||
static void
|
||||
chanview_insert_sorted (chanview *cv, GtkTreeIter *add_iter, GtkTreeIter *parent, void *ud)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
chan *ch;
|
||||
|
||||
if (cv->sorted && gtk_tree_model_iter_children (GTK_TREE_MODEL (cv->store), &iter, parent))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &iter, COL_CHAN, &ch, -1);
|
||||
if (ch->tag == 0 && cv->cb_compare (ch->userdata, ud) > 0)
|
||||
{
|
||||
gtk_tree_store_insert_before (cv->store, add_iter, parent, &iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &iter));
|
||||
}
|
||||
|
||||
gtk_tree_store_append (cv->store, add_iter, parent);
|
||||
}
|
||||
|
||||
/* find a parent node with the same "family" pointer (i.e. the Server tab) */
|
||||
|
||||
static int
|
||||
chanview_find_parent (chanview *cv, void *family, GtkTreeIter *search_iter, chan *avoid)
|
||||
{
|
||||
chan *search_ch;
|
||||
|
||||
/* find this new row's parent, if any */
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (cv->store), search_iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), search_iter,
|
||||
COL_CHAN, &search_ch, -1);
|
||||
if (family == search_ch->family && search_ch != avoid /*&&
|
||||
gtk_tree_store_iter_depth (cv->store, search_iter) == 0*/)
|
||||
return TRUE;
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), search_iter));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static chan *
|
||||
chanview_add_real (chanview *cv, char *name, void *family, void *userdata,
|
||||
gboolean allow_closure, int tag, GdkPixbuf *icon,
|
||||
chan *ch, chan *avoid)
|
||||
{
|
||||
GtkTreeIter parent_iter;
|
||||
GtkTreeIter iter;
|
||||
gboolean has_parent = FALSE;
|
||||
|
||||
if (chanview_find_parent (cv, family, &parent_iter, avoid))
|
||||
{
|
||||
chanview_insert_sorted (cv, &iter, &parent_iter, userdata);
|
||||
has_parent = TRUE;
|
||||
} else
|
||||
{
|
||||
gtk_tree_store_append (cv->store, &iter, NULL);
|
||||
}
|
||||
|
||||
if (!ch)
|
||||
{
|
||||
ch = calloc (1, sizeof (chan));
|
||||
ch->userdata = userdata;
|
||||
ch->family = family;
|
||||
ch->cv = cv;
|
||||
ch->allow_closure = allow_closure;
|
||||
ch->tag = tag;
|
||||
ch->icon = icon;
|
||||
}
|
||||
memcpy (&(ch->iter), &iter, sizeof (iter));
|
||||
|
||||
gtk_tree_store_set (cv->store, &iter, COL_NAME, name, COL_CHAN, ch,
|
||||
COL_PIXBUF, icon, -1);
|
||||
|
||||
cv->size++;
|
||||
if (!has_parent)
|
||||
ch->impl = cv->func_add (cv, ch, name, NULL);
|
||||
else
|
||||
ch->impl = cv->func_add (cv, ch, name, &parent_iter);
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
chan *
|
||||
chanview_add (chanview *cv, char *name, void *family, void *userdata, gboolean allow_closure, int tag, GdkPixbuf *icon)
|
||||
{
|
||||
char *new_name;
|
||||
chan *ret;
|
||||
|
||||
new_name = truncate_tab_name (name, cv->trunc_len);
|
||||
|
||||
ret = chanview_add_real (cv, new_name, family, userdata, allow_closure, tag, icon, NULL, NULL);
|
||||
|
||||
if (new_name != name)
|
||||
free (new_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
chanview_get_size (chanview *cv)
|
||||
{
|
||||
return cv->size;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
chanview_get_box (chanview *cv)
|
||||
{
|
||||
return cv->box;
|
||||
}
|
||||
|
||||
void
|
||||
chanview_move_focus (chanview *cv, gboolean relative, int num)
|
||||
{
|
||||
cv->func_move_focus (cv, relative, num);
|
||||
}
|
||||
|
||||
GtkOrientation
|
||||
chanview_get_orientation (chanview *cv)
|
||||
{
|
||||
return (cv->vertical ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL);
|
||||
}
|
||||
|
||||
void
|
||||
chanview_set_orientation (chanview *cv, gboolean vertical)
|
||||
{
|
||||
if (vertical != cv->vertical)
|
||||
{
|
||||
cv->vertical = vertical;
|
||||
cv->func_change_orientation (cv);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
chan_get_tag (chan *ch)
|
||||
{
|
||||
return ch->tag;
|
||||
}
|
||||
|
||||
void *
|
||||
chan_get_userdata (chan *ch)
|
||||
{
|
||||
return ch->userdata;
|
||||
}
|
||||
|
||||
void
|
||||
chan_focus (chan *ch)
|
||||
{
|
||||
if (ch->cv->focused == ch)
|
||||
return;
|
||||
|
||||
ch->cv->func_focus (ch);
|
||||
}
|
||||
|
||||
void
|
||||
chan_move (chan *ch, int delta)
|
||||
{
|
||||
ch->cv->func_move (ch, delta);
|
||||
}
|
||||
|
||||
void
|
||||
chan_move_family (chan *ch, int delta)
|
||||
{
|
||||
ch->cv->func_move_family (ch, delta);
|
||||
}
|
||||
|
||||
void
|
||||
chan_set_color (chan *ch, PangoAttrList *list)
|
||||
{
|
||||
gtk_tree_store_set (ch->cv->store, &ch->iter, COL_ATTR, list, -1);
|
||||
ch->cv->func_set_color (ch, list);
|
||||
}
|
||||
|
||||
void
|
||||
chan_rename (chan *ch, char *name, int trunc_len)
|
||||
{
|
||||
char *new_name;
|
||||
|
||||
new_name = truncate_tab_name (name, trunc_len);
|
||||
|
||||
gtk_tree_store_set (ch->cv->store, &ch->iter, COL_NAME, new_name, -1);
|
||||
ch->cv->func_rename (ch, new_name);
|
||||
ch->cv->trunc_len = trunc_len;
|
||||
|
||||
if (new_name != name)
|
||||
free (new_name);
|
||||
}
|
||||
|
||||
/* this thing is overly complicated */
|
||||
|
||||
static int
|
||||
cv_find_number_of_chan (chanview *cv, chan *find_ch)
|
||||
{
|
||||
GtkTreeIter iter, inner;
|
||||
chan *ch;
|
||||
int i = 0;
|
||||
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (cv->store), &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &iter, COL_CHAN, &ch, -1);
|
||||
if (ch == find_ch)
|
||||
return i;
|
||||
i++;
|
||||
|
||||
if (gtk_tree_model_iter_children (GTK_TREE_MODEL (cv->store), &inner, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &inner, COL_CHAN, &ch, -1);
|
||||
if (ch == find_ch)
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &inner));
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &iter));
|
||||
}
|
||||
|
||||
return 0; /* WARNING */
|
||||
}
|
||||
|
||||
/* this thing is overly complicated too */
|
||||
|
||||
static chan *
|
||||
cv_find_chan_by_number (chanview *cv, int num)
|
||||
{
|
||||
GtkTreeIter iter, inner;
|
||||
chan *ch;
|
||||
int i = 0;
|
||||
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (cv->store), &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
if (i == num)
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &iter, COL_CHAN, &ch, -1);
|
||||
return ch;
|
||||
}
|
||||
i++;
|
||||
|
||||
if (gtk_tree_model_iter_children (GTK_TREE_MODEL (cv->store), &inner, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
if (i == num)
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &inner, COL_CHAN, &ch, -1);
|
||||
return ch;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &inner));
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &iter));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
chan_emancipate_children (chan *ch)
|
||||
{
|
||||
char *name;
|
||||
chan *childch;
|
||||
GtkTreeIter childiter;
|
||||
PangoAttrList *attr;
|
||||
|
||||
while (gtk_tree_model_iter_children (GTK_TREE_MODEL (ch->cv->store), &childiter, &ch->iter))
|
||||
{
|
||||
/* remove and re-add all the children, but avoid using "ch" as parent */
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (ch->cv->store), &childiter,
|
||||
COL_NAME, &name, COL_CHAN, &childch, COL_ATTR, &attr, -1);
|
||||
ch->cv->func_remove (childch);
|
||||
gtk_tree_store_remove (ch->cv->store, &childiter);
|
||||
ch->cv->size--;
|
||||
chanview_add_real (childch->cv, name, childch->family, childch->userdata, childch->allow_closure, childch->tag, childch->icon, childch, ch);
|
||||
if (attr)
|
||||
{
|
||||
childch->cv->func_set_color (childch, attr);
|
||||
pango_attr_list_unref (attr);
|
||||
}
|
||||
g_free (name);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
chan_remove (chan *ch, gboolean force)
|
||||
{
|
||||
chan *new_ch;
|
||||
int i, num;
|
||||
extern int xchat_is_quitting;
|
||||
|
||||
if (xchat_is_quitting) /* avoid lots of looping on exit */
|
||||
return TRUE;
|
||||
|
||||
/* is this ch allowed to be closed while still having children? */
|
||||
if (!force &&
|
||||
gtk_tree_model_iter_has_child (GTK_TREE_MODEL (ch->cv->store), &ch->iter) &&
|
||||
!ch->allow_closure)
|
||||
return FALSE;
|
||||
|
||||
chan_emancipate_children (ch);
|
||||
ch->cv->func_remove (ch);
|
||||
|
||||
/* is it the focused one? */
|
||||
if (ch->cv->focused == ch)
|
||||
{
|
||||
ch->cv->focused = NULL;
|
||||
|
||||
/* try to move the focus to some other valid channel */
|
||||
num = cv_find_number_of_chan (ch->cv, ch);
|
||||
/* move to the one left of the closing tab */
|
||||
new_ch = cv_find_chan_by_number (ch->cv, num - 1);
|
||||
if (new_ch && new_ch != ch)
|
||||
{
|
||||
chan_focus (new_ch); /* this'll will set ch->cv->focused for us too */
|
||||
} else
|
||||
{
|
||||
/* if it fails, try focus from tab 0 and up */
|
||||
for (i = 0; i < ch->cv->size; i++)
|
||||
{
|
||||
new_ch = cv_find_chan_by_number (ch->cv, i);
|
||||
if (new_ch && new_ch != ch)
|
||||
{
|
||||
chan_focus (new_ch); /* this'll will set ch->cv->focused for us too */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ch->cv->size--;
|
||||
gtk_tree_store_remove (ch->cv->store, &ch->iter);
|
||||
free (ch);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
chan_is_collapsed (chan *ch)
|
||||
{
|
||||
return ch->cv->func_is_collapsed (ch);
|
||||
}
|
||||
|
||||
chan *
|
||||
chan_get_parent (chan *ch)
|
||||
{
|
||||
return ch->cv->func_get_parent (ch);
|
||||
}
|
||||
31
src/fe-gtk/chanview.h
Normal file
31
src/fe-gtk/chanview.h
Normal file
@@ -0,0 +1,31 @@
|
||||
typedef struct _chanview chanview;
|
||||
typedef struct _chan chan;
|
||||
|
||||
chanview *chanview_new (int type, int trunc_len, gboolean sort, gboolean use_icons, GtkStyle *style);
|
||||
void chanview_set_callbacks (chanview *cv,
|
||||
void (*cb_focus) (chanview *, chan *, int tag, void *userdata),
|
||||
void (*cb_xbutton) (chanview *, chan *, int tag, void *userdata),
|
||||
gboolean (*cb_contextmenu) (chanview *, chan *, int tag, void *userdata, GdkEventButton *),
|
||||
int (*cb_compare) (void *a, void *b));
|
||||
void chanview_set_impl (chanview *cv, int type);
|
||||
chan *chanview_add (chanview *cv, char *name, void *family, void *userdata, gboolean allow_closure, int tag, GdkPixbuf *icon);
|
||||
int chanview_get_size (chanview *cv);
|
||||
GtkWidget *chanview_get_box (chanview *cv);
|
||||
void chanview_move_focus (chanview *cv, gboolean relative, int num);
|
||||
GtkOrientation chanview_get_orientation (chanview *cv);
|
||||
void chanview_set_orientation (chanview *cv, gboolean vertical);
|
||||
|
||||
int chan_get_tag (chan *ch);
|
||||
void *chan_get_userdata (chan *ch);
|
||||
void chan_focus (chan *ch);
|
||||
void chan_move (chan *ch, int delta);
|
||||
void chan_move_family (chan *ch, int delta);
|
||||
void chan_set_color (chan *ch, PangoAttrList *list);
|
||||
void chan_rename (chan *ch, char *new_name, int trunc_len);
|
||||
gboolean chan_remove (chan *ch, gboolean force);
|
||||
gboolean chan_is_collapsed (chan *ch);
|
||||
chan * chan_get_parent (chan *ch);
|
||||
|
||||
#define FOCUS_NEW_ALL 1
|
||||
#define FOCUS_NEW_ONLY_ASKED 2
|
||||
#define FOCUS_NEW_NONE 0
|
||||
753
src/fe-gtk/custom-list.c
Normal file
753
src/fe-gtk/custom-list.c
Normal file
@@ -0,0 +1,753 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "custom-list.h"
|
||||
|
||||
/* indent -i3 -ci3 -ut -ts3 -bli0 -c0 custom-list.c */
|
||||
|
||||
/* boring declarations of local functions */
|
||||
|
||||
static void custom_list_init (CustomList * pkg_tree);
|
||||
|
||||
static void custom_list_class_init (CustomListClass * klass);
|
||||
|
||||
static void custom_list_tree_model_init (GtkTreeModelIface * iface);
|
||||
|
||||
static void custom_list_finalize (GObject * object);
|
||||
|
||||
static GtkTreeModelFlags custom_list_get_flags (GtkTreeModel * tree_model);
|
||||
|
||||
static gint custom_list_get_n_columns (GtkTreeModel * tree_model);
|
||||
|
||||
static GType custom_list_get_column_type (GtkTreeModel * tree_model,
|
||||
gint index);
|
||||
|
||||
static gboolean custom_list_get_iter (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter, GtkTreePath * path);
|
||||
|
||||
static GtkTreePath *custom_list_get_path (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter);
|
||||
|
||||
static void custom_list_get_value (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter,
|
||||
gint column, GValue * value);
|
||||
|
||||
static gboolean custom_list_iter_next (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter);
|
||||
|
||||
static gboolean custom_list_iter_children (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter,
|
||||
GtkTreeIter * parent);
|
||||
|
||||
static gboolean custom_list_iter_has_child (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter);
|
||||
|
||||
static gint custom_list_iter_n_children (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter);
|
||||
|
||||
static gboolean custom_list_iter_nth_child (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter,
|
||||
GtkTreeIter * parent, gint n);
|
||||
|
||||
static gboolean custom_list_iter_parent (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter,
|
||||
GtkTreeIter * child);
|
||||
|
||||
/* -- GtkTreeSortable interface functions -- */
|
||||
|
||||
static gboolean custom_list_sortable_get_sort_column_id (GtkTreeSortable *
|
||||
sortable,
|
||||
gint * sort_col_id,
|
||||
GtkSortType * order);
|
||||
|
||||
static void custom_list_sortable_set_sort_column_id (GtkTreeSortable *
|
||||
sortable,
|
||||
gint sort_col_id,
|
||||
GtkSortType order);
|
||||
|
||||
static void custom_list_sortable_set_sort_func (GtkTreeSortable * sortable,
|
||||
gint sort_col_id,
|
||||
GtkTreeIterCompareFunc
|
||||
sort_func, gpointer user_data,
|
||||
GtkDestroyNotify
|
||||
destroy_func);
|
||||
|
||||
static void custom_list_sortable_set_default_sort_func (GtkTreeSortable *
|
||||
sortable,
|
||||
GtkTreeIterCompareFunc
|
||||
sort_func,
|
||||
gpointer user_data,
|
||||
GtkDestroyNotify
|
||||
destroy_func);
|
||||
|
||||
static gboolean custom_list_sortable_has_default_sort_func (GtkTreeSortable *
|
||||
sortable);
|
||||
|
||||
|
||||
|
||||
static GObjectClass *parent_class = NULL; /* GObject stuff - nothing to worry about */
|
||||
|
||||
|
||||
static void
|
||||
custom_list_sortable_init (GtkTreeSortableIface * iface)
|
||||
{
|
||||
iface->get_sort_column_id = custom_list_sortable_get_sort_column_id;
|
||||
iface->set_sort_column_id = custom_list_sortable_set_sort_column_id;
|
||||
iface->set_sort_func = custom_list_sortable_set_sort_func; /* NOT SUPPORTED */
|
||||
iface->set_default_sort_func = custom_list_sortable_set_default_sort_func; /* NOT SUPPORTED */
|
||||
iface->has_default_sort_func = custom_list_sortable_has_default_sort_func; /* NOT SUPPORTED */
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_get_type: here we register our new type and its interfaces
|
||||
* with the type system. If you want to implement
|
||||
* additional interfaces like GtkTreeSortable, you
|
||||
* will need to do it here.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static GType
|
||||
custom_list_get_type (void)
|
||||
{
|
||||
static GType custom_list_type = 0;
|
||||
|
||||
if (custom_list_type)
|
||||
return custom_list_type;
|
||||
|
||||
/* Some boilerplate type registration stuff */
|
||||
if (1)
|
||||
{
|
||||
static const GTypeInfo custom_list_info = {
|
||||
sizeof (CustomListClass),
|
||||
NULL, /* base_init */
|
||||
NULL, /* base_finalize */
|
||||
(GClassInitFunc) custom_list_class_init,
|
||||
NULL, /* class finalize */
|
||||
NULL, /* class_data */
|
||||
sizeof (CustomList),
|
||||
0, /* n_preallocs */
|
||||
(GInstanceInitFunc) custom_list_init
|
||||
};
|
||||
|
||||
custom_list_type =
|
||||
g_type_register_static (G_TYPE_OBJECT, "CustomList",
|
||||
&custom_list_info, (GTypeFlags) 0);
|
||||
}
|
||||
|
||||
/* Here we register our GtkTreeModel interface with the type system */
|
||||
if (1)
|
||||
{
|
||||
static const GInterfaceInfo tree_model_info = {
|
||||
(GInterfaceInitFunc) custom_list_tree_model_init,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
g_type_add_interface_static (custom_list_type, GTK_TYPE_TREE_MODEL,
|
||||
&tree_model_info);
|
||||
}
|
||||
|
||||
/* Add GtkTreeSortable interface */
|
||||
if (1)
|
||||
{
|
||||
static const GInterfaceInfo tree_sortable_info = {
|
||||
(GInterfaceInitFunc) custom_list_sortable_init,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
g_type_add_interface_static (custom_list_type,
|
||||
GTK_TYPE_TREE_SORTABLE,
|
||||
&tree_sortable_info);
|
||||
}
|
||||
|
||||
return custom_list_type;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_class_init: more boilerplate GObject/GType stuff.
|
||||
* Init callback for the type system,
|
||||
* called once when our new class is created.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static void
|
||||
custom_list_class_init (CustomListClass * klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
|
||||
parent_class = (GObjectClass *) g_type_class_peek_parent (klass);
|
||||
object_class = (GObjectClass *) klass;
|
||||
|
||||
object_class->finalize = custom_list_finalize;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_tree_model_init: init callback for the interface registration
|
||||
* in custom_list_get_type. Here we override
|
||||
* the GtkTreeModel interface functions that
|
||||
* we implement.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static void
|
||||
custom_list_tree_model_init (GtkTreeModelIface * iface)
|
||||
{
|
||||
iface->get_flags = custom_list_get_flags;
|
||||
iface->get_n_columns = custom_list_get_n_columns;
|
||||
iface->get_column_type = custom_list_get_column_type;
|
||||
iface->get_iter = custom_list_get_iter;
|
||||
iface->get_path = custom_list_get_path;
|
||||
iface->get_value = custom_list_get_value;
|
||||
iface->iter_next = custom_list_iter_next;
|
||||
iface->iter_children = custom_list_iter_children;
|
||||
iface->iter_has_child = custom_list_iter_has_child;
|
||||
iface->iter_n_children = custom_list_iter_n_children;
|
||||
iface->iter_nth_child = custom_list_iter_nth_child;
|
||||
iface->iter_parent = custom_list_iter_parent;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_init: this is called everytime a new custom list object
|
||||
* instance is created (we do that in custom_list_new).
|
||||
* Initialise the list structure's fields here.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static void
|
||||
custom_list_init (CustomList * custom_list)
|
||||
{
|
||||
custom_list->n_columns = CUSTOM_LIST_N_COLUMNS;
|
||||
|
||||
custom_list->column_types[0] = G_TYPE_STRING; /* CUSTOM_LIST_COL_NAME */
|
||||
custom_list->column_types[1] = G_TYPE_UINT; /* CUSTOM_LIST_COL_USERS */
|
||||
custom_list->column_types[2] = G_TYPE_STRING; /* CUSTOM_LIST_COL_TOPIC */
|
||||
|
||||
custom_list->num_rows = 0;
|
||||
custom_list->num_alloc = 0;
|
||||
custom_list->rows = NULL;
|
||||
|
||||
custom_list->sort_id = SORT_ID_CHANNEL;
|
||||
custom_list->sort_order = GTK_SORT_ASCENDING;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_finalize: this is called just before a custom list is
|
||||
* destroyed. Free dynamically allocated memory here.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static void
|
||||
custom_list_finalize (GObject * object)
|
||||
{
|
||||
custom_list_clear (CUSTOM_LIST (object));
|
||||
|
||||
/* must chain up - finalize parent */
|
||||
(*parent_class->finalize) (object);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_get_flags: tells the rest of the world whether our tree model
|
||||
* has any special characteristics. In our case,
|
||||
* we have a list model (instead of a tree), and each
|
||||
* tree iter is valid as long as the row in question
|
||||
* exists, as it only contains a pointer to our struct.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static GtkTreeModelFlags
|
||||
custom_list_get_flags (GtkTreeModel * tree_model)
|
||||
{
|
||||
return (GTK_TREE_MODEL_LIST_ONLY /*| GTK_TREE_MODEL_ITERS_PERSIST */ );
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_get_n_columns: tells the rest of the world how many data
|
||||
* columns we export via the tree model interface
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gint
|
||||
custom_list_get_n_columns (GtkTreeModel * tree_model)
|
||||
{
|
||||
return 3;/*CUSTOM_LIST (tree_model)->n_columns;*/
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_get_column_type: tells the rest of the world which type of
|
||||
* data an exported model column contains
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static GType
|
||||
custom_list_get_column_type (GtkTreeModel * tree_model, gint index)
|
||||
{
|
||||
return CUSTOM_LIST (tree_model)->column_types[index];
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_get_iter: converts a tree path (physical position) into a
|
||||
* tree iter structure (the content of the iter
|
||||
* fields will only be used internally by our model).
|
||||
* We simply store a pointer to our chanlistrow
|
||||
* structure that represents that row in the tree iter.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
custom_list_get_iter (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter, GtkTreePath * path)
|
||||
{
|
||||
CustomList *custom_list = CUSTOM_LIST (tree_model);
|
||||
chanlistrow *record;
|
||||
gint n;
|
||||
|
||||
n = gtk_tree_path_get_indices (path)[0];
|
||||
if (n >= custom_list->num_rows || n < 0)
|
||||
return FALSE;
|
||||
|
||||
record = custom_list->rows[n];
|
||||
|
||||
/* We simply store a pointer to our custom record in the iter */
|
||||
iter->user_data = record;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_get_path: converts a tree iter into a tree path (ie. the
|
||||
* physical position of that row in the list).
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static GtkTreePath *
|
||||
custom_list_get_path (GtkTreeModel * tree_model, GtkTreeIter * iter)
|
||||
{
|
||||
GtkTreePath *path;
|
||||
chanlistrow *record;
|
||||
|
||||
record = (chanlistrow *) iter->user_data;
|
||||
|
||||
path = gtk_tree_path_new ();
|
||||
gtk_tree_path_append_index (path, record->pos);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_get_value: Returns a row's exported data columns
|
||||
* (_get_value is what gtk_tree_model_get uses)
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static void
|
||||
custom_list_get_value (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter, gint column, GValue * value)
|
||||
{
|
||||
chanlistrow *record;
|
||||
CustomList *custom_list = CUSTOM_LIST (tree_model);
|
||||
|
||||
if (custom_list->num_rows == 0)
|
||||
return;
|
||||
|
||||
g_value_init (value, custom_list->column_types[column]);
|
||||
|
||||
record = (chanlistrow *) iter->user_data;
|
||||
|
||||
switch (column)
|
||||
{
|
||||
case CUSTOM_LIST_COL_NAME:
|
||||
g_value_set_static_string (value, GET_CHAN (record));
|
||||
break;
|
||||
|
||||
case CUSTOM_LIST_COL_USERS:
|
||||
g_value_set_uint (value, record->users);
|
||||
break;
|
||||
|
||||
case CUSTOM_LIST_COL_TOPIC:
|
||||
g_value_set_static_string (value, record->topic);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_iter_next: Takes an iter structure and sets it to point
|
||||
* to the next row.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
custom_list_iter_next (GtkTreeModel * tree_model, GtkTreeIter * iter)
|
||||
{
|
||||
chanlistrow *record, *nextrecord;
|
||||
CustomList *custom_list = CUSTOM_LIST (tree_model);
|
||||
|
||||
record = (chanlistrow *) iter->user_data;
|
||||
|
||||
/* Is this the last record in the list? */
|
||||
if ((record->pos + 1) >= custom_list->num_rows)
|
||||
return FALSE;
|
||||
|
||||
nextrecord = custom_list->rows[(record->pos + 1)];
|
||||
|
||||
g_assert (nextrecord != NULL);
|
||||
g_assert (nextrecord->pos == (record->pos + 1));
|
||||
|
||||
iter->user_data = nextrecord;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_iter_children: Returns TRUE or FALSE depending on whether
|
||||
* the row specified by 'parent' has any children.
|
||||
* If it has children, then 'iter' is set to
|
||||
* point to the first child. Special case: if
|
||||
* 'parent' is NULL, then the first top-level
|
||||
* row should be returned if it exists.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
custom_list_iter_children (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter, GtkTreeIter * parent)
|
||||
{
|
||||
CustomList *custom_list = CUSTOM_LIST (tree_model);
|
||||
|
||||
/* this is a list, nodes have no children */
|
||||
if (parent)
|
||||
return FALSE;
|
||||
|
||||
/* parent == NULL is a special case; we need to return the first top-level row */
|
||||
/* No rows => no first row */
|
||||
if (custom_list->num_rows == 0)
|
||||
return FALSE;
|
||||
|
||||
/* Set iter to first item in list */
|
||||
iter->user_data = custom_list->rows[0];
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_iter_has_child: Returns TRUE or FALSE depending on whether
|
||||
* the row specified by 'iter' has any children.
|
||||
* We only have a list and thus no children.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
custom_list_iter_has_child (GtkTreeModel * tree_model, GtkTreeIter * iter)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_iter_n_children: Returns the number of children the row
|
||||
* specified by 'iter' has. This is usually 0,
|
||||
* as we only have a list and thus do not have
|
||||
* any children to any rows. A special case is
|
||||
* when 'iter' is NULL, in which case we need
|
||||
* to return the number of top-level nodes,
|
||||
* ie. the number of rows in our list.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gint
|
||||
custom_list_iter_n_children (GtkTreeModel * tree_model, GtkTreeIter * iter)
|
||||
{
|
||||
CustomList *custom_list = CUSTOM_LIST (tree_model);
|
||||
|
||||
/* special case: if iter == NULL, return number of top-level rows */
|
||||
if (!iter)
|
||||
return custom_list->num_rows;
|
||||
|
||||
return 0; /* otherwise, this is easy again for a list */
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_iter_nth_child: If the row specified by 'parent' has any
|
||||
* children, set 'iter' to the n-th child and
|
||||
* return TRUE if it exists, otherwise FALSE.
|
||||
* A special case is when 'parent' is NULL, in
|
||||
* which case we need to set 'iter' to the n-th
|
||||
* row if it exists.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
custom_list_iter_nth_child (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter, GtkTreeIter * parent, gint n)
|
||||
{
|
||||
CustomList *custom_list = CUSTOM_LIST (tree_model);
|
||||
|
||||
/* a list has only top-level rows */
|
||||
if (parent)
|
||||
return FALSE;
|
||||
|
||||
/* special case: if parent == NULL, set iter to n-th top-level row */
|
||||
if (n >= custom_list->num_rows)
|
||||
return FALSE;
|
||||
|
||||
iter->user_data = custom_list->rows[n];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_iter_parent: Point 'iter' to the parent node of 'child'. As
|
||||
* we have a list and thus no children and no
|
||||
* parents of children, we can just return FALSE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
custom_list_iter_parent (GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter, GtkTreeIter * child)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
custom_list_sortable_get_sort_column_id (GtkTreeSortable * sortable,
|
||||
gint * sort_col_id,
|
||||
GtkSortType * order)
|
||||
{
|
||||
CustomList *custom_list = CUSTOM_LIST (sortable);
|
||||
|
||||
if (sort_col_id)
|
||||
*sort_col_id = custom_list->sort_id;
|
||||
|
||||
if (order)
|
||||
*order = custom_list->sort_order;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
custom_list_sortable_set_sort_column_id (GtkTreeSortable * sortable,
|
||||
gint sort_col_id, GtkSortType order)
|
||||
{
|
||||
CustomList *custom_list = CUSTOM_LIST (sortable);
|
||||
|
||||
if (custom_list->sort_id == sort_col_id
|
||||
&& custom_list->sort_order == order)
|
||||
return;
|
||||
|
||||
custom_list->sort_id = sort_col_id;
|
||||
custom_list->sort_order = order;
|
||||
|
||||
custom_list_resort (custom_list);
|
||||
|
||||
/* emit "sort-column-changed" signal to tell any tree views
|
||||
* that the sort column has changed (so the little arrow
|
||||
* in the column header of the sort column is drawn
|
||||
* in the right column) */
|
||||
|
||||
gtk_tree_sortable_sort_column_changed (sortable);
|
||||
}
|
||||
|
||||
static void
|
||||
custom_list_sortable_set_sort_func (GtkTreeSortable * sortable,
|
||||
gint sort_col_id,
|
||||
GtkTreeIterCompareFunc sort_func,
|
||||
gpointer user_data,
|
||||
GtkDestroyNotify destroy_func)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
custom_list_sortable_set_default_sort_func (GtkTreeSortable * sortable,
|
||||
GtkTreeIterCompareFunc sort_func,
|
||||
gpointer user_data,
|
||||
GtkDestroyNotify destroy_func)
|
||||
{
|
||||
}
|
||||
|
||||
static gboolean
|
||||
custom_list_sortable_has_default_sort_func (GtkTreeSortable * sortable)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* fast as possible compare func for sorting.
|
||||
TODO: If fast enough, use a unicode collation key and strcmp. */
|
||||
|
||||
#define TOSML(c) (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c))
|
||||
|
||||
static inline int
|
||||
fast_ascii_stricmp (const char *s1, const char *s2)
|
||||
{
|
||||
int c1, c2;
|
||||
|
||||
while (*s1 && *s2)
|
||||
{
|
||||
c1 = (int) (unsigned char) TOSML (*s1);
|
||||
c2 = (int) (unsigned char) TOSML (*s2);
|
||||
if (c1 != c2)
|
||||
return (c1 - c2);
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
|
||||
return (((int) (unsigned char) *s1) - ((int) (unsigned char) *s2));
|
||||
}
|
||||
|
||||
static gint
|
||||
custom_list_qsort_compare_func (chanlistrow ** a, chanlistrow ** b,
|
||||
CustomList * custom_list)
|
||||
{
|
||||
if (custom_list->sort_order == GTK_SORT_DESCENDING)
|
||||
{
|
||||
chanlistrow **tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
if (custom_list->sort_id == SORT_ID_USERS)
|
||||
{
|
||||
return (*a)->users - (*b)->users;
|
||||
}
|
||||
|
||||
if (custom_list->sort_id == SORT_ID_TOPIC)
|
||||
{
|
||||
return fast_ascii_stricmp ((*a)->topic, (*b)->topic);
|
||||
}
|
||||
|
||||
return strcmp ((*a)->collation_key, (*b)->collation_key);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* custom_list_new: This is what you use in your own code to create a
|
||||
* new custom list tree model for you to use.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
CustomList *
|
||||
custom_list_new (void)
|
||||
{
|
||||
return (CustomList *) g_object_new (CUSTOM_TYPE_LIST, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
custom_list_append (CustomList * custom_list, chanlistrow * newrecord)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GtkTreePath *path;
|
||||
gulong newsize;
|
||||
guint pos;
|
||||
|
||||
if (custom_list->num_rows >= custom_list->num_alloc)
|
||||
{
|
||||
custom_list->num_alloc += 64;
|
||||
newsize = custom_list->num_alloc * sizeof (chanlistrow *);
|
||||
custom_list->rows = g_realloc (custom_list->rows, newsize);
|
||||
}
|
||||
|
||||
/* TODO: Binary search insert? */
|
||||
|
||||
pos = custom_list->num_rows;
|
||||
custom_list->rows[pos] = newrecord;
|
||||
custom_list->num_rows++;
|
||||
newrecord->pos = pos;
|
||||
|
||||
/* inform the tree view and other interested objects
|
||||
* (e.g. tree row references) that we have inserted
|
||||
* a new row, and where it was inserted */
|
||||
|
||||
path = gtk_tree_path_new ();
|
||||
gtk_tree_path_append_index (path, newrecord->pos);
|
||||
/* custom_list_get_iter(GTK_TREE_MODEL(custom_list), &iter, path);*/
|
||||
iter.user_data = newrecord;
|
||||
gtk_tree_model_row_inserted (GTK_TREE_MODEL (custom_list), path, &iter);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
|
||||
void
|
||||
custom_list_resort (CustomList * custom_list)
|
||||
{
|
||||
GtkTreePath *path;
|
||||
gint *neworder, i;
|
||||
|
||||
if (custom_list->num_rows < 2)
|
||||
return;
|
||||
|
||||
/* resort */
|
||||
g_qsort_with_data (custom_list->rows,
|
||||
custom_list->num_rows,
|
||||
sizeof (chanlistrow *),
|
||||
(GCompareDataFunc) custom_list_qsort_compare_func,
|
||||
custom_list);
|
||||
|
||||
/* let other objects know about the new order */
|
||||
neworder = malloc (sizeof (gint) * custom_list->num_rows);
|
||||
|
||||
for (i = custom_list->num_rows - 1; i >= 0; i--)
|
||||
{
|
||||
/* Note that the API reference might be wrong about
|
||||
* this, see bug number 124790 on bugs.gnome.org.
|
||||
* Both will work, but one will give you 'jumpy'
|
||||
* selections after row reordering. */
|
||||
/* neworder[(custom_list->rows[i])->pos] = i; */
|
||||
neworder[i] = (custom_list->rows[i])->pos;
|
||||
(custom_list->rows[i])->pos = i;
|
||||
}
|
||||
|
||||
path = gtk_tree_path_new ();
|
||||
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (custom_list), path, NULL,
|
||||
neworder);
|
||||
gtk_tree_path_free (path);
|
||||
free (neworder);
|
||||
}
|
||||
|
||||
void
|
||||
custom_list_clear (CustomList * custom_list)
|
||||
{
|
||||
int i, max = custom_list->num_rows - 1;
|
||||
GtkTreePath *path;
|
||||
|
||||
for (i = max; i >= 0; i--)
|
||||
{
|
||||
path = gtk_tree_path_new ();
|
||||
gtk_tree_path_append_index (path, custom_list->rows[i]->pos);
|
||||
gtk_tree_model_row_deleted (GTK_TREE_MODEL (custom_list), path);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
|
||||
custom_list->num_rows = 0;
|
||||
custom_list->num_alloc = 0;
|
||||
|
||||
g_free (custom_list->rows);
|
||||
custom_list->rows = NULL;
|
||||
}
|
||||
85
src/fe-gtk/custom-list.h
Normal file
85
src/fe-gtk/custom-list.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#ifndef _custom_list_h_included_
|
||||
#define _custom_list_h_included_
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/* Some boilerplate GObject defines. 'klass' is used
|
||||
* instead of 'class', because 'class' is a C++ keyword */
|
||||
|
||||
#define CUSTOM_TYPE_LIST (custom_list_get_type ())
|
||||
#define CUSTOM_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CUSTOM_TYPE_LIST, CustomList))
|
||||
#define CUSTOM_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CUSTOM_TYPE_LIST, CustomListClass))
|
||||
#define CUSTOM_IS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CUSTOM_TYPE_LIST))
|
||||
#define CUSTOM_IS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CUSTOM_TYPE_LIST))
|
||||
#define CUSTOM_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CUSTOM_TYPE_LIST, CustomListClass))
|
||||
|
||||
/* The data columns that we export via the tree model interface */
|
||||
|
||||
enum
|
||||
{
|
||||
CUSTOM_LIST_COL_NAME,
|
||||
CUSTOM_LIST_COL_USERS,
|
||||
CUSTOM_LIST_COL_TOPIC,
|
||||
CUSTOM_LIST_N_COLUMNS
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SORT_ID_CHANNEL,
|
||||
SORT_ID_USERS,
|
||||
SORT_ID_TOPIC
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *topic;
|
||||
char *collation_key;
|
||||
guint32 pos; /* pos within the array */
|
||||
guint32 users;
|
||||
/* channel string lives beyond "users" */
|
||||
#define GET_CHAN(row) (((char *)row)+sizeof(chanlistrow))
|
||||
}
|
||||
chanlistrow;
|
||||
|
||||
typedef struct _CustomList CustomList;
|
||||
typedef struct _CustomListClass CustomListClass;
|
||||
|
||||
|
||||
|
||||
/* CustomList: this structure contains everything we need for our
|
||||
* model implementation. You can add extra fields to
|
||||
* this structure, e.g. hashtables to quickly lookup
|
||||
* rows or whatever else you might need, but it is
|
||||
* crucial that 'parent' is the first member of the
|
||||
* structure. */
|
||||
struct _CustomList
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
guint num_rows; /* number of rows that we have used */
|
||||
guint num_alloc; /* number of rows allocated */
|
||||
chanlistrow **rows; /* a dynamically allocated array of pointers to the
|
||||
* CustomRecord structure for each row */
|
||||
|
||||
gint n_columns;
|
||||
GType column_types[CUSTOM_LIST_N_COLUMNS];
|
||||
|
||||
gint sort_id;
|
||||
GtkSortType sort_order;
|
||||
};
|
||||
|
||||
|
||||
/* CustomListClass: more boilerplate GObject stuff */
|
||||
|
||||
struct _CustomListClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
CustomList *custom_list_new (void);
|
||||
void custom_list_append (CustomList *, chanlistrow *);
|
||||
void custom_list_resort (CustomList *);
|
||||
void custom_list_clear (CustomList *);
|
||||
|
||||
#endif /* _custom_list_h_included_ */
|
||||
1098
src/fe-gtk/dccgui.c
Normal file
1098
src/fe-gtk/dccgui.c
Normal file
File diff suppressed because it is too large
Load Diff
409
src/fe-gtk/editlist.c
Normal file
409
src/fe-gtk/editlist.c
Normal file
@@ -0,0 +1,409 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkclist.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkvseparator.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/cfgfiles.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "../common/fe.h"
|
||||
#include "menu.h"
|
||||
#include "gtkutil.h"
|
||||
#include "maingui.h"
|
||||
#include "editlist.h"
|
||||
|
||||
|
||||
static GtkWidget *editlist_gui_entry_name;
|
||||
static GtkWidget *editlist_gui_entry_cmd;
|
||||
static GtkWidget *editlist_gui_window;
|
||||
static GtkWidget *editlist_gui_list;
|
||||
static GSList *editlist_list;
|
||||
static char *editlist_file;
|
||||
static char *editlist_help;
|
||||
|
||||
|
||||
|
||||
static void
|
||||
editlist_gui_load (GtkWidget * listgad)
|
||||
{
|
||||
gchar *nnew[2];
|
||||
GSList *list = editlist_list;
|
||||
struct popup *pop;
|
||||
|
||||
while (list)
|
||||
{
|
||||
pop = (struct popup *) list->data;
|
||||
nnew[0] = pop->name;
|
||||
nnew[1] = pop->cmd;
|
||||
gtk_clist_append (GTK_CLIST (listgad), nnew);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_row_unselected (GtkWidget * clist, gint row, gint column,
|
||||
GdkEventButton * even, gpointer none)
|
||||
{
|
||||
gtk_entry_set_text (GTK_ENTRY (editlist_gui_entry_name), "");
|
||||
gtk_entry_set_text (GTK_ENTRY (editlist_gui_entry_cmd), "");
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_row_selected (GtkWidget * clist, gint row, gint column,
|
||||
GdkEventButton * even, gpointer none)
|
||||
{
|
||||
char *name, *cmd;
|
||||
|
||||
row = gtkutil_clist_selection (editlist_gui_list);
|
||||
if (row != -1)
|
||||
{
|
||||
gtk_clist_get_text (GTK_CLIST (clist), row, 0, &name);
|
||||
gtk_clist_get_text (GTK_CLIST (clist), row, 1, &cmd);
|
||||
|
||||
name = strdup (name);
|
||||
cmd = strdup (cmd);
|
||||
|
||||
gtk_entry_set_text (GTK_ENTRY (editlist_gui_entry_name), name);
|
||||
gtk_entry_set_text (GTK_ENTRY (editlist_gui_entry_cmd), cmd);
|
||||
|
||||
free (name);
|
||||
free (cmd);
|
||||
} else
|
||||
{
|
||||
editlist_gui_row_unselected (0, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_handle_cmd (GtkWidget * igad)
|
||||
{
|
||||
int row;
|
||||
const char *reply;
|
||||
|
||||
row = gtkutil_clist_selection (editlist_gui_list);
|
||||
if (row != -1)
|
||||
{
|
||||
reply = gtk_entry_get_text (GTK_ENTRY (igad));
|
||||
gtk_clist_set_text (GTK_CLIST (editlist_gui_list), row, 1, reply);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_handle_name (GtkWidget * igad)
|
||||
{
|
||||
int row;
|
||||
const char *ctcp;
|
||||
|
||||
row = gtkutil_clist_selection (editlist_gui_list);
|
||||
if (row != -1)
|
||||
{
|
||||
ctcp = gtk_entry_get_text (GTK_ENTRY (igad));
|
||||
gtk_clist_set_text (GTK_CLIST (editlist_gui_list), row, 0, ctcp);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_addnew (GtkWidget * igad)
|
||||
{
|
||||
int i;
|
||||
gchar *nnew[2];
|
||||
|
||||
nnew[0] = _("*NEW*");
|
||||
nnew[1] = _("EDIT ME");
|
||||
|
||||
i = gtk_clist_append (GTK_CLIST (editlist_gui_list), nnew);
|
||||
gtk_clist_select_row (GTK_CLIST (editlist_gui_list), i, 0);
|
||||
gtk_clist_moveto (GTK_CLIST (editlist_gui_list), i, 0, 0.5, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_delete (GtkWidget * igad)
|
||||
{
|
||||
int row;
|
||||
|
||||
row = gtkutil_clist_selection (editlist_gui_list);
|
||||
if (row != -1)
|
||||
{
|
||||
gtk_clist_unselect_all (GTK_CLIST (editlist_gui_list));
|
||||
gtk_clist_remove (GTK_CLIST (editlist_gui_list), row);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_save (GtkWidget * igad)
|
||||
{
|
||||
int fh, i = 0;
|
||||
char buf[512];
|
||||
char *a, *b;
|
||||
|
||||
fh = xchat_open_file (editlist_file, O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
|
||||
if (fh != -1)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
if (!gtk_clist_get_text (GTK_CLIST (editlist_gui_list), i, 0, &a))
|
||||
break;
|
||||
gtk_clist_get_text (GTK_CLIST (editlist_gui_list), i, 1, &b);
|
||||
snprintf (buf, sizeof (buf), "NAME %s\nCMD %s\n\n", a, b);
|
||||
write (fh, buf, strlen (buf));
|
||||
i++;
|
||||
}
|
||||
close (fh);
|
||||
gtk_widget_destroy (editlist_gui_window);
|
||||
if (editlist_list == replace_list)
|
||||
{
|
||||
list_free (&replace_list);
|
||||
list_loadconf (editlist_file, &replace_list, 0);
|
||||
} else if (editlist_list == popup_list)
|
||||
{
|
||||
list_free (&popup_list);
|
||||
list_loadconf (editlist_file, &popup_list, 0);
|
||||
} else if (editlist_list == button_list)
|
||||
{
|
||||
GSList *list = sess_list;
|
||||
struct session *sess;
|
||||
list_free (&button_list);
|
||||
list_loadconf (editlist_file, &button_list, 0);
|
||||
while (list)
|
||||
{
|
||||
sess = (struct session *) list->data;
|
||||
fe_buttons_update (sess);
|
||||
list = list->next;
|
||||
}
|
||||
} else if (editlist_list == dlgbutton_list)
|
||||
{
|
||||
GSList *list = sess_list;
|
||||
struct session *sess;
|
||||
list_free (&dlgbutton_list);
|
||||
list_loadconf (editlist_file, &dlgbutton_list, 0);
|
||||
while (list)
|
||||
{
|
||||
sess = (struct session *) list->data;
|
||||
fe_dlgbuttons_update (sess);
|
||||
list = list->next;
|
||||
}
|
||||
} else if (editlist_list == ctcp_list)
|
||||
{
|
||||
list_free (&ctcp_list);
|
||||
list_loadconf (editlist_file, &ctcp_list, 0);
|
||||
} else if (editlist_list == command_list)
|
||||
{
|
||||
list_free (&command_list);
|
||||
list_loadconf (editlist_file, &command_list, 0);
|
||||
} else if (editlist_list == usermenu_list)
|
||||
{
|
||||
list_free (&usermenu_list);
|
||||
list_loadconf (editlist_file, &usermenu_list, 0);
|
||||
usermenu_update ();
|
||||
} else
|
||||
{
|
||||
list_free (&urlhandler_list);
|
||||
list_loadconf (editlist_file, &urlhandler_list, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_help (GtkWidget * igad)
|
||||
{
|
||||
/* if (editlist_help)*/
|
||||
fe_message (editlist_help, FE_MSG_INFO);
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_sort (GtkWidget * igad)
|
||||
{
|
||||
int row;
|
||||
|
||||
row = gtkutil_clist_selection (editlist_gui_list);
|
||||
if (row != -1)
|
||||
gtk_clist_unselect_row (GTK_CLIST (editlist_gui_list), row, 0);
|
||||
gtk_clist_sort (GTK_CLIST (editlist_gui_list));
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_movedown (GtkWidget * igad)
|
||||
{
|
||||
int row;
|
||||
char *temp;
|
||||
|
||||
row = gtkutil_clist_selection (editlist_gui_list);
|
||||
if (row != -1)
|
||||
{
|
||||
if (!gtk_clist_get_text (GTK_CLIST (editlist_gui_list), row + 1, 0, &temp))
|
||||
return;
|
||||
gtk_clist_freeze (GTK_CLIST (editlist_gui_list));
|
||||
gtk_clist_swap_rows (GTK_CLIST (editlist_gui_list), row, row + 1);
|
||||
gtk_clist_thaw (GTK_CLIST (editlist_gui_list));
|
||||
row++;
|
||||
if (!gtk_clist_row_is_visible (GTK_CLIST (editlist_gui_list), row) !=
|
||||
GTK_VISIBILITY_FULL)
|
||||
gtk_clist_moveto (GTK_CLIST (editlist_gui_list), row, 0, 0.9, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_moveup (GtkWidget * igad)
|
||||
{
|
||||
int row;
|
||||
|
||||
row = gtkutil_clist_selection (editlist_gui_list);
|
||||
if (row != -1 && row > 0)
|
||||
{
|
||||
gtk_clist_freeze (GTK_CLIST (editlist_gui_list));
|
||||
gtk_clist_swap_rows (GTK_CLIST (editlist_gui_list), row - 1, row);
|
||||
gtk_clist_thaw (GTK_CLIST (editlist_gui_list));
|
||||
row--;
|
||||
if (gtk_clist_row_is_visible (GTK_CLIST (editlist_gui_list), row) !=
|
||||
GTK_VISIBILITY_FULL)
|
||||
gtk_clist_moveto (GTK_CLIST (editlist_gui_list), row, 0, 0.1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
editlist_gui_close (void)
|
||||
{
|
||||
editlist_gui_window = 0;
|
||||
}
|
||||
|
||||
void
|
||||
editlist_gui_open (char *title1, char *title2, GSList * list, char *title, char *wmclass,
|
||||
char *file, char *help)
|
||||
{
|
||||
gchar *titles[2];
|
||||
GtkWidget *vbox, *hbox, *button;
|
||||
|
||||
if (title1)
|
||||
{
|
||||
titles[0] = title1;
|
||||
titles[1] = title2;
|
||||
} else
|
||||
{
|
||||
titles[0] = _("Name");
|
||||
titles[1] = _("Command");
|
||||
}
|
||||
|
||||
if (editlist_gui_window)
|
||||
{
|
||||
mg_bring_tofront (editlist_gui_window);
|
||||
return;
|
||||
}
|
||||
|
||||
editlist_list = list;
|
||||
editlist_file = file;
|
||||
editlist_help = help;
|
||||
|
||||
editlist_gui_window =
|
||||
mg_create_generic_tab (wmclass, title, TRUE, FALSE,
|
||||
editlist_gui_close, NULL, 450, 250, &vbox, 0);
|
||||
|
||||
editlist_gui_list = gtkutil_clist_new (2, titles, vbox, GTK_POLICY_ALWAYS,
|
||||
editlist_gui_row_selected, 0,
|
||||
editlist_gui_row_unselected, 0,
|
||||
GTK_SELECTION_BROWSE);
|
||||
gtk_clist_set_column_width (GTK_CLIST (editlist_gui_list), 0, 90);
|
||||
|
||||
hbox = gtk_hbox_new (0, 2);
|
||||
gtk_box_pack_end (GTK_BOX (vbox), hbox, 0, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_GO_UP, 0, editlist_gui_moveup,
|
||||
0, _("Move Up"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_GO_DOWN, 0, editlist_gui_movedown,
|
||||
0, _("Move Dn"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
button = gtk_vseparator_new ();
|
||||
gtk_container_add (GTK_CONTAINER (hbox), button);
|
||||
gtk_widget_show (button);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_CANCEL, 0, gtkutil_destroy,
|
||||
editlist_gui_window, _("Cancel"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_SAVE, 0, editlist_gui_save,
|
||||
0, _("Save"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
hbox = gtk_hbox_new (0, 2);
|
||||
gtk_box_pack_end (GTK_BOX (vbox), hbox, 0, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_ADD, 0, editlist_gui_addnew,
|
||||
0, _("Add New"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_REMOVE, 0, editlist_gui_delete,
|
||||
0, _("Delete"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
button = gtk_vseparator_new ();
|
||||
gtk_container_add (GTK_CONTAINER (hbox), button);
|
||||
gtk_widget_show (button);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_SORT_ASCENDING, 0, editlist_gui_sort,
|
||||
0, _("Sort"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
button = gtkutil_button (hbox, GTK_STOCK_HELP, 0, editlist_gui_help,
|
||||
0, _("Help"));
|
||||
gtk_widget_set_usize (button, 100, 0);
|
||||
|
||||
if (!help)
|
||||
gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
|
||||
|
||||
hbox = gtk_hbox_new (0, 2);
|
||||
gtk_box_pack_end (GTK_BOX (vbox), hbox, 0, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
editlist_gui_entry_name = gtk_entry_new_with_max_length (82);
|
||||
gtk_widget_set_usize (editlist_gui_entry_name, 96, 0);
|
||||
gtk_signal_connect (GTK_OBJECT (editlist_gui_entry_name), "changed",
|
||||
GTK_SIGNAL_FUNC (editlist_gui_handle_name), 0);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), editlist_gui_entry_name, 0, 0, 0);
|
||||
gtk_widget_show (editlist_gui_entry_name);
|
||||
|
||||
editlist_gui_entry_cmd = gtk_entry_new_with_max_length (255);
|
||||
gtk_signal_connect (GTK_OBJECT (editlist_gui_entry_cmd), "changed",
|
||||
GTK_SIGNAL_FUNC (editlist_gui_handle_cmd), 0);
|
||||
gtk_container_add (GTK_CONTAINER (hbox), editlist_gui_entry_cmd);
|
||||
gtk_widget_show (editlist_gui_entry_cmd);
|
||||
|
||||
hbox = gtk_hbox_new (0, 2);
|
||||
gtk_box_pack_end (GTK_BOX (vbox), hbox, 0, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
editlist_gui_load (editlist_gui_list);
|
||||
|
||||
gtk_widget_show (editlist_gui_window);
|
||||
}
|
||||
1
src/fe-gtk/editlist.h
Normal file
1
src/fe-gtk/editlist.h
Normal file
@@ -0,0 +1 @@
|
||||
void editlist_gui_open (char *title1, char *title2, GSList * list, char *title, char *wmclass, char *file, char *help);
|
||||
1063
src/fe-gtk/fe-gtk.c
Normal file
1063
src/fe-gtk/fe-gtk.c
Normal file
File diff suppressed because it is too large
Load Diff
197
src/fe-gtk/fe-gtk.h
Normal file
197
src/fe-gtk/fe-gtk.h
Normal file
@@ -0,0 +1,197 @@
|
||||
#include "../../config.h"
|
||||
|
||||
#ifdef WIN32
|
||||
/* If you're compiling this for Windows, your release is un-official
|
||||
* and not condoned. Please don't use the XChat name. Make up your
|
||||
* own name! */
|
||||
#define DISPLAY_NAME "XChat-Unofficial"
|
||||
#else
|
||||
#define DISPLAY_NAME "XChat"
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/types.h>
|
||||
#include <regex.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_NLS) && !defined(_)
|
||||
# include <libintl.h>
|
||||
# define _(x) gettext(x)
|
||||
# ifdef gettext_noop
|
||||
# define N_(String) gettext_noop (String)
|
||||
# else
|
||||
# define N_(String) (String)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(ENABLE_NLS) && defined(_)
|
||||
# undef _
|
||||
# define N_(String) (String)
|
||||
# define _(x) (x)
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkwidget.h>
|
||||
#include <gtk/gtkcontainer.h>
|
||||
#include <gtk/gtksignal.h>
|
||||
|
||||
#undef gtk_signal_connect
|
||||
#define gtk_signal_connect g_signal_connect
|
||||
|
||||
#define flag_t flag_wid[0]
|
||||
#define flag_n flag_wid[1]
|
||||
#define flag_s flag_wid[2]
|
||||
#define flag_i flag_wid[3]
|
||||
#define flag_p flag_wid[4]
|
||||
#define flag_m flag_wid[5]
|
||||
#define flag_l flag_wid[6]
|
||||
#define flag_k flag_wid[7]
|
||||
#define flag_b flag_wid[8]
|
||||
#define NUM_FLAG_WIDS 9
|
||||
|
||||
struct server_gui
|
||||
{
|
||||
GtkWidget *rawlog_window;
|
||||
GtkWidget *rawlog_textlist;
|
||||
|
||||
/* join dialog */
|
||||
GtkWidget *joind_win;
|
||||
GtkWidget *joind_entry;
|
||||
GtkWidget *joind_radio1;
|
||||
GtkWidget *joind_radio2;
|
||||
GtkWidget *joind_check;
|
||||
|
||||
/* chanlist variables */
|
||||
GtkWidget *chanlist_wild; /* GtkEntry */
|
||||
GtkWidget *chanlist_window;
|
||||
GtkWidget *chanlist_list;
|
||||
GtkWidget *chanlist_label;
|
||||
GtkWidget *chanlist_min_spin; /* minusers GtkSpinButton */
|
||||
GtkWidget *chanlist_refresh; /* buttons */
|
||||
GtkWidget *chanlist_join;
|
||||
GtkWidget *chanlist_savelist;
|
||||
GtkWidget *chanlist_search;
|
||||
|
||||
GSList *chanlist_data_stored_rows; /* stored list so it can be resorted */
|
||||
GSList *chanlist_pending_rows;
|
||||
gint chanlist_tag;
|
||||
gint chanlist_flash_tag;
|
||||
|
||||
gboolean chanlist_match_wants_channel; /* match in channel name */
|
||||
gboolean chanlist_match_wants_topic; /* match in topic */
|
||||
|
||||
#ifndef WIN32
|
||||
regex_t chanlist_match_regex; /* compiled regular expression here */
|
||||
unsigned int have_regex;
|
||||
#endif
|
||||
|
||||
guint chanlist_users_found_count; /* users total for all channels */
|
||||
guint chanlist_users_shown_count; /* users total for displayed channels */
|
||||
guint chanlist_channels_found_count; /* channel total for /LIST operation */
|
||||
guint chanlist_channels_shown_count; /* total number of displayed
|
||||
channels */
|
||||
|
||||
int chanlist_maxusers;
|
||||
int chanlist_minusers;
|
||||
int chanlist_minusers_downloaded; /* used by LIST IRC command */
|
||||
int chanlist_search_type; /* 0=simple 1=pattern/wildcard 2=regexp */
|
||||
gboolean chanlist_caption_is_stale;
|
||||
};
|
||||
|
||||
/* this struct is persistant even when delinking/relinking */
|
||||
|
||||
typedef struct restore_gui
|
||||
{
|
||||
/* banlist stuff */
|
||||
GtkWidget *banlist_window;
|
||||
GtkWidget *banlist_treeview;
|
||||
GtkWidget *banlist_butRefresh;
|
||||
|
||||
void *tab; /* (chan *) */
|
||||
|
||||
/* information stored when this tab isn't front-most */
|
||||
void *user_model; /* for filling the GtkTreeView */
|
||||
void *buffer; /* xtext_Buffer */
|
||||
char *input_text; /* input text buffer (while not-front tab) */
|
||||
char *topic_text; /* topic GtkEntry buffer */
|
||||
char *key_text;
|
||||
char *limit_text;
|
||||
gfloat old_ul_value; /* old userlist value (for adj) */
|
||||
gfloat lag_value; /* lag-o-meter */
|
||||
char *lag_text; /* lag-o-meter text */
|
||||
char *lag_tip; /* lag-o-meter tooltip */
|
||||
gfloat queue_value; /* outbound queue meter */
|
||||
char *queue_text; /* outbound queue text */
|
||||
char *queue_tip; /* outbound queue tooltip */
|
||||
short flag_wid_state[NUM_FLAG_WIDS];
|
||||
unsigned int c_graph:1; /* connecting graph, is there one? */
|
||||
} restore_gui;
|
||||
|
||||
typedef struct session_gui
|
||||
{
|
||||
GtkWidget
|
||||
*xtext,
|
||||
*vscrollbar,
|
||||
*window, /* toplevel */
|
||||
*topic_entry,
|
||||
*note_book,
|
||||
*main_table,
|
||||
*user_tree, /* GtkTreeView */
|
||||
*user_box, /* userlist box */
|
||||
*button_box_parent,
|
||||
*button_box, /* userlist buttons' box */
|
||||
*dialogbutton_box,
|
||||
*topicbutton_box,
|
||||
*meter_box, /* all the meters inside this */
|
||||
*lagometer,
|
||||
*laginfo,
|
||||
*throttlemeter,
|
||||
*throttleinfo,
|
||||
*topic_bar,
|
||||
*hpane_left,
|
||||
*hpane_right,
|
||||
*vpane_left,
|
||||
*vpane_right,
|
||||
*menu,
|
||||
*bar, /* connecting progress bar */
|
||||
*nick_box, /* contains label to the left of input_box */
|
||||
*nick_label,
|
||||
*op_xpm, /* icon to the left of nickname */
|
||||
*namelistinfo, /* label above userlist */
|
||||
*input_box,
|
||||
*flag_wid[NUM_FLAG_WIDS], /* channelmode buttons */
|
||||
*limit_entry, /* +l */
|
||||
*key_entry; /* +k */
|
||||
|
||||
#define MENU_ID_NUM 12
|
||||
GtkWidget *menu_item[MENU_ID_NUM+1]; /* some items we may change state of */
|
||||
|
||||
void *chanview; /* chanview.h */
|
||||
|
||||
int bartag; /*connecting progressbar timeout */
|
||||
|
||||
int pane_left_size; /*last position of the pane*/
|
||||
int pane_right_size;
|
||||
|
||||
guint16 is_tab; /* is tab or toplevel? */
|
||||
guint16 ul_hidden; /* userlist hidden? */
|
||||
|
||||
} session_gui;
|
||||
|
||||
extern GdkPixmap *channelwin_pix;
|
||||
extern GdkPixmap *dialogwin_pix;
|
||||
|
||||
|
||||
#ifdef USE_GTKSPELL
|
||||
char *SPELL_ENTRY_GET_TEXT (GtkWidget *entry);
|
||||
#define SPELL_ENTRY_SET_TEXT(e,txt) gtk_text_buffer_set_text (gtk_text_view_get_buffer(GTK_TEXT_VIEW(e)),txt,-1);
|
||||
#define SPELL_ENTRY_SET_EDITABLE(e,v) gtk_text_view_set_editable(GTK_TEXT_VIEW(e), v)
|
||||
int SPELL_ENTRY_GET_POS (GtkWidget *entry);
|
||||
void SPELL_ENTRY_SET_POS (GtkWidget *entry, int pos);
|
||||
void SPELL_ENTRY_INSERT (GtkWidget *entry, const char *text, int len, int *pos);
|
||||
#else
|
||||
#define SPELL_ENTRY_GET_TEXT(e) (GTK_ENTRY(e)->text)
|
||||
#define SPELL_ENTRY_SET_TEXT(e,txt) gtk_entry_set_text(GTK_ENTRY(e),txt)
|
||||
#define SPELL_ENTRY_SET_EDITABLE(e,v) gtk_editable_set_editable(GTK_EDITABLE(e),v)
|
||||
#define SPELL_ENTRY_GET_POS(e) gtk_editable_get_position(GTK_EDITABLE(e))
|
||||
#define SPELL_ENTRY_SET_POS(e,p) gtk_editable_set_position(GTK_EDITABLE(e),p);
|
||||
#define SPELL_ENTRY_INSERT(e,t,l,p) gtk_editable_insert_text(GTK_EDITABLE(e),t,l,p)
|
||||
#endif
|
||||
1814
src/fe-gtk/fkeys.c
Normal file
1814
src/fe-gtk/fkeys.c
Normal file
File diff suppressed because it is too large
Load Diff
5
src/fe-gtk/fkeys.h
Normal file
5
src/fe-gtk/fkeys.h
Normal file
@@ -0,0 +1,5 @@
|
||||
void key_init (void);
|
||||
void key_dialog_show (void);
|
||||
int key_handle_key_press (GtkWidget * wid, GdkEventKey * evt, session *sess);
|
||||
int key_action_insert (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2,
|
||||
session *sess);
|
||||
675
src/fe-gtk/gtkutil.c
Normal file
675
src/fe-gtk/gtkutil.c
Normal file
@@ -0,0 +1,675 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
#define _FILE_OFFSET_BITS 64 /* allow selection of large files */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtkclist.h>
|
||||
#include <gtk/gtkscrolledwindow.h>
|
||||
#include <gtk/gtkmessagedialog.h>
|
||||
#include <gtk/gtkwindow.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkimage.h>
|
||||
#include <gtk/gtktooltips.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkspinbutton.h>
|
||||
#include <gtk/gtkclipboard.h>
|
||||
#include <gtk/gtktreeview.h>
|
||||
#include <gtk/gtktreeselection.h>
|
||||
#include <gtk/gtkcellrenderertext.h>
|
||||
#include <gtk/gtkcellrenderertoggle.h>
|
||||
#include <gtk/gtkversion.h>
|
||||
#include <gtk/gtkfilechooserdialog.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/fe.h"
|
||||
#include "../common/util.h"
|
||||
#include "gtkutil.h"
|
||||
#include "pixmaps.h"
|
||||
|
||||
/* gtkutil.c, just some gtk wrappers */
|
||||
|
||||
extern void path_part (char *file, char *path, int pathlen);
|
||||
|
||||
|
||||
struct file_req
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
void *userdata;
|
||||
filereqcallback callback;
|
||||
int flags; /* FRF_* flags */
|
||||
};
|
||||
|
||||
static char last_dir[256] = "";
|
||||
|
||||
|
||||
static void
|
||||
gtkutil_file_req_destroy (GtkWidget * wid, struct file_req *freq)
|
||||
{
|
||||
freq->callback (freq->userdata, NULL);
|
||||
free (freq);
|
||||
}
|
||||
|
||||
static void
|
||||
gtkutil_check_file (char *file, struct file_req *freq)
|
||||
{
|
||||
struct stat st;
|
||||
int axs = FALSE;
|
||||
|
||||
path_part (file, last_dir, sizeof (last_dir));
|
||||
|
||||
/* check if the file is readable or writable */
|
||||
if (freq->flags & FRF_WRITE)
|
||||
{
|
||||
if (access (last_dir, W_OK) == 0)
|
||||
axs = TRUE;
|
||||
} else
|
||||
{
|
||||
if (stat (file, &st) != -1)
|
||||
{
|
||||
if (!S_ISDIR (st.st_mode) || (freq->flags & FRF_CHOOSEFOLDER))
|
||||
axs = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (axs)
|
||||
{
|
||||
char *utf8_file;
|
||||
/* convert to UTF8. It might be converted back to locale by
|
||||
server.c's g_convert */
|
||||
utf8_file = xchat_filename_to_utf8 (file, -1, NULL, NULL, NULL);
|
||||
if (utf8_file)
|
||||
{
|
||||
freq->callback (freq->userdata, utf8_file);
|
||||
g_free (utf8_file);
|
||||
} else
|
||||
{
|
||||
fe_message ("Filename encoding is corrupt.", FE_MSG_ERROR);
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (freq->flags & FRF_WRITE)
|
||||
fe_message (_("Cannot write to that file."), FE_MSG_ERROR);
|
||||
else
|
||||
fe_message (_("Cannot read that file."), FE_MSG_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtkutil_file_req_done (GtkWidget * wid, struct file_req *freq)
|
||||
{
|
||||
GSList *files, *cur;
|
||||
GtkFileChooser *fs = GTK_FILE_CHOOSER (freq->dialog);
|
||||
|
||||
if (freq->flags & FRF_MULTIPLE)
|
||||
{
|
||||
files = cur = gtk_file_chooser_get_filenames (fs);
|
||||
while (cur)
|
||||
{
|
||||
gtkutil_check_file (cur->data, freq);
|
||||
g_free (cur->data);
|
||||
cur = cur->next;
|
||||
}
|
||||
if (files)
|
||||
g_slist_free (files);
|
||||
} else
|
||||
{
|
||||
if (freq->flags & FRF_CHOOSEFOLDER)
|
||||
gtkutil_check_file (gtk_file_chooser_get_current_folder (fs), freq);
|
||||
else
|
||||
gtkutil_check_file (gtk_file_chooser_get_filename (fs), freq);
|
||||
}
|
||||
|
||||
/* this should call the "destroy" cb, where we free(freq) */
|
||||
gtk_widget_destroy (freq->dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
gtkutil_file_req_response (GtkWidget *dialog, gint res, struct file_req *freq)
|
||||
{
|
||||
switch (res)
|
||||
{
|
||||
case GTK_RESPONSE_ACCEPT:
|
||||
gtkutil_file_req_done (dialog, freq);
|
||||
break;
|
||||
|
||||
case GTK_RESPONSE_CANCEL:
|
||||
/* this should call the "destroy" cb, where we free(freq) */
|
||||
gtk_widget_destroy (freq->dialog);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtkutil_file_req (const char *title, void *callback, void *userdata, char *filter,
|
||||
int flags)
|
||||
{
|
||||
struct file_req *freq;
|
||||
GtkWidget *dialog;
|
||||
extern char *get_xdir_fs (void);
|
||||
|
||||
if (flags & FRF_WRITE)
|
||||
{
|
||||
dialog = gtk_file_chooser_dialog_new (title, NULL,
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
if (filter && filter[0]) /* filter becomes initial name when saving */
|
||||
{
|
||||
char temp[1024];
|
||||
path_part (filter, temp, sizeof (temp));
|
||||
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), temp);
|
||||
gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), file_part (filter));
|
||||
}
|
||||
#if GTK_CHECK_VERSION(2,8,0)
|
||||
if (!(flags & FRF_NOASKOVERWRITE))
|
||||
gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
dialog = gtk_file_chooser_dialog_new (title, NULL,
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
if (flags & FRF_MULTIPLE)
|
||||
gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
|
||||
if (last_dir[0])
|
||||
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), last_dir);
|
||||
if (flags & FRF_ADDFOLDER)
|
||||
gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (dialog),
|
||||
get_xdir_fs (), NULL);
|
||||
if (flags & FRF_CHOOSEFOLDER)
|
||||
{
|
||||
gtk_file_chooser_set_action (GTK_FILE_CHOOSER (dialog), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
|
||||
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), filter);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (filter && (flags & FRF_FILTERISINITIAL))
|
||||
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), filter);
|
||||
}
|
||||
|
||||
freq = malloc (sizeof (struct file_req));
|
||||
freq->dialog = dialog;
|
||||
freq->flags = flags;
|
||||
freq->callback = callback;
|
||||
freq->userdata = userdata;
|
||||
|
||||
g_signal_connect (G_OBJECT (dialog), "response",
|
||||
G_CALLBACK (gtkutil_file_req_response), freq);
|
||||
g_signal_connect (G_OBJECT (dialog), "destroy",
|
||||
G_CALLBACK (gtkutil_file_req_destroy), (gpointer) freq);
|
||||
gtk_widget_show (dialog);
|
||||
}
|
||||
|
||||
void
|
||||
gtkutil_destroy (GtkWidget * igad, GtkWidget * dgad)
|
||||
{
|
||||
gtk_widget_destroy (dgad);
|
||||
}
|
||||
|
||||
static void
|
||||
gtkutil_get_str_response (GtkDialog *dialog, gint arg1, gpointer entry)
|
||||
{
|
||||
void (*callback) (int cancel, char *text, void *user_data);
|
||||
char *text;
|
||||
void *user_data;
|
||||
|
||||
text = (char *) gtk_entry_get_text (GTK_ENTRY (entry));
|
||||
callback = g_object_get_data (G_OBJECT (dialog), "cb");
|
||||
user_data = g_object_get_data (G_OBJECT (dialog), "ud");
|
||||
|
||||
switch (arg1)
|
||||
{
|
||||
case GTK_RESPONSE_REJECT:
|
||||
callback (TRUE, text, user_data);
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
break;
|
||||
case GTK_RESPONSE_ACCEPT:
|
||||
callback (FALSE, text, user_data);
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtkutil_str_enter (GtkWidget *entry, GtkWidget *dialog)
|
||||
{
|
||||
gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
|
||||
}
|
||||
|
||||
void
|
||||
fe_get_str (char *msg, char *def, void *callback, void *userdata)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *entry;
|
||||
GtkWidget *hbox;
|
||||
GtkWidget *label;
|
||||
|
||||
dialog = gtk_dialog_new_with_buttons (msg, NULL, 0,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
|
||||
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
gtk_box_set_homogeneous (GTK_BOX (GTK_DIALOG (dialog)->vbox), TRUE);
|
||||
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
|
||||
hbox = gtk_hbox_new (TRUE, 0);
|
||||
|
||||
g_object_set_data (G_OBJECT (dialog), "cb", callback);
|
||||
g_object_set_data (G_OBJECT (dialog), "ud", userdata);
|
||||
|
||||
entry = gtk_entry_new ();
|
||||
g_signal_connect (G_OBJECT (entry), "activate",
|
||||
G_CALLBACK (gtkutil_str_enter), dialog);
|
||||
gtk_entry_set_text (GTK_ENTRY (entry), def);
|
||||
gtk_box_pack_end (GTK_BOX (hbox), entry, 0, 0, 0);
|
||||
|
||||
label = gtk_label_new (msg);
|
||||
gtk_box_pack_end (GTK_BOX (hbox), label, 0, 0, 0);
|
||||
|
||||
g_signal_connect (G_OBJECT (dialog), "response",
|
||||
G_CALLBACK (gtkutil_get_str_response), entry);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox);
|
||||
|
||||
gtk_widget_show_all (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
gtkutil_get_number_response (GtkDialog *dialog, gint arg1, gpointer spin)
|
||||
{
|
||||
void (*callback) (int cancel, int value, void *user_data);
|
||||
int num;
|
||||
void *user_data;
|
||||
|
||||
num = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spin));
|
||||
callback = g_object_get_data (G_OBJECT (dialog), "cb");
|
||||
user_data = g_object_get_data (G_OBJECT (dialog), "ud");
|
||||
|
||||
switch (arg1)
|
||||
{
|
||||
case GTK_RESPONSE_REJECT:
|
||||
callback (TRUE, num, user_data);
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
break;
|
||||
case GTK_RESPONSE_ACCEPT:
|
||||
callback (FALSE, num, user_data);
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fe_get_int (char *msg, int def, void *callback, void *userdata)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *spin;
|
||||
GtkWidget *hbox;
|
||||
GtkWidget *label;
|
||||
GtkAdjustment *adj;
|
||||
|
||||
dialog = gtk_dialog_new_with_buttons (msg, NULL, 0,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
|
||||
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
gtk_box_set_homogeneous (GTK_BOX (GTK_DIALOG (dialog)->vbox), TRUE);
|
||||
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
|
||||
hbox = gtk_hbox_new (TRUE, 0);
|
||||
|
||||
g_object_set_data (G_OBJECT (dialog), "cb", callback);
|
||||
g_object_set_data (G_OBJECT (dialog), "ud", userdata);
|
||||
|
||||
spin = gtk_spin_button_new (NULL, 1, 0);
|
||||
adj = gtk_spin_button_get_adjustment ((GtkSpinButton*)spin);
|
||||
adj->lower = 0;
|
||||
adj->upper = 1024;
|
||||
adj->step_increment = 1;
|
||||
gtk_adjustment_changed (adj);
|
||||
gtk_spin_button_set_value ((GtkSpinButton*)spin, def);
|
||||
gtk_box_pack_end (GTK_BOX (hbox), spin, 0, 0, 0);
|
||||
|
||||
label = gtk_label_new (msg);
|
||||
gtk_box_pack_end (GTK_BOX (hbox), label, 0, 0, 0);
|
||||
|
||||
g_signal_connect (G_OBJECT (dialog), "response",
|
||||
G_CALLBACK (gtkutil_get_number_response), spin);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox);
|
||||
|
||||
gtk_widget_show_all (dialog);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtkutil_button (GtkWidget *box, char *stock, char *tip, void *callback,
|
||||
void *userdata, char *labeltext)
|
||||
{
|
||||
GtkWidget *wid, *img, *bbox;
|
||||
|
||||
wid = gtk_button_new ();
|
||||
|
||||
if (labeltext)
|
||||
{
|
||||
gtk_button_set_label (GTK_BUTTON (wid), labeltext);
|
||||
gtk_button_set_image (GTK_BUTTON (wid), gtk_image_new_from_stock (stock, GTK_ICON_SIZE_MENU));
|
||||
gtk_button_set_use_underline (GTK_BUTTON (wid), TRUE);
|
||||
if (box)
|
||||
gtk_container_add (GTK_CONTAINER (box), wid);
|
||||
}
|
||||
else
|
||||
{
|
||||
bbox = gtk_hbox_new (0, 0);
|
||||
gtk_container_add (GTK_CONTAINER (wid), bbox);
|
||||
gtk_widget_show (bbox);
|
||||
|
||||
img = gtk_image_new_from_stock (stock, GTK_ICON_SIZE_MENU);
|
||||
if (stock == GTK_STOCK_GOTO_LAST)
|
||||
gtk_widget_set_usize (img, 10, 6);
|
||||
gtk_container_add (GTK_CONTAINER (bbox), img);
|
||||
gtk_widget_show (img);
|
||||
gtk_box_pack_start (GTK_BOX (box), wid, 0, 0, 0);
|
||||
}
|
||||
|
||||
g_signal_connect (G_OBJECT (wid), "clicked",
|
||||
G_CALLBACK (callback), userdata);
|
||||
gtk_widget_show (wid);
|
||||
if (tip)
|
||||
add_tip (wid, tip);
|
||||
|
||||
return wid;
|
||||
}
|
||||
|
||||
void
|
||||
gtkutil_label_new (char *text, GtkWidget * box)
|
||||
{
|
||||
GtkWidget *label = gtk_label_new (text);
|
||||
gtk_container_add (GTK_CONTAINER (box), label);
|
||||
gtk_widget_show (label);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtkutil_entry_new (int max, GtkWidget * box, void *callback,
|
||||
gpointer userdata)
|
||||
{
|
||||
GtkWidget *entry = gtk_entry_new_with_max_length (max);
|
||||
gtk_container_add (GTK_CONTAINER (box), entry);
|
||||
if (callback)
|
||||
g_signal_connect (G_OBJECT (entry), "changed",
|
||||
G_CALLBACK (callback), userdata);
|
||||
gtk_widget_show (entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtkutil_clist_new (int columns, char *titles[],
|
||||
GtkWidget * box, int policy,
|
||||
void *select_callback, gpointer select_userdata,
|
||||
void *unselect_callback,
|
||||
gpointer unselect_userdata, int selection_mode)
|
||||
{
|
||||
GtkWidget *clist, *win;
|
||||
|
||||
win = gtk_scrolled_window_new (0, 0);
|
||||
gtk_container_add (GTK_CONTAINER (box), win);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (win),
|
||||
GTK_POLICY_AUTOMATIC, policy);
|
||||
gtk_widget_show (win);
|
||||
|
||||
if (titles)
|
||||
clist = gtk_clist_new_with_titles (columns, titles);
|
||||
else
|
||||
clist = gtk_clist_new (columns);
|
||||
|
||||
gtk_clist_set_selection_mode (GTK_CLIST (clist), selection_mode);
|
||||
gtk_clist_column_titles_passive (GTK_CLIST (clist));
|
||||
gtk_container_add (GTK_CONTAINER (win), clist);
|
||||
if (select_callback)
|
||||
{
|
||||
g_signal_connect (G_OBJECT (clist), "select_row",
|
||||
G_CALLBACK (select_callback), select_userdata);
|
||||
}
|
||||
if (unselect_callback)
|
||||
{
|
||||
g_signal_connect (G_OBJECT (clist), "unselect_row",
|
||||
G_CALLBACK (unselect_callback), unselect_userdata);
|
||||
}
|
||||
gtk_widget_show (clist);
|
||||
|
||||
return clist;
|
||||
}
|
||||
|
||||
int
|
||||
gtkutil_clist_selection (GtkWidget * clist)
|
||||
{
|
||||
if (GTK_CLIST (clist)->selection)
|
||||
return GPOINTER_TO_INT(GTK_CLIST (clist)->selection->data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
int_compare (const int * elem1, const int * elem2)
|
||||
{
|
||||
return (*elem1) - (*elem2);
|
||||
}
|
||||
|
||||
int
|
||||
gtkutil_clist_multiple_selection (GtkWidget * clist, int ** rows, const int max_rows)
|
||||
{
|
||||
int i = 0;
|
||||
GList *tmp_clist;
|
||||
*rows = malloc (sizeof (int) * max_rows );
|
||||
memset( *rows, -1, max_rows * sizeof(int) );
|
||||
|
||||
for( tmp_clist = GTK_CLIST(clist)->selection;
|
||||
tmp_clist && i < max_rows; tmp_clist = tmp_clist->next, i++)
|
||||
{
|
||||
(*rows)[i] = GPOINTER_TO_INT( tmp_clist->data );
|
||||
}
|
||||
qsort(*rows, i, sizeof(int), (void *)int_compare);
|
||||
return i;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
add_tip (GtkWidget * wid, char *text)
|
||||
{
|
||||
static GtkTooltips *tip = NULL;
|
||||
if (!tip)
|
||||
tip = gtk_tooltips_new ();
|
||||
gtk_tooltips_set_tip (tip, wid, text, 0);
|
||||
}
|
||||
|
||||
void
|
||||
show_and_unfocus (GtkWidget * wid)
|
||||
{
|
||||
GTK_WIDGET_UNSET_FLAGS (wid, GTK_CAN_FOCUS);
|
||||
gtk_widget_show (wid);
|
||||
}
|
||||
|
||||
void
|
||||
gtkutil_set_icon (GtkWidget *win)
|
||||
{
|
||||
gtk_window_set_icon (GTK_WINDOW (win), pix_xchat);
|
||||
}
|
||||
|
||||
extern GtkWidget *parent_window; /* maingui.c */
|
||||
|
||||
GtkWidget *
|
||||
gtkutil_window_new (char *title, char *role, int width, int height, int flags)
|
||||
{
|
||||
GtkWidget *win;
|
||||
|
||||
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtkutil_set_icon (win);
|
||||
#ifdef WIN32
|
||||
gtk_window_set_wmclass (GTK_WINDOW (win), "XChat", "xchat");
|
||||
#endif
|
||||
gtk_window_set_title (GTK_WINDOW (win), title);
|
||||
gtk_window_set_default_size (GTK_WINDOW (win), width, height);
|
||||
gtk_window_set_role (GTK_WINDOW (win), role);
|
||||
if (flags & 1)
|
||||
gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_MOUSE);
|
||||
if ((flags & 2) && parent_window)
|
||||
{
|
||||
gtk_window_set_type_hint (GTK_WINDOW (win), GDK_WINDOW_TYPE_HINT_DIALOG);
|
||||
gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (parent_window));
|
||||
}
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
/* pass NULL as selection to paste to both clipboard & X11 text */
|
||||
void
|
||||
gtkutil_copy_to_clipboard (GtkWidget *widget, GdkAtom selection,
|
||||
const gchar *str)
|
||||
{
|
||||
GtkWidget *win;
|
||||
GtkClipboard *clip, *clip2;
|
||||
|
||||
win = gtk_widget_get_toplevel (GTK_WIDGET (widget));
|
||||
if (GTK_WIDGET_TOPLEVEL (win))
|
||||
{
|
||||
int len = strlen (str);
|
||||
|
||||
if (selection)
|
||||
{
|
||||
clip = gtk_widget_get_clipboard (win, selection);
|
||||
gtk_clipboard_set_text (clip, str, len);
|
||||
} else
|
||||
{
|
||||
/* copy to both primary X selection and clipboard */
|
||||
clip = gtk_widget_get_clipboard (win, GDK_SELECTION_PRIMARY);
|
||||
clip2 = gtk_widget_get_clipboard (win, GDK_SELECTION_CLIPBOARD);
|
||||
gtk_clipboard_set_text (clip, str, len);
|
||||
gtk_clipboard_set_text (clip2, str, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Treeview util functions */
|
||||
|
||||
GtkWidget *
|
||||
gtkutil_treeview_new (GtkWidget *box, GtkTreeModel *model,
|
||||
GtkTreeCellDataFunc mapper, ...)
|
||||
{
|
||||
GtkWidget *win, *view;
|
||||
GtkCellRenderer *renderer = NULL;
|
||||
GtkTreeViewColumn *col;
|
||||
va_list args;
|
||||
int col_id = 0;
|
||||
GType type;
|
||||
char *title, *attr;
|
||||
|
||||
win = gtk_scrolled_window_new (0, 0);
|
||||
gtk_container_add (GTK_CONTAINER (box), win);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (win),
|
||||
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
||||
gtk_widget_show (win);
|
||||
|
||||
view = gtk_tree_view_new_with_model (model);
|
||||
/* the view now has a ref on the model, we can unref it */
|
||||
g_object_unref (G_OBJECT (model));
|
||||
gtk_container_add (GTK_CONTAINER (win), view);
|
||||
|
||||
va_start (args, mapper);
|
||||
for (col_id = va_arg (args, int); col_id != -1; col_id = va_arg (args, int))
|
||||
{
|
||||
type = gtk_tree_model_get_column_type (model, col_id);
|
||||
switch (type)
|
||||
{
|
||||
case G_TYPE_BOOLEAN:
|
||||
renderer = gtk_cell_renderer_toggle_new ();
|
||||
attr = "active";
|
||||
break;
|
||||
case G_TYPE_STRING: /* fall through */
|
||||
default:
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
attr = "text";
|
||||
break;
|
||||
}
|
||||
|
||||
title = va_arg (args, char *);
|
||||
if (mapper) /* user-specified function to set renderer attributes */
|
||||
{
|
||||
col = gtk_tree_view_column_new_with_attributes (title, renderer, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func (col, renderer, mapper,
|
||||
GINT_TO_POINTER (col_id), NULL);
|
||||
} else
|
||||
{
|
||||
/* just set the typical attribute for this type of renderer */
|
||||
col = gtk_tree_view_column_new_with_attributes (title, renderer,
|
||||
attr, col_id, NULL);
|
||||
}
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);
|
||||
}
|
||||
|
||||
va_end (args);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtkutil_treemodel_string_to_iter (GtkTreeModel *model, gchar *pathstr, GtkTreeIter *iter_ret)
|
||||
{
|
||||
GtkTreePath *path = gtk_tree_path_new_from_string (pathstr);
|
||||
gboolean success;
|
||||
|
||||
success = gtk_tree_model_get_iter (model, iter_ret, path);
|
||||
gtk_tree_path_free (path);
|
||||
return success;
|
||||
}
|
||||
|
||||
/*gboolean
|
||||
gtkutil_treeview_get_selected_iter (GtkTreeView *view, GtkTreeIter *iter_ret)
|
||||
{
|
||||
GtkTreeModel *store;
|
||||
GtkTreeSelection *select;
|
||||
|
||||
select = gtk_tree_view_get_selection (view);
|
||||
return gtk_tree_selection_get_selected (select, &store, iter_ret);
|
||||
}*/
|
||||
|
||||
gboolean
|
||||
gtkutil_treeview_get_selected (GtkTreeView *view, GtkTreeIter *iter_ret, ...)
|
||||
{
|
||||
GtkTreeModel *store;
|
||||
GtkTreeSelection *select;
|
||||
gboolean has_selected;
|
||||
va_list args;
|
||||
|
||||
select = gtk_tree_view_get_selection (view);
|
||||
has_selected = gtk_tree_selection_get_selected (select, &store, iter_ret);
|
||||
|
||||
if (has_selected) {
|
||||
va_start (args, iter_ret);
|
||||
gtk_tree_model_get_valist (store, iter_ret, args);
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
return has_selected;
|
||||
}
|
||||
|
||||
39
src/fe-gtk/gtkutil.h
Normal file
39
src/fe-gtk/gtkutil.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <gtk/gtktreeview.h>
|
||||
#include <gtk/gtktreemodel.h>
|
||||
|
||||
typedef void (*filereqcallback) (void *, char *file);
|
||||
|
||||
#define FRF_WRITE 1
|
||||
#define FRF_MULTIPLE 2
|
||||
#define FRF_ADDFOLDER 4
|
||||
#define FRF_CHOOSEFOLDER 8
|
||||
#define FRF_FILTERISINITIAL 16
|
||||
#define FRF_NOASKOVERWRITE 32
|
||||
|
||||
void gtkutil_file_req (const char *title, void *callback, void *userdata, char *filter, int flags);
|
||||
void gtkutil_destroy (GtkWidget * igad, GtkWidget * dgad);
|
||||
GtkWidget *gtkutil_button (GtkWidget *box, char *stock, char *tip, void *callback,
|
||||
void *userdata, char *labeltext);
|
||||
void gtkutil_label_new (char *text, GtkWidget * box);
|
||||
GtkWidget *gtkutil_entry_new (int max, GtkWidget * box, void *callback,
|
||||
gpointer userdata);
|
||||
GtkWidget *gtkutil_clist_new (int columns, char *titles[], GtkWidget * box,
|
||||
int policy, void *select_callback,
|
||||
gpointer select_userdata,
|
||||
void *unselect_callback,
|
||||
gpointer unselect_userdata, int selection_mode);
|
||||
int gtkutil_clist_selection (GtkWidget * clist);
|
||||
int gtkutil_clist_multiple_selection (GtkWidget * clist,
|
||||
int ** rows, const int max_rows);
|
||||
void add_tip (GtkWidget * wid, char *text);
|
||||
void show_and_unfocus (GtkWidget * wid);
|
||||
void gtkutil_set_icon (GtkWidget *win);
|
||||
GtkWidget *gtkutil_window_new (char *title, char *role, int width, int height, int flags);
|
||||
void gtkutil_copy_to_clipboard (GtkWidget *widget, GdkAtom selection,
|
||||
const gchar *str);
|
||||
GtkWidget *gtkutil_treeview_new (GtkWidget *box, GtkTreeModel *model,
|
||||
GtkTreeCellDataFunc mapper, ...);
|
||||
gboolean gtkutil_treemodel_string_to_iter (GtkTreeModel *model, gchar *pathstr, GtkTreeIter *iter_ret);
|
||||
gboolean gtkutil_treeview_get_selected_iter (GtkTreeView *view, GtkTreeIter *iter_ret);
|
||||
gboolean gtkutil_treeview_get_selected (GtkTreeView *view, GtkTreeIter *iter_ret, ...);
|
||||
|
||||
449
src/fe-gtk/ignoregui.c
Normal file
449
src/fe-gtk/ignoregui.c
Normal file
@@ -0,0 +1,449 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkcheckbutton.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkhbbox.h>
|
||||
#include <gtk/gtkframe.h>
|
||||
#include <gtk/gtkhseparator.h>
|
||||
#include <gtk/gtkversion.h>
|
||||
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gtk/gtktreeview.h>
|
||||
#include <gtk/gtktreeselection.h>
|
||||
#include <gtk/gtkcellrenderertext.h>
|
||||
#include <gtk/gtkcellrenderertoggle.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/ignore.h"
|
||||
#include "../common/cfgfiles.h"
|
||||
#include "../common/fe.h"
|
||||
#include "gtkutil.h"
|
||||
#include "maingui.h"
|
||||
|
||||
enum
|
||||
{
|
||||
MASK_COLUMN,
|
||||
CHAN_COLUMN,
|
||||
PRIV_COLUMN,
|
||||
NOTICE_COLUMN,
|
||||
CTCP_COLUMN,
|
||||
DCC_COLUMN,
|
||||
INVITE_COLUMN,
|
||||
UNIGNORE_COLUMN,
|
||||
N_COLUMNS
|
||||
};
|
||||
|
||||
static GtkWidget *ignorewin = 0;
|
||||
|
||||
static GtkWidget *num_ctcp;
|
||||
static GtkWidget *num_priv;
|
||||
static GtkWidget *num_chan;
|
||||
static GtkWidget *num_noti;
|
||||
static GtkWidget *num_invi;
|
||||
|
||||
static GtkTreeModel *
|
||||
get_store (void)
|
||||
{
|
||||
return gtk_tree_view_get_model (g_object_get_data (G_OBJECT (ignorewin), "view"));
|
||||
}
|
||||
|
||||
static int
|
||||
ignore_get_flags (GtkTreeModel *model, GtkTreeIter *iter)
|
||||
{
|
||||
gboolean chan, priv, noti, ctcp, dcc, invi, unig;
|
||||
int flags = 0;
|
||||
|
||||
gtk_tree_model_get (model, iter, 1, &chan, 2, &priv, 3, ¬i,
|
||||
4, &ctcp, 5, &dcc, 6, &invi, 7, &unig, -1);
|
||||
if (chan)
|
||||
flags |= IG_CHAN;
|
||||
if (priv)
|
||||
flags |= IG_PRIV;
|
||||
if (noti)
|
||||
flags |= IG_NOTI;
|
||||
if (ctcp)
|
||||
flags |= IG_CTCP;
|
||||
if (dcc)
|
||||
flags |= IG_DCC;
|
||||
if (invi)
|
||||
flags |= IG_INVI;
|
||||
if (unig)
|
||||
flags |= IG_UNIG;
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void
|
||||
mask_edited (GtkCellRendererText *render, gchar *path, gchar *new, gpointer dat)
|
||||
{
|
||||
GtkListStore *store = GTK_LIST_STORE (get_store ());
|
||||
GtkTreeIter iter;
|
||||
char *old;
|
||||
int flags;
|
||||
|
||||
gtkutil_treemodel_string_to_iter (GTK_TREE_MODEL (store), path, &iter);
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &old, -1);
|
||||
|
||||
if (!strcmp (old, new)) /* no change */
|
||||
;
|
||||
else if (ignore_exists (new)) /* duplicate, ignore */
|
||||
fe_message (_("That mask already exists."), FE_MSG_ERROR);
|
||||
else
|
||||
{
|
||||
/* delete old mask, and add new one with original flags */
|
||||
ignore_del (old, NULL);
|
||||
flags = ignore_get_flags (GTK_TREE_MODEL (store), &iter);
|
||||
ignore_add (new, flags);
|
||||
|
||||
/* update tree */
|
||||
gtk_list_store_set (store, &iter, MASK_COLUMN, new, -1);
|
||||
}
|
||||
g_free (old);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
option_toggled (GtkCellRendererToggle *render, gchar *path, gpointer data)
|
||||
{
|
||||
GtkListStore *store = GTK_LIST_STORE (get_store ());
|
||||
GtkTreeIter iter;
|
||||
int col_id = GPOINTER_TO_INT (data);
|
||||
gboolean active;
|
||||
char *mask;
|
||||
int flags;
|
||||
|
||||
gtkutil_treemodel_string_to_iter (GTK_TREE_MODEL (store), path, &iter);
|
||||
|
||||
/* update model */
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, col_id, &active, -1);
|
||||
gtk_list_store_set (store, &iter, col_id, !active, -1);
|
||||
|
||||
/* update ignore list */
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &mask, -1);
|
||||
flags = ignore_get_flags (GTK_TREE_MODEL (store), &iter);
|
||||
if (ignore_add (mask, flags) != 2)
|
||||
g_warning ("ignore treeview is out of sync!\n");
|
||||
|
||||
g_free (mask);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
ignore_treeview_new (GtkWidget *box)
|
||||
{
|
||||
GtkListStore *store;
|
||||
GtkWidget *view;
|
||||
GtkTreeViewColumn *col;
|
||||
GtkCellRenderer *render;
|
||||
int col_id;
|
||||
|
||||
store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING,
|
||||
G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
|
||||
G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
|
||||
G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
|
||||
G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
|
||||
g_return_val_if_fail (store != NULL, NULL);
|
||||
|
||||
view = gtkutil_treeview_new (box, GTK_TREE_MODEL (store),
|
||||
NULL,
|
||||
MASK_COLUMN, _("Mask"),
|
||||
CHAN_COLUMN, _("Channel"),
|
||||
PRIV_COLUMN, _("Private"),
|
||||
NOTICE_COLUMN, _("Notice"),
|
||||
CTCP_COLUMN, _("CTCP"),
|
||||
DCC_COLUMN, _("DCC"),
|
||||
INVITE_COLUMN, _("Invite"),
|
||||
UNIGNORE_COLUMN, _("Unignore"),
|
||||
-1);
|
||||
|
||||
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
|
||||
gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (view), 0), TRUE);
|
||||
|
||||
/* attach to signals and customise columns */
|
||||
for (col_id=0; (col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), col_id));
|
||||
col_id++)
|
||||
{
|
||||
GList *list = gtk_tree_view_column_get_cell_renderers (col);
|
||||
GList *tmp;
|
||||
|
||||
for (tmp = list; tmp; tmp = tmp->next)
|
||||
{
|
||||
render = tmp->data;
|
||||
if (col_id > 0) /* it's a toggle button column */
|
||||
{
|
||||
g_signal_connect (render, "toggled", G_CALLBACK (option_toggled),
|
||||
GINT_TO_POINTER (col_id));
|
||||
} else /* mask column */
|
||||
{
|
||||
g_object_set (G_OBJECT (render), "editable", TRUE, NULL);
|
||||
g_signal_connect (render, "edited", G_CALLBACK (mask_edited), NULL);
|
||||
/* make this column sortable */
|
||||
gtk_tree_view_column_set_sort_column_id (col, col_id);
|
||||
gtk_tree_view_column_set_min_width (col, 272);
|
||||
}
|
||||
/* centre titles */
|
||||
gtk_tree_view_column_set_alignment (col, 0.5);
|
||||
}
|
||||
|
||||
g_list_free (list);
|
||||
}
|
||||
|
||||
gtk_widget_show (view);
|
||||
return view;
|
||||
}
|
||||
|
||||
static void
|
||||
ignore_delete_entry_clicked (GtkWidget * wid, struct session *sess)
|
||||
{
|
||||
GtkTreeView *view = g_object_get_data (G_OBJECT (ignorewin), "view");
|
||||
GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
|
||||
GtkTreeIter iter;
|
||||
GtkTreePath *path;
|
||||
char *mask = NULL;
|
||||
|
||||
if (gtkutil_treeview_get_selected (view, &iter, 0, &mask, -1))
|
||||
{
|
||||
/* delete this row, select next one */
|
||||
#if (GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION == 0)
|
||||
gtk_list_store_remove (store, &iter);
|
||||
#else
|
||||
if (gtk_list_store_remove (store, &iter))
|
||||
{
|
||||
path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
|
||||
gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 1.0, 0.0);
|
||||
gtk_tree_view_set_cursor (view, path, NULL, FALSE);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
#endif
|
||||
|
||||
ignore_del (mask, NULL);
|
||||
g_free (mask);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ignore_store_new (int cancel, char *mask, gpointer data)
|
||||
{
|
||||
GtkTreeView *view = g_object_get_data (G_OBJECT (ignorewin), "view");
|
||||
GtkListStore *store = GTK_LIST_STORE (get_store ());
|
||||
GtkTreeIter iter;
|
||||
GtkTreePath *path;
|
||||
int flags = IG_CHAN | IG_PRIV | IG_NOTI | IG_CTCP | IG_DCC | IG_INVI;
|
||||
|
||||
if (cancel)
|
||||
return;
|
||||
/* check if it already exists */
|
||||
if (ignore_exists (mask))
|
||||
{
|
||||
fe_message (_("That mask already exists."), FE_MSG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ignore_add (mask, flags);
|
||||
|
||||
gtk_list_store_append (store, &iter);
|
||||
/* ignore everything by default */
|
||||
gtk_list_store_set (store, &iter, 0, mask, 1, TRUE, 2, TRUE, 3, TRUE,
|
||||
4, TRUE, 5, TRUE, 6, TRUE, 7, FALSE, -1);
|
||||
/* make sure the new row is visible and selected */
|
||||
path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
|
||||
gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 1.0, 0.0);
|
||||
gtk_tree_view_set_cursor (view, path, NULL, FALSE);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
|
||||
static void
|
||||
ignore_clear_entry_clicked (GtkWidget * wid, gpointer unused)
|
||||
{
|
||||
GtkListStore *store = GTK_LIST_STORE (get_store ());
|
||||
GtkTreeIter iter;
|
||||
char *mask;
|
||||
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
|
||||
{
|
||||
/* remove from ignore_list */
|
||||
do
|
||||
{
|
||||
mask = NULL;
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, MASK_COLUMN, &mask, -1);
|
||||
ignore_del (mask, NULL);
|
||||
g_free (mask);
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
|
||||
|
||||
/* remove from GUI */
|
||||
gtk_list_store_clear (store);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ignore_new_entry_clicked (GtkWidget * wid, struct session *sess)
|
||||
{
|
||||
fe_get_str (_("Enter mask to ignore:"), "nick!userid@host.com",
|
||||
ignore_store_new, NULL);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
close_ignore_gui_callback ()
|
||||
{
|
||||
ignore_save ();
|
||||
ignorewin = 0;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
ignore_stats_entry (GtkWidget * box, char *label, int value)
|
||||
{
|
||||
GtkWidget *wid;
|
||||
char buf[16];
|
||||
|
||||
sprintf (buf, "%d", value);
|
||||
gtkutil_label_new (label, box);
|
||||
wid = gtkutil_entry_new (16, box, 0, 0);
|
||||
gtk_widget_set_size_request (wid, 30, -1);
|
||||
gtk_editable_set_editable (GTK_EDITABLE (wid), FALSE);
|
||||
gtk_widget_set_sensitive (GTK_WIDGET (wid), FALSE);
|
||||
gtk_entry_set_text (GTK_ENTRY (wid), buf);
|
||||
|
||||
return wid;
|
||||
}
|
||||
|
||||
void
|
||||
ignore_gui_open ()
|
||||
{
|
||||
GtkWidget *vbox, *box, *stat_box, *frame;
|
||||
GtkWidget *view;
|
||||
GtkListStore *store;
|
||||
GtkTreeIter iter;
|
||||
GSList *temp = ignore_list;
|
||||
char *mask;
|
||||
gboolean private, chan, notice, ctcp, dcc, invite, unignore;
|
||||
|
||||
if (ignorewin)
|
||||
{
|
||||
mg_bring_tofront (ignorewin);
|
||||
return;
|
||||
}
|
||||
|
||||
ignorewin =
|
||||
mg_create_generic_tab ("IgnoreList", _("XChat: Ignore list"),
|
||||
FALSE, TRUE, close_ignore_gui_callback,
|
||||
NULL, 600, 256, &vbox, 0);
|
||||
|
||||
view = ignore_treeview_new (vbox);
|
||||
g_object_set_data (G_OBJECT (ignorewin), "view", view);
|
||||
|
||||
frame = gtk_frame_new (_("Ignore Stats:"));
|
||||
gtk_widget_show (frame);
|
||||
|
||||
stat_box = gtk_hbox_new (0, 2);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (stat_box), 6);
|
||||
gtk_container_add (GTK_CONTAINER (frame), stat_box);
|
||||
gtk_widget_show (stat_box);
|
||||
|
||||
num_chan = ignore_stats_entry (stat_box, _("Channel:"), ignored_chan);
|
||||
num_priv = ignore_stats_entry (stat_box, _("Private:"), ignored_priv);
|
||||
num_noti = ignore_stats_entry (stat_box, _("Notice:"), ignored_noti);
|
||||
num_ctcp = ignore_stats_entry (stat_box, _("CTCP:"), ignored_ctcp);
|
||||
num_invi = ignore_stats_entry (stat_box, _("Invite:"), ignored_invi);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (vbox), frame, 0, 0, 5);
|
||||
|
||||
box = gtk_hbutton_box_new ();
|
||||
gtk_button_box_set_layout (GTK_BUTTON_BOX (box), GTK_BUTTONBOX_SPREAD);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 2);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (box), 5);
|
||||
gtk_widget_show (box);
|
||||
|
||||
gtkutil_button (box, GTK_STOCK_NEW, 0, ignore_new_entry_clicked, 0,
|
||||
_("Add..."));
|
||||
gtkutil_button (box, GTK_STOCK_DELETE, 0, ignore_delete_entry_clicked,
|
||||
0, _("Delete"));
|
||||
gtkutil_button (box, GTK_STOCK_CLEAR, 0, ignore_clear_entry_clicked,
|
||||
0, _("Clear"));
|
||||
|
||||
store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
|
||||
|
||||
while (temp)
|
||||
{
|
||||
struct ignore *ignore = temp->data;
|
||||
|
||||
mask = ignore->mask;
|
||||
chan = (ignore->type & IG_CHAN);
|
||||
private = (ignore->type & IG_PRIV);
|
||||
notice = (ignore->type & IG_NOTI);
|
||||
ctcp = (ignore->type & IG_CTCP);
|
||||
dcc = (ignore->type & IG_DCC);
|
||||
invite = (ignore->type & IG_INVI);
|
||||
unignore = (ignore->type & IG_UNIG);
|
||||
|
||||
gtk_list_store_append (store, &iter);
|
||||
gtk_list_store_set (store, &iter,
|
||||
MASK_COLUMN, mask,
|
||||
CHAN_COLUMN, chan,
|
||||
PRIV_COLUMN, private,
|
||||
NOTICE_COLUMN, notice,
|
||||
CTCP_COLUMN, ctcp,
|
||||
DCC_COLUMN, dcc,
|
||||
INVITE_COLUMN, invite,
|
||||
UNIGNORE_COLUMN, unignore,
|
||||
-1);
|
||||
|
||||
temp = temp->next;
|
||||
}
|
||||
gtk_widget_show (ignorewin);
|
||||
}
|
||||
|
||||
void
|
||||
fe_ignore_update (int level)
|
||||
{
|
||||
/* some ignores have changed via /ignore, we should update
|
||||
the gui now */
|
||||
/* level 1 = the list only. */
|
||||
/* level 2 = the numbers only. */
|
||||
/* for now, ignore level 1, since the ignore GUI isn't realtime,
|
||||
only saved when you click OK */
|
||||
char buf[16];
|
||||
|
||||
if (level == 2 && ignorewin)
|
||||
{
|
||||
sprintf (buf, "%d", ignored_ctcp);
|
||||
gtk_entry_set_text (GTK_ENTRY (num_ctcp), buf);
|
||||
|
||||
sprintf (buf, "%d", ignored_noti);
|
||||
gtk_entry_set_text (GTK_ENTRY (num_noti), buf);
|
||||
|
||||
sprintf (buf, "%d", ignored_chan);
|
||||
gtk_entry_set_text (GTK_ENTRY (num_chan), buf);
|
||||
|
||||
sprintf (buf, "%d", ignored_invi);
|
||||
gtk_entry_set_text (GTK_ENTRY (num_invi), buf);
|
||||
|
||||
sprintf (buf, "%d", ignored_priv);
|
||||
gtk_entry_set_text (GTK_ENTRY (num_priv), buf);
|
||||
}
|
||||
}
|
||||
257
src/fe-gtk/joind.c
Normal file
257
src/fe-gtk/joind.c
Normal file
@@ -0,0 +1,257 @@
|
||||
/* Copyright (c) 2005 Peter Zelezny
|
||||
All Rights Reserved.
|
||||
|
||||
joind.c - The Join Dialog.
|
||||
|
||||
Popups up when you connect without any autojoin channels and helps you
|
||||
to find or join a channel.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <gtk/gtkbbox.h>
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtkdialog.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkimage.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtkradiobutton.h>
|
||||
#include <gtk/gtkvbox.h>
|
||||
#include <gtk/gtkwindow.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "../common/server.h"
|
||||
#include "../common/fe.h"
|
||||
#include "fe-gtk.h"
|
||||
#include "chanlist.h"
|
||||
|
||||
|
||||
static void
|
||||
joind_radio2_cb (GtkWidget *radio, server *serv)
|
||||
{
|
||||
if (GTK_TOGGLE_BUTTON (radio)->active)
|
||||
{
|
||||
gtk_widget_grab_focus (serv->gui->joind_entry);
|
||||
gtk_editable_set_position (GTK_EDITABLE (serv->gui->joind_entry), 999);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
joind_entryenter_cb (GtkWidget *entry, GtkWidget *ok)
|
||||
{
|
||||
gtk_widget_grab_focus (ok);
|
||||
}
|
||||
|
||||
static void
|
||||
joind_entryfocus_cb (GtkWidget *entry, GdkEventFocus *event, server *serv)
|
||||
{
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (serv->gui->joind_radio2), TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
joind_destroy_cb (GtkWidget *win, server *serv)
|
||||
{
|
||||
if (is_server (serv))
|
||||
serv->gui->joind_win = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
joind_ok_cb (GtkWidget *ok, server *serv)
|
||||
{
|
||||
if (!is_server (serv))
|
||||
{
|
||||
gtk_widget_destroy (gtk_widget_get_toplevel (ok));
|
||||
return;
|
||||
}
|
||||
|
||||
/* do nothing */
|
||||
if (GTK_TOGGLE_BUTTON (serv->gui->joind_radio1)->active)
|
||||
goto xit;
|
||||
|
||||
/* join specific channel */
|
||||
if (GTK_TOGGLE_BUTTON (serv->gui->joind_radio2)->active)
|
||||
{
|
||||
char *text = GTK_ENTRY (serv->gui->joind_entry)->text;
|
||||
if (strlen (text) < 2)
|
||||
{
|
||||
fe_message (_("Channel name too short, try again."), FE_MSG_ERROR);
|
||||
return;
|
||||
}
|
||||
serv->p_join (serv, text, "");
|
||||
goto xit;
|
||||
}
|
||||
|
||||
/* channel list */
|
||||
chanlist_opengui (serv, TRUE);
|
||||
|
||||
xit:
|
||||
prefs.gui_join_dialog = 0;
|
||||
if (GTK_TOGGLE_BUTTON (serv->gui->joind_check)->active)
|
||||
prefs.gui_join_dialog = 1;
|
||||
|
||||
gtk_widget_destroy (serv->gui->joind_win);
|
||||
serv->gui->joind_win = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
joind_show_dialog (server *serv)
|
||||
{
|
||||
GtkWidget *dialog1;
|
||||
GtkWidget *dialog_vbox1;
|
||||
GtkWidget *vbox1;
|
||||
GtkWidget *hbox1;
|
||||
GtkWidget *image1;
|
||||
GtkWidget *vbox2;
|
||||
GtkWidget *label;
|
||||
GtkWidget *radiobutton1;
|
||||
GtkWidget *radiobutton2;
|
||||
GtkWidget *radiobutton3;
|
||||
GSList *radiobutton1_group;
|
||||
GtkWidget *hbox2;
|
||||
GtkWidget *entry1;
|
||||
GtkWidget *checkbutton1;
|
||||
GtkWidget *dialog_action_area1;
|
||||
GtkWidget *okbutton1;
|
||||
char buf[256];
|
||||
char buf2[256];
|
||||
|
||||
serv->gui->joind_win = dialog1 = gtk_dialog_new ();
|
||||
gtk_window_set_title (GTK_WINDOW (dialog1), _("XChat: Connection Complete"));
|
||||
gtk_window_set_type_hint (GTK_WINDOW (dialog1), GDK_WINDOW_TYPE_HINT_DIALOG);
|
||||
gtk_window_set_position (GTK_WINDOW (dialog1), GTK_WIN_POS_MOUSE);
|
||||
|
||||
dialog_vbox1 = GTK_DIALOG (dialog1)->vbox;
|
||||
gtk_widget_show (dialog_vbox1);
|
||||
|
||||
vbox1 = gtk_vbox_new (FALSE, 0);
|
||||
gtk_widget_show (vbox1);
|
||||
gtk_box_pack_start (GTK_BOX (dialog_vbox1), vbox1, TRUE, TRUE, 0);
|
||||
|
||||
hbox1 = gtk_hbox_new (FALSE, 0);
|
||||
gtk_widget_show (hbox1);
|
||||
gtk_box_pack_start (GTK_BOX (vbox1), hbox1, TRUE, TRUE, 0);
|
||||
|
||||
image1 = gtk_image_new_from_stock ("gtk-yes", GTK_ICON_SIZE_DIALOG);
|
||||
gtk_widget_show (image1);
|
||||
gtk_box_pack_start (GTK_BOX (hbox1), image1, FALSE, TRUE, 24);
|
||||
gtk_misc_set_alignment (GTK_MISC (image1), 0.5, 0.06);
|
||||
|
||||
vbox2 = gtk_vbox_new (FALSE, 10);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (vbox2), 6);
|
||||
gtk_widget_show (vbox2);
|
||||
gtk_box_pack_start (GTK_BOX (hbox1), vbox2, TRUE, TRUE, 0);
|
||||
|
||||
snprintf (buf2, sizeof (buf2), _("Connection to %s complete."),
|
||||
server_get_network (serv, TRUE));
|
||||
snprintf (buf, sizeof (buf), "\n<b>%s</b>", buf2);
|
||||
label = gtk_label_new (buf);
|
||||
gtk_widget_show (label);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
|
||||
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
|
||||
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
|
||||
|
||||
label = gtk_label_new (_("In the Server-List window, no channel (chat room) has been entered to be automatically joined for this network."));
|
||||
gtk_widget_show (label);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
|
||||
GTK_LABEL (label)->wrap = TRUE;
|
||||
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
|
||||
|
||||
label = gtk_label_new (_("What would you like to do next?"));
|
||||
gtk_widget_show (label);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
|
||||
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
|
||||
|
||||
serv->gui->joind_radio1 = radiobutton1 = gtk_radio_button_new_with_mnemonic (NULL, _("_Nothing, I'll join a channel later."));
|
||||
gtk_widget_show (radiobutton1);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), radiobutton1, FALSE, FALSE, 0);
|
||||
radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton1));
|
||||
|
||||
hbox2 = gtk_hbox_new (FALSE, 0);
|
||||
gtk_widget_show (hbox2);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), hbox2, FALSE, FALSE, 0);
|
||||
|
||||
serv->gui->joind_radio2 = radiobutton2 = gtk_radio_button_new_with_mnemonic (NULL, _("_Join this channel:"));
|
||||
gtk_widget_show (radiobutton2);
|
||||
gtk_box_pack_start (GTK_BOX (hbox2), radiobutton2, FALSE, FALSE, 0);
|
||||
gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton2), radiobutton1_group);
|
||||
radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton2));
|
||||
|
||||
serv->gui->joind_entry = entry1 = gtk_entry_new ();
|
||||
gtk_entry_set_text (GTK_ENTRY (entry1), "#");
|
||||
gtk_widget_show (entry1);
|
||||
gtk_box_pack_start (GTK_BOX (hbox2), entry1, TRUE, TRUE, 8);
|
||||
|
||||
snprintf (buf, sizeof (buf), "<small> %s</small>",
|
||||
_("If you know the name of the channel you want to join, enter it here."));
|
||||
label = gtk_label_new (buf);
|
||||
gtk_widget_show (label);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
|
||||
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
|
||||
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
|
||||
|
||||
radiobutton3 = gtk_radio_button_new_with_mnemonic (NULL, _("O_pen the Channel-List window."));
|
||||
gtk_widget_show (radiobutton3);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), radiobutton3, FALSE, FALSE, 0);
|
||||
gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton3), radiobutton1_group);
|
||||
radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton3));
|
||||
|
||||
snprintf (buf, sizeof (buf), "<small> %s</small>",
|
||||
_("Retrieving the Channel-List may take a minute or two."));
|
||||
label = gtk_label_new (buf);
|
||||
gtk_widget_show (label);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
|
||||
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
|
||||
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
|
||||
|
||||
serv->gui->joind_check = checkbutton1 = gtk_check_button_new_with_mnemonic (_("_Always show this dialog after connecting."));
|
||||
if (prefs.gui_join_dialog)
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton1), TRUE);
|
||||
gtk_widget_show (checkbutton1);
|
||||
gtk_box_pack_start (GTK_BOX (vbox1), checkbutton1, FALSE, FALSE, 0);
|
||||
|
||||
dialog_action_area1 = GTK_DIALOG (dialog1)->action_area;
|
||||
gtk_widget_show (dialog_action_area1);
|
||||
gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1), GTK_BUTTONBOX_END);
|
||||
|
||||
okbutton1 = gtk_button_new_from_stock ("gtk-ok");
|
||||
gtk_widget_show (okbutton1);
|
||||
gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog1)->action_area), okbutton1, FALSE, TRUE, 0);
|
||||
GTK_WIDGET_SET_FLAGS (okbutton1, GTK_CAN_DEFAULT);
|
||||
|
||||
g_signal_connect (G_OBJECT (dialog1), "destroy",
|
||||
G_CALLBACK (joind_destroy_cb), serv);
|
||||
g_signal_connect (G_OBJECT (entry1), "focus_in_event",
|
||||
G_CALLBACK (joind_entryfocus_cb), serv);
|
||||
g_signal_connect (G_OBJECT (entry1), "activate",
|
||||
G_CALLBACK (joind_entryenter_cb), okbutton1);
|
||||
g_signal_connect (G_OBJECT (radiobutton2), "toggled",
|
||||
G_CALLBACK (joind_radio2_cb), serv);
|
||||
g_signal_connect (G_OBJECT (okbutton1), "clicked",
|
||||
G_CALLBACK (joind_ok_cb), serv);
|
||||
|
||||
gtk_widget_grab_focus (okbutton1);
|
||||
gtk_widget_show_all (dialog1);
|
||||
}
|
||||
|
||||
void
|
||||
joind_open (server *serv)
|
||||
{
|
||||
if (prefs.gui_join_dialog)
|
||||
joind_show_dialog (serv);
|
||||
}
|
||||
|
||||
void
|
||||
joind_close (server *serv)
|
||||
{
|
||||
if (serv->gui->joind_win)
|
||||
{
|
||||
gtk_widget_destroy (serv->gui->joind_win);
|
||||
serv->gui->joind_win = NULL;
|
||||
}
|
||||
}
|
||||
2
src/fe-gtk/joind.h
Normal file
2
src/fe-gtk/joind.h
Normal file
@@ -0,0 +1,2 @@
|
||||
void joind_open (server *serv);
|
||||
void joind_close (server *serv);
|
||||
3811
src/fe-gtk/maingui.c
Normal file
3811
src/fe-gtk/maingui.c
Normal file
File diff suppressed because it is too large
Load Diff
33
src/fe-gtk/maingui.h
Normal file
33
src/fe-gtk/maingui.h
Normal file
@@ -0,0 +1,33 @@
|
||||
extern GtkStyle *input_style;
|
||||
extern GtkWidget *parent_window;
|
||||
|
||||
void mg_changui_new (session *sess, restore_gui *res, int tab, int focus);
|
||||
void mg_update_xtext (GtkWidget *wid);
|
||||
void mg_open_quit_dialog (gboolean minimize_button);
|
||||
void mg_switch_page (int relative, int num);
|
||||
void mg_move_tab (session *, int delta);
|
||||
void mg_move_tab_family (session *, int delta);
|
||||
void mg_bring_tofront (GtkWidget *vbox);
|
||||
void mg_bring_tofront_sess (session *sess);
|
||||
void mg_decide_userlist (session *sess, gboolean switch_to_current);
|
||||
void mg_set_topic_tip (session *sess);
|
||||
GtkWidget *mg_create_generic_tab (char *name, char *title, int force_toplevel, int link_buttons, void *close_callback, void *userdata, int width, int height, GtkWidget **vbox_ret, void *family);
|
||||
void mg_set_title (GtkWidget *button, char *title);
|
||||
void mg_set_access_icon (session_gui *gui, GdkPixbuf *pix, gboolean away);
|
||||
void mg_apply_setup (void);
|
||||
void mg_close_sess (session *);
|
||||
void mg_tab_close (session *sess);
|
||||
void mg_detach (session *sess, int mode);
|
||||
void mg_progressbar_create (session_gui *gui);
|
||||
void mg_progressbar_destroy (session_gui *gui);
|
||||
void mg_dnd_drop_file (session *sess, char *target, char *uri);
|
||||
void mg_change_layout (int type);
|
||||
void mg_update_meters (session_gui *);
|
||||
void mg_inputbox_cb (GtkWidget *igad, session_gui *gui);
|
||||
void mg_create_icon_item (char *label, char *stock, GtkWidget *menu, void *callback, void *userdata);
|
||||
GtkWidget *mg_submenu (GtkWidget *menu, char *text);
|
||||
/* DND */
|
||||
gboolean mg_drag_begin_cb (GtkWidget *widget, GdkDragContext *context, gpointer userdata);
|
||||
void mg_drag_end_cb (GtkWidget *widget, GdkDragContext *context, gpointer userdata);
|
||||
gboolean mg_drag_drop_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time, gpointer user_data);
|
||||
gboolean mg_drag_motion_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time, gpointer user_data);
|
||||
2270
src/fe-gtk/menu.c
Normal file
2270
src/fe-gtk/menu.c
Normal file
File diff suppressed because it is too large
Load Diff
41
src/fe-gtk/menu.h
Normal file
41
src/fe-gtk/menu.h
Normal file
@@ -0,0 +1,41 @@
|
||||
GtkWidget *menu_create_main (void *accel_group, int bar, int away, int toplevel, GtkWidget **menu_widgets);
|
||||
void menu_urlmenu (GdkEventButton * event, char *url);
|
||||
void menu_chanmenu (session *sess, GdkEventButton * event, char *chan);
|
||||
void menu_addfavoritemenu (server *serv, GtkWidget *menu, char *channel);
|
||||
void menu_nickmenu (session *sess, GdkEventButton * event, char *nick, int num_sel);
|
||||
void menu_middlemenu (session *sess, GdkEventButton *event);
|
||||
void userlist_button_cb (GtkWidget * button, char *cmd);
|
||||
void nick_command_parse (session *sess, char *cmd, char *nick, char *allnick);
|
||||
void usermenu_update (void);
|
||||
GtkWidget *menu_toggle_item (char *label, GtkWidget *menu, void *callback, void *userdata, int state);
|
||||
GtkWidget *menu_quick_item (char *cmd, char *label, GtkWidget * menu, int flags, gpointer userdata, char *icon);
|
||||
GtkWidget *menu_quick_sub (char *name, GtkWidget *menu, GtkWidget **sub_item_ret, int flags, int pos);
|
||||
GtkWidget *create_icon_menu (char *labeltext, void *stock_name, int is_stock);
|
||||
void menu_create (GtkWidget *menu, GSList *list, char *target, int check_path);
|
||||
void menu_bar_toggle (void);
|
||||
void menu_add_plugin_items (GtkWidget *menu, char *root, char *target);
|
||||
void menu_change_layout (void);
|
||||
|
||||
/* for menu_quick functions */
|
||||
#define XCMENU_DOLIST 1
|
||||
#define XCMENU_SHADED 1
|
||||
#define XCMENU_MARKUP 2
|
||||
#define XCMENU_MNEMONIC 4
|
||||
|
||||
/* menu items we keep a GtkWidget* for (to change their state) */
|
||||
#define MENU_ID_AWAY 1
|
||||
#define MENU_ID_MENUBAR 2
|
||||
#define MENU_ID_TOPICBAR 3
|
||||
#define MENU_ID_USERLIST 4
|
||||
#define MENU_ID_ULBUTTONS 5
|
||||
#define MENU_ID_MODEBUTTONS 6
|
||||
#define MENU_ID_LAYOUT_TABS 7
|
||||
#define MENU_ID_LAYOUT_TREE 8
|
||||
#define MENU_ID_DISCONNECT 9
|
||||
#define MENU_ID_RECONNECT 10
|
||||
#define MENU_ID_JOIN 11
|
||||
#define MENU_ID_USERMENU 12
|
||||
|
||||
#if (MENU_ID_NUM < MENU_ID_USERMENU)
|
||||
#error MENU_ID_NUM is set wrong
|
||||
#endif
|
||||
530
src/fe-gtk/mmx_cmod.S
Normal file
530
src/fe-gtk/mmx_cmod.S
Normal file
@@ -0,0 +1,530 @@
|
||||
/*
|
||||
* Copyright (C) 1997-2001, Michael Jennings
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies of the Software, its documentation and marketing & publicity
|
||||
* materials, and acknowledgment shall be given in the documentation, materials
|
||||
* and software packages that this Software was used.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* MMX routines for tinting XImages written by Willem Monsuwe <willem@stack.nl> */
|
||||
|
||||
/* Function calling conventions:
|
||||
* shade_ximage_xx(void *data, int bpl, int w, int h, int rm, int gm, int bm);
|
||||
*/
|
||||
|
||||
#define data 8(%ebp)
|
||||
#define bpl 12(%ebp)
|
||||
#define w 16(%ebp)
|
||||
#define h 20(%ebp)
|
||||
#define rm 24(%ebp)
|
||||
#define gm 28(%ebp)
|
||||
#define bm 32(%ebp)
|
||||
|
||||
#ifdef UNDERSCORE_SYMBOLS /* need this to link with msvc */
|
||||
#define SHADE_XIMAGE_15 _shade_ximage_15_mmx
|
||||
#define SHADE_XIMAGE_16 _shade_ximage_16_mmx
|
||||
#define SHADE_XIMAGE_32 _shade_ximage_32_mmx
|
||||
#define HAVE_MMX _have_mmx
|
||||
#else
|
||||
#define SHADE_XIMAGE_15 shade_ximage_15_mmx
|
||||
#define SHADE_XIMAGE_16 shade_ximage_16_mmx
|
||||
#define SHADE_XIMAGE_32 shade_ximage_32_mmx
|
||||
#define HAVE_MMX have_mmx
|
||||
#endif
|
||||
|
||||
.globl SHADE_XIMAGE_15
|
||||
.globl SHADE_XIMAGE_16
|
||||
.globl SHADE_XIMAGE_32
|
||||
.globl HAVE_MMX
|
||||
|
||||
.bss
|
||||
.text
|
||||
.align 8
|
||||
|
||||
#define ENTER \
|
||||
pushl %ebp ;\
|
||||
movl %esp, %ebp ;\
|
||||
pushl %ebx ;\
|
||||
pushl %ecx ;\
|
||||
pushl %edx ;\
|
||||
pushl %edi ;\
|
||||
pushl %esi ;\
|
||||
movl data, %esi ;\
|
||||
movl w, %ebx ;\
|
||||
movl h, %edx
|
||||
|
||||
#define LEAVE \
|
||||
4: ;\
|
||||
emms ;\
|
||||
popl %esi ;\
|
||||
popl %edi ;\
|
||||
popl %edx ;\
|
||||
popl %ecx ;\
|
||||
popl %ebx ;\
|
||||
movl %ebp, %esp ;\
|
||||
popl %ebp ;\
|
||||
ret
|
||||
|
||||
|
||||
SHADE_XIMAGE_15:
|
||||
ENTER
|
||||
|
||||
leal -6(%esi, %ebx, 2), %esi
|
||||
negl %ebx
|
||||
jz 5f
|
||||
|
||||
/* Setup multipliers */
|
||||
movd rm, %mm5
|
||||
movd gm, %mm6
|
||||
movd bm, %mm7
|
||||
punpcklwd %mm5, %mm5 /* 00 00 00 00 rm rm rm rm */
|
||||
punpcklwd %mm6, %mm6 /* 00 00 00 00 gm gm gm gm */
|
||||
punpcklwd %mm7, %mm7 /* 00 00 00 00 bm bm bm bm */
|
||||
punpckldq %mm5, %mm5 /* rm rm rm rm rm rm rm rm */
|
||||
punpckldq %mm6, %mm6 /* gm gm gm gm gm gm gm gm */
|
||||
punpckldq %mm7, %mm7 /* bm bm bm bm bm bm bm bm */
|
||||
|
||||
cmpl $256, rm
|
||||
jg shade_ximage_15_mmx_saturate
|
||||
cmpl $256, gm
|
||||
jg shade_ximage_15_mmx_saturate
|
||||
cmpl $256, bm
|
||||
jg shade_ximage_15_mmx_saturate
|
||||
|
||||
1: movl %ebx, %ecx
|
||||
addl $3, %ecx
|
||||
jns 3f
|
||||
2:
|
||||
movq (%esi, %ecx, 2), %mm0
|
||||
|
||||
movq %mm0, %mm1 /* rg gb */
|
||||
movq %mm0, %mm2 /* rg gb */
|
||||
psrlw $5, %mm1 /* 0r rg */
|
||||
psrlw $10, %mm0 /* 00 0r */
|
||||
psllw $11, %mm2 /* b0 00 */
|
||||
psllw $11, %mm1 /* g0 00 */
|
||||
psllw $8, %mm0 /* 0r 00 */
|
||||
psrlw $3, %mm1 /* 0g 00 */
|
||||
psrlw $3, %mm2 /* 0b 00 */
|
||||
|
||||
pmulhw %mm5, %mm0 /* 00 0r */
|
||||
pmulhw %mm6, %mm1 /* 00 0g */
|
||||
pmulhw %mm7, %mm2 /* 00 0b */
|
||||
|
||||
psllw $10, %mm0 /* r0 00 */
|
||||
psllw $5, %mm1 /* 0g g0 */
|
||||
por %mm2, %mm0 /* r0 0b */
|
||||
por %mm1, %mm0 /* rg gb */
|
||||
|
||||
movq %mm0, (%esi, %ecx, 2)
|
||||
|
||||
addl $4, %ecx
|
||||
js 2b
|
||||
jmp 4f
|
||||
3:
|
||||
movw (%esi, %ecx, 2), %ax
|
||||
movd %eax, %mm0
|
||||
|
||||
movq %mm0, %mm1 /* rg gb */
|
||||
movq %mm0, %mm2 /* rg gb */
|
||||
psrlw $5, %mm1 /* 0r rg */
|
||||
psrlw $10, %mm0 /* 00 0r */
|
||||
psllw $11, %mm2 /* b0 00 */
|
||||
psllw $11, %mm1 /* g0 00 */
|
||||
psllw $8, %mm0 /* 0r 00 */
|
||||
psrlw $3, %mm1 /* 0g 00 */
|
||||
psrlw $3, %mm2 /* 0b 00 */
|
||||
|
||||
pmulhw %mm5, %mm0 /* 00 0r */
|
||||
pmulhw %mm6, %mm1 /* 00 0g */
|
||||
pmulhw %mm7, %mm2 /* 00 0b */
|
||||
|
||||
psllw $10, %mm0 /* r0 00 */
|
||||
psllw $5, %mm1 /* 0g g0 */
|
||||
por %mm2, %mm0 /* r0 0b */
|
||||
por %mm1, %mm0 /* rg gb */
|
||||
|
||||
movd %mm0, %eax
|
||||
movw %ax, (%esi, %ecx, 2)
|
||||
|
||||
incl %ecx
|
||||
4:
|
||||
cmpl $2, %ecx
|
||||
jng 3b
|
||||
|
||||
addl bpl, %esi
|
||||
decl %edx
|
||||
jnz 1b
|
||||
5:
|
||||
LEAVE
|
||||
|
||||
|
||||
shade_ximage_15_mmx_saturate:
|
||||
|
||||
pcmpeqw %mm3, %mm3
|
||||
psllw $5, %mm3 /* ff e0 ff e0 ff e0 ff e0 */
|
||||
|
||||
1: movl %ebx, %ecx
|
||||
addl $3, %ecx
|
||||
jns 3f
|
||||
2:
|
||||
movq (%esi, %ecx, 2), %mm0
|
||||
|
||||
movq %mm0, %mm1 /* rg gb */
|
||||
movq %mm0, %mm2 /* rg gb */
|
||||
psrlw $5, %mm1 /* 0r rg */
|
||||
psrlw $10, %mm0 /* 00 0r */
|
||||
psllw $11, %mm2 /* b0 00 */
|
||||
psllw $11, %mm1 /* g0 00 */
|
||||
psllw $8, %mm0 /* 0r 00 */
|
||||
psrlw $3, %mm1 /* 0g 00 */
|
||||
psrlw $3, %mm2 /* 0b 00 */
|
||||
|
||||
pmulhw %mm5, %mm0 /* xx xr */
|
||||
pmulhw %mm6, %mm1 /* xx xg */
|
||||
pmulhw %mm7, %mm2 /* xx xb */
|
||||
|
||||
/* Saturate upper */
|
||||
paddusw %mm3, %mm0 /* ff er */
|
||||
paddusw %mm3, %mm1 /* ff eg */
|
||||
paddusw %mm3, %mm2 /* ff eb */
|
||||
|
||||
psubw %mm3, %mm0 /* 00 0r */
|
||||
psubw %mm3, %mm1 /* 00 0g */
|
||||
psubw %mm3, %mm2 /* 00 0b */
|
||||
|
||||
psllw $10, %mm0 /* r0 00 */
|
||||
psllw $5, %mm1 /* 0g g0 */
|
||||
por %mm2, %mm0 /* r0 0b */
|
||||
por %mm1, %mm0 /* rg gb */
|
||||
|
||||
movq %mm0, (%esi, %ecx, 2)
|
||||
|
||||
addl $4, %ecx
|
||||
js 2b
|
||||
jmp 4f
|
||||
3:
|
||||
movw (%esi, %ecx, 2), %ax
|
||||
movd %eax, %mm0
|
||||
|
||||
movq %mm0, %mm1 /* rg gb */
|
||||
movq %mm0, %mm2 /* rg gb */
|
||||
psrlw $5, %mm1 /* 0r rg */
|
||||
psrlw $10, %mm0 /* 00 0r */
|
||||
psllw $11, %mm2 /* b0 00 */
|
||||
psllw $11, %mm1 /* g0 00 */
|
||||
psllw $8, %mm0 /* 0r 00 */
|
||||
psrlw $3, %mm1 /* 0g 00 */
|
||||
psrlw $3, %mm2 /* 0b 00 */
|
||||
|
||||
pmulhw %mm5, %mm0 /* xx xr */
|
||||
pmulhw %mm6, %mm1 /* xx xg */
|
||||
pmulhw %mm7, %mm2 /* xx xb */
|
||||
|
||||
/* Saturate upper */
|
||||
paddusw %mm3, %mm0 /* ff er */
|
||||
paddusw %mm3, %mm1 /* ff eg */
|
||||
paddusw %mm3, %mm2 /* ff eb */
|
||||
|
||||
psubw %mm3, %mm0 /* 00 0r */
|
||||
psubw %mm3, %mm1 /* 00 0g */
|
||||
psubw %mm3, %mm2 /* 00 0b */
|
||||
|
||||
psllw $10, %mm0 /* r0 00 */
|
||||
psllw $5, %mm1 /* 0g g0 */
|
||||
por %mm2, %mm0 /* r0 0b */
|
||||
por %mm1, %mm0 /* rg gb */
|
||||
|
||||
movd %mm0, %eax
|
||||
movw %ax, (%esi, %ecx, 2)
|
||||
|
||||
incl %ecx
|
||||
4:
|
||||
cmpl $2, %ecx
|
||||
jng 3b
|
||||
|
||||
addl bpl, %esi
|
||||
decl %edx
|
||||
jnz 1b
|
||||
5:
|
||||
LEAVE
|
||||
|
||||
|
||||
SHADE_XIMAGE_16:
|
||||
ENTER
|
||||
|
||||
leal -6(%esi, %ebx, 2), %esi
|
||||
negl %ebx
|
||||
jz 5f
|
||||
|
||||
/* Setup multipliers */
|
||||
movd rm, %mm5
|
||||
movd gm, %mm6
|
||||
movd bm, %mm7
|
||||
punpcklwd %mm5, %mm5 /* 00 00 00 00 rm rm rm rm */
|
||||
punpcklwd %mm6, %mm6 /* 00 00 00 00 gm gm gm gm */
|
||||
punpcklwd %mm7, %mm7 /* 00 00 00 00 bm bm bm bm */
|
||||
punpckldq %mm5, %mm5 /* rm rm rm rm rm rm rm rm */
|
||||
punpckldq %mm6, %mm6 /* gm gm gm gm gm gm gm gm */
|
||||
punpckldq %mm7, %mm7 /* bm bm bm bm bm bm bm bm */
|
||||
|
||||
cmpl $256, rm
|
||||
jg shade_ximage_16_mmx_saturate
|
||||
cmpl $256, gm
|
||||
jg shade_ximage_16_mmx_saturate
|
||||
cmpl $256, bm
|
||||
jg shade_ximage_16_mmx_saturate
|
||||
|
||||
1: movl %ebx, %ecx
|
||||
addl $3, %ecx
|
||||
jns 3f
|
||||
2:
|
||||
movq (%esi, %ecx, 2), %mm0
|
||||
|
||||
movq %mm0, %mm1 /* rg gb */
|
||||
movq %mm0, %mm2 /* rg gb */
|
||||
psrlw $5, %mm1 /* 0r rg */
|
||||
psrlw $11, %mm0 /* 00 0r */
|
||||
psllw $11, %mm2 /* b0 00 */
|
||||
psllw $10, %mm1 /* g0 00 */
|
||||
psllw $8, %mm0 /* 0r 00 */
|
||||
psrlw $2, %mm1 /* 0g 00 */
|
||||
psrlw $3, %mm2 /* 0b 00 */
|
||||
|
||||
pmulhw %mm5, %mm0 /* 00 0r */
|
||||
pmulhw %mm6, %mm1 /* 00 0g */
|
||||
pmulhw %mm7, %mm2 /* 00 0b */
|
||||
|
||||
psllw $11, %mm0 /* r0 00 */
|
||||
psllw $5, %mm1 /* 0g g0 */
|
||||
por %mm2, %mm0 /* r0 0b */
|
||||
por %mm1, %mm0 /* rg gb */
|
||||
|
||||
movq %mm0, (%esi, %ecx, 2)
|
||||
|
||||
addl $4, %ecx
|
||||
js 2b
|
||||
jmp 4f
|
||||
3:
|
||||
movw (%esi, %ecx, 2), %ax
|
||||
movd %eax, %mm0
|
||||
|
||||
movq %mm0, %mm1 /* rg gb */
|
||||
movq %mm0, %mm2 /* rg gb */
|
||||
psrlw $5, %mm1 /* 0r rg */
|
||||
psrlw $11, %mm0 /* 00 0r */
|
||||
psllw $11, %mm2 /* b0 00 */
|
||||
psllw $10, %mm1 /* g0 00 */
|
||||
psllw $8, %mm0 /* 0r 00 */
|
||||
psrlw $2, %mm1 /* 0g 00 */
|
||||
psrlw $3, %mm2 /* 0b 00 */
|
||||
|
||||
pmulhw %mm5, %mm0 /* 00 0r */
|
||||
pmulhw %mm6, %mm1 /* 00 0g */
|
||||
pmulhw %mm7, %mm2 /* 00 0b */
|
||||
|
||||
psllw $11, %mm0 /* r0 00 */
|
||||
psllw $5, %mm1 /* 0g g0 */
|
||||
por %mm2, %mm0 /* r0 0b */
|
||||
por %mm1, %mm0 /* rg gb */
|
||||
|
||||
movd %mm0, %eax
|
||||
movw %ax, (%esi, %ecx, 2)
|
||||
|
||||
incl %ecx
|
||||
4:
|
||||
cmpl $2, %ecx
|
||||
jng 3b
|
||||
|
||||
addl bpl, %esi
|
||||
decl %edx
|
||||
jnz 1b
|
||||
5:
|
||||
LEAVE
|
||||
|
||||
|
||||
shade_ximage_16_mmx_saturate:
|
||||
|
||||
pcmpeqw %mm3, %mm3
|
||||
movq %mm3, %mm4
|
||||
psllw $5, %mm3 /* ff e0 ff e0 ff e0 ff e0 */
|
||||
psllw $6, %mm4 /* ff c0 ff c0 ff c0 ff c0 */
|
||||
|
||||
1: movl %ebx, %ecx
|
||||
addl $3, %ecx
|
||||
jns 3f
|
||||
2:
|
||||
movq (%esi, %ecx, 2), %mm0
|
||||
|
||||
movq %mm0, %mm1 /* rg gb */
|
||||
movq %mm0, %mm2 /* rg gb */
|
||||
psrlw $5, %mm1 /* 0r rg */
|
||||
psrlw $11, %mm0 /* 00 0r */
|
||||
psllw $11, %mm2 /* b0 00 */
|
||||
psllw $10, %mm1 /* g0 00 */
|
||||
psllw $8, %mm0 /* 0r 00 */
|
||||
psrlw $2, %mm1 /* 0g 00 */
|
||||
psrlw $3, %mm2 /* 0b 00 */
|
||||
|
||||
pmulhw %mm5, %mm0 /* xx xr */
|
||||
pmulhw %mm6, %mm1 /* xx xg */
|
||||
pmulhw %mm7, %mm2 /* xx xb */
|
||||
|
||||
/* Saturate upper */
|
||||
paddusw %mm3, %mm0 /* ff er */
|
||||
paddusw %mm4, %mm1 /* ff cg */
|
||||
paddusw %mm3, %mm2 /* ff eb */
|
||||
|
||||
psubw %mm4, %mm1 /* 00 0g */
|
||||
psubw %mm3, %mm2 /* 00 0b */
|
||||
|
||||
psllw $11, %mm0 /* r0 00 */
|
||||
psllw $5, %mm1 /* 0g g0 */
|
||||
por %mm2, %mm0 /* r0 0b */
|
||||
por %mm1, %mm0 /* rg gb */
|
||||
|
||||
movq %mm0, (%esi, %ecx, 2)
|
||||
|
||||
addl $4, %ecx
|
||||
js 2b
|
||||
jmp 4f
|
||||
3:
|
||||
movw (%esi, %ecx, 2), %ax
|
||||
movd %eax, %mm0
|
||||
|
||||
movq %mm0, %mm1 /* rg gb */
|
||||
movq %mm0, %mm2 /* rg gb */
|
||||
psrlw $5, %mm1 /* 0r rg */
|
||||
psrlw $11, %mm0 /* 00 0r */
|
||||
psllw $11, %mm2 /* b0 00 */
|
||||
psllw $10, %mm1 /* g0 00 */
|
||||
psllw $8, %mm0 /* 0r 00 */
|
||||
psrlw $2, %mm1 /* 0g 00 */
|
||||
psrlw $3, %mm2 /* 0b 00 */
|
||||
|
||||
pmulhw %mm5, %mm0 /* xx xr */
|
||||
pmulhw %mm6, %mm1 /* xx xg */
|
||||
pmulhw %mm7, %mm2 /* xx xb */
|
||||
|
||||
/* Saturate upper */
|
||||
paddusw %mm3, %mm0 /* ff er */
|
||||
paddusw %mm4, %mm1 /* ff cg */
|
||||
paddusw %mm3, %mm2 /* ff eb */
|
||||
|
||||
psubw %mm4, %mm1 /* 00 0g */
|
||||
psubw %mm3, %mm2 /* 00 0b */
|
||||
|
||||
psllw $11, %mm0 /* r0 00 */
|
||||
psllw $5, %mm1 /* 0g g0 */
|
||||
por %mm2, %mm0 /* r0 0b */
|
||||
por %mm1, %mm0 /* rg gb */
|
||||
|
||||
movd %mm0, %eax
|
||||
movw %ax, (%esi, %ecx, 2)
|
||||
|
||||
incl %ecx
|
||||
4:
|
||||
cmpl $2, %ecx
|
||||
jng 3b
|
||||
|
||||
addl bpl, %esi
|
||||
decl %edx
|
||||
jnz 1b
|
||||
5:
|
||||
LEAVE
|
||||
|
||||
|
||||
SHADE_XIMAGE_32:
|
||||
ENTER
|
||||
|
||||
leal (%esi, %ebx, 4), %esi
|
||||
negl %ebx
|
||||
jz 3f
|
||||
|
||||
movd rm, %mm4
|
||||
movd gm, %mm5
|
||||
movd bm, %mm6
|
||||
psllq $32, %mm4
|
||||
psllq $16, %mm5
|
||||
por %mm6, %mm4
|
||||
por %mm5, %mm4
|
||||
|
||||
pcmpeqw %mm6, %mm6
|
||||
psllw $15, %mm6 /* 80 00 80 00 80 00 80 00 */
|
||||
movq %mm6, %mm5
|
||||
pmulhw %mm4, %mm5 /* Get correction factor */
|
||||
1:
|
||||
movl %ebx, %ecx
|
||||
2:
|
||||
movd (%esi, %ecx, 4), %mm1 /* 00 rr gg bb */
|
||||
pxor %mm0, %mm0
|
||||
punpcklbw %mm1, %mm0 /* 00 00 rr 00 gg 00 bb 00 */
|
||||
pxor %mm6, %mm0 /* Flip sign */
|
||||
|
||||
pmulhw %mm4, %mm0 /* 00 00 xx rr xx gg xx bb */
|
||||
psubw %mm5, %mm0 /* Correct range */
|
||||
packuswb %mm0, %mm0 /* 00 rr gg bb 00 rr gg bb */
|
||||
|
||||
movd %mm0, (%esi, %ecx, 4)
|
||||
|
||||
incl %ecx
|
||||
jnz 2b
|
||||
|
||||
addl bpl, %esi
|
||||
decl %edx
|
||||
jnz 1b
|
||||
3:
|
||||
LEAVE
|
||||
|
||||
|
||||
HAVE_MMX:
|
||||
push %ebx
|
||||
/* Check if bit 21 in flags word is writeable */
|
||||
pushfl
|
||||
popl %eax
|
||||
movl %eax,%ebx
|
||||
xorl $0x00200000, %eax
|
||||
pushl %eax
|
||||
popfl
|
||||
pushfl
|
||||
popl %eax
|
||||
|
||||
cmpl %eax, %ebx
|
||||
je 8f
|
||||
|
||||
/* OK, we have CPUID */
|
||||
|
||||
movl $1, %eax
|
||||
cpuid
|
||||
|
||||
test $0x00800000, %edx
|
||||
jz 8f
|
||||
|
||||
movl $1, %eax /* success, have mmx */
|
||||
popl %ebx
|
||||
ret
|
||||
|
||||
8:
|
||||
xorl %eax,%eax /* failed, no mmx */
|
||||
popl %ebx
|
||||
ret
|
||||
|
||||
#if defined(__GNUC__) && !defined(_WIN32)
|
||||
.section .note.GNU-stack, "", @progbits
|
||||
.previous
|
||||
#endif
|
||||
4
src/fe-gtk/mmx_cmod.h
Normal file
4
src/fe-gtk/mmx_cmod.h
Normal file
@@ -0,0 +1,4 @@
|
||||
void shade_ximage_15_mmx(void *data, int bpl, int w, int h, int rm, int gm, int bm);
|
||||
void shade_ximage_16_mmx(void *data, int bpl, int w, int h, int rm, int gm, int bm);
|
||||
void shade_ximage_32_mmx(void *data, int bpl, int w, int h, int rm, int gm, int bm);
|
||||
int have_mmx (void);
|
||||
442
src/fe-gtk/notifygui.c
Normal file
442
src/fe-gtk/notifygui.c
Normal file
@@ -0,0 +1,442 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkhbbox.h>
|
||||
#include <gtk/gtkscrolledwindow.h>
|
||||
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkmessagedialog.h>
|
||||
#include <gtk/gtktable.h>
|
||||
#include <gtk/gtktreeview.h>
|
||||
#include <gtk/gtktreeselection.h>
|
||||
#include <gtk/gtkcellrenderertext.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/notify.h"
|
||||
#include "../common/cfgfiles.h"
|
||||
#include "../common/fe.h"
|
||||
#include "../common/server.h"
|
||||
#include "../common/util.h"
|
||||
#include "../common/userlist.h"
|
||||
#include "gtkutil.h"
|
||||
#include "maingui.h"
|
||||
#include "palette.h"
|
||||
#include "notifygui.h"
|
||||
|
||||
|
||||
/* model for the notify treeview */
|
||||
enum
|
||||
{
|
||||
USER_COLUMN,
|
||||
STATUS_COLUMN,
|
||||
SERVER_COLUMN,
|
||||
SEEN_COLUMN,
|
||||
COLOUR_COLUMN,
|
||||
NPS_COLUMN, /* struct notify_per_server * */
|
||||
N_COLUMNS
|
||||
};
|
||||
|
||||
|
||||
static GtkWidget *notify_window = 0;
|
||||
static GtkWidget *notify_button_opendialog;
|
||||
static GtkWidget *notify_button_remove;
|
||||
|
||||
|
||||
static void
|
||||
notify_closegui (void)
|
||||
{
|
||||
notify_window = 0;
|
||||
}
|
||||
|
||||
/* Need this to be able to set the foreground colour property of a row
|
||||
* from a GdkColor * in the model -Vince
|
||||
*/
|
||||
static void
|
||||
notify_treecell_property_mapper (GtkTreeViewColumn *col, GtkCellRenderer *cell,
|
||||
GtkTreeModel *model, GtkTreeIter *iter,
|
||||
gpointer data)
|
||||
{
|
||||
gchar *text;
|
||||
GdkColor *colour;
|
||||
int model_column = GPOINTER_TO_INT (data);
|
||||
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
|
||||
COLOUR_COLUMN, &colour,
|
||||
model_column, &text, -1);
|
||||
g_object_set (G_OBJECT (cell), "text", text, NULL);
|
||||
g_object_set (G_OBJECT (cell), "foreground-gdk", colour, NULL);
|
||||
g_free (text);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_row_cb (GtkTreeSelection *sel, GtkTreeView *view)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
struct notify_per_server *servnot;
|
||||
|
||||
if (gtkutil_treeview_get_selected (view, &iter, NPS_COLUMN, &servnot, -1))
|
||||
{
|
||||
gtk_widget_set_sensitive (notify_button_opendialog, servnot ? servnot->ison : 0);
|
||||
gtk_widget_set_sensitive (notify_button_remove, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_widget_set_sensitive (notify_button_opendialog, FALSE);
|
||||
gtk_widget_set_sensitive (notify_button_remove, FALSE);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
notify_treeview_new (GtkWidget *box)
|
||||
{
|
||||
GtkListStore *store;
|
||||
GtkWidget *view;
|
||||
GtkTreeViewColumn *col;
|
||||
int col_id;
|
||||
|
||||
store = gtk_list_store_new (N_COLUMNS,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_POINTER, /* can't specify colour! */
|
||||
G_TYPE_POINTER
|
||||
);
|
||||
g_return_val_if_fail (store != NULL, NULL);
|
||||
|
||||
view = gtkutil_treeview_new (box, GTK_TREE_MODEL (store),
|
||||
notify_treecell_property_mapper,
|
||||
USER_COLUMN, _("Name"),
|
||||
STATUS_COLUMN, _("Status"),
|
||||
SERVER_COLUMN, _("Network"),
|
||||
SEEN_COLUMN, _("Last Seen"), -1);
|
||||
gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (view), 0), TRUE);
|
||||
|
||||
for (col_id=0; (col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), col_id));
|
||||
col_id++)
|
||||
gtk_tree_view_column_set_alignment (col, 0.5);
|
||||
|
||||
g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (view))),
|
||||
"changed", G_CALLBACK (notify_row_cb), view);
|
||||
|
||||
gtk_widget_show (view);
|
||||
return view;
|
||||
}
|
||||
|
||||
void
|
||||
notify_gui_update (void)
|
||||
{
|
||||
struct notify *notify;
|
||||
struct notify_per_server *servnot;
|
||||
GSList *list = notify_list;
|
||||
GSList *slist;
|
||||
gchar *name, *status, *server, *seen;
|
||||
int online, servcount;
|
||||
time_t lastseen;
|
||||
char agobuf[128];
|
||||
|
||||
GtkListStore *store;
|
||||
GtkTreeView *view;
|
||||
GtkTreeIter iter;
|
||||
gboolean valid; /* true if we don't need to append a new tree row */
|
||||
|
||||
if (!notify_window)
|
||||
return;
|
||||
|
||||
view = g_object_get_data (G_OBJECT (notify_window), "view");
|
||||
store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
|
||||
valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
|
||||
|
||||
while (list)
|
||||
{
|
||||
notify = (struct notify *) list->data;
|
||||
name = notify->name;
|
||||
status = _("Offline");
|
||||
server = "";
|
||||
|
||||
online = FALSE;
|
||||
lastseen = 0;
|
||||
/* First see if they're online on any servers */
|
||||
slist = notify->server_list;
|
||||
while (slist)
|
||||
{
|
||||
servnot = (struct notify_per_server *) slist->data;
|
||||
if (servnot->ison)
|
||||
online = TRUE;
|
||||
if (servnot->lastseen > lastseen)
|
||||
lastseen = servnot->lastseen;
|
||||
slist = slist->next;
|
||||
}
|
||||
|
||||
if (!online) /* Offline on all servers */
|
||||
{
|
||||
if (!lastseen)
|
||||
seen = _("Never");
|
||||
else
|
||||
{
|
||||
snprintf (agobuf, sizeof (agobuf), _("%d minutes ago"), (int)(time (0) - lastseen) / 60);
|
||||
seen = agobuf;
|
||||
}
|
||||
if (!valid) /* create new tree row if required */
|
||||
gtk_list_store_append (store, &iter);
|
||||
gtk_list_store_set (store, &iter, 0, name, 1, status,
|
||||
2, server, 3, seen, 4, &colors[4], 5, NULL, -1);
|
||||
if (valid)
|
||||
valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
|
||||
|
||||
} else
|
||||
{
|
||||
/* Online - add one line per server */
|
||||
servcount = 0;
|
||||
slist = notify->server_list;
|
||||
status = _("Online");
|
||||
while (slist)
|
||||
{
|
||||
servnot = (struct notify_per_server *) slist->data;
|
||||
if (servnot->ison)
|
||||
{
|
||||
if (servcount > 0)
|
||||
name = "";
|
||||
server = server_get_network (servnot->server, TRUE);
|
||||
|
||||
snprintf (agobuf, sizeof (agobuf), _("%d minutes ago"), (int)(time (0) - lastseen) / 60);
|
||||
seen = agobuf;
|
||||
|
||||
if (!valid) /* create new tree row if required */
|
||||
gtk_list_store_append (store, &iter);
|
||||
gtk_list_store_set (store, &iter, 0, name, 1, status,
|
||||
2, server, 3, seen, 4, &colors[3], 5, servnot, -1);
|
||||
if (valid)
|
||||
valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
|
||||
|
||||
servcount++;
|
||||
}
|
||||
slist = slist->next;
|
||||
}
|
||||
}
|
||||
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
while (valid)
|
||||
{
|
||||
GtkTreeIter old = iter;
|
||||
/* get next iter now because removing invalidates old one */
|
||||
valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store),
|
||||
&iter);
|
||||
gtk_list_store_remove (store, &old);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
notify_opendialog_clicked (GtkWidget * igad)
|
||||
{
|
||||
GtkTreeView *view;
|
||||
GtkTreeIter iter;
|
||||
struct notify_per_server *servnot;
|
||||
|
||||
view = g_object_get_data (G_OBJECT (notify_window), "view");
|
||||
if (gtkutil_treeview_get_selected (view, &iter, NPS_COLUMN, &servnot, -1))
|
||||
{
|
||||
if (servnot)
|
||||
open_query (servnot->server, servnot->notify->name, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
notify_remove_clicked (GtkWidget * igad)
|
||||
{
|
||||
GtkTreeView *view;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
GtkTreePath *path = NULL;
|
||||
gboolean found = FALSE;
|
||||
char *name;
|
||||
|
||||
view = g_object_get_data (G_OBJECT (notify_window), "view");
|
||||
if (gtkutil_treeview_get_selected (view, &iter, USER_COLUMN, &name, -1))
|
||||
{
|
||||
model = gtk_tree_view_get_model (view);
|
||||
found = (*name != 0);
|
||||
while (!found) /* the real nick is some previous node */
|
||||
{
|
||||
g_free (name); /* it's useless to us */
|
||||
if (!path)
|
||||
path = gtk_tree_model_get_path (model, &iter);
|
||||
if (!gtk_tree_path_prev (path)) /* arrgh! no previous node! */
|
||||
{
|
||||
g_warning ("notify list state is invalid\n");
|
||||
break;
|
||||
}
|
||||
if (!gtk_tree_model_get_iter (model, &iter, path))
|
||||
break;
|
||||
gtk_tree_model_get (model, &iter, USER_COLUMN, &name, -1);
|
||||
found = (*name != 0);
|
||||
}
|
||||
if (path)
|
||||
gtk_tree_path_free (path);
|
||||
if (!found)
|
||||
return;
|
||||
|
||||
/* ok, now we can remove it */
|
||||
gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
|
||||
notify_deluser (name);
|
||||
g_free (name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
notifygui_add_cb (GtkDialog *dialog, gint response, gpointer entry)
|
||||
{
|
||||
char *networks;
|
||||
char *text;
|
||||
|
||||
text = GTK_ENTRY (entry)->text;
|
||||
if (text[0] && response == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
networks = GTK_ENTRY (g_object_get_data (G_OBJECT (entry), "net"))->text;
|
||||
if (strcasecmp (networks, "ALL") == 0 || networks[0] == 0)
|
||||
notify_adduser (text, NULL);
|
||||
else
|
||||
notify_adduser (text, networks);
|
||||
}
|
||||
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
}
|
||||
|
||||
static void
|
||||
notifygui_add_enter (GtkWidget *entry, GtkWidget *dialog)
|
||||
{
|
||||
gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
|
||||
}
|
||||
|
||||
void
|
||||
fe_notify_ask (char *nick, char *networks)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *entry;
|
||||
GtkWidget *label;
|
||||
GtkWidget *wid;
|
||||
GtkWidget *table;
|
||||
char *msg = _("Enter nickname to add:");
|
||||
char buf[256];
|
||||
|
||||
dialog = gtk_dialog_new_with_buttons (msg, NULL, 0,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
|
||||
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
if (parent_window)
|
||||
gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent_window));
|
||||
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
|
||||
|
||||
table = gtk_table_new (2, 3, FALSE);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (table), 12);
|
||||
gtk_table_set_row_spacings (GTK_TABLE (table), 3);
|
||||
gtk_table_set_col_spacings (GTK_TABLE (table), 8);
|
||||
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), table);
|
||||
|
||||
label = gtk_label_new (msg);
|
||||
gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 0, 1);
|
||||
|
||||
entry = gtk_entry_new ();
|
||||
gtk_entry_set_text (GTK_ENTRY (entry), nick);
|
||||
g_signal_connect (G_OBJECT (entry), "activate",
|
||||
G_CALLBACK (notifygui_add_enter), dialog);
|
||||
gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, 0, 1);
|
||||
|
||||
g_signal_connect (G_OBJECT (dialog), "response",
|
||||
G_CALLBACK (notifygui_add_cb), entry);
|
||||
|
||||
label = gtk_label_new (_("Notify on these networks:"));
|
||||
gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 2, 3);
|
||||
|
||||
wid = gtk_entry_new ();
|
||||
g_object_set_data (G_OBJECT (entry), "net", wid);
|
||||
g_signal_connect (G_OBJECT (wid), "activate",
|
||||
G_CALLBACK (notifygui_add_enter), dialog);
|
||||
gtk_entry_set_text (GTK_ENTRY (wid), networks ? networks : "ALL");
|
||||
gtk_table_attach_defaults (GTK_TABLE (table), wid, 1, 2, 2, 3);
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
snprintf (buf, sizeof (buf), "<i><span size=\"smaller\">%s</span></i>", _("Comma separated list of networks is accepted."));
|
||||
gtk_label_set_markup (GTK_LABEL (label), buf);
|
||||
gtk_table_attach_defaults (GTK_TABLE (table), label, 1, 2, 3, 4);
|
||||
|
||||
gtk_widget_show_all (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_add_clicked (GtkWidget * igad)
|
||||
{
|
||||
fe_notify_ask ("", NULL);
|
||||
}
|
||||
|
||||
void
|
||||
notify_opengui (void)
|
||||
{
|
||||
GtkWidget *vbox, *bbox;
|
||||
GtkWidget *view;
|
||||
|
||||
if (notify_window)
|
||||
{
|
||||
mg_bring_tofront (notify_window);
|
||||
return;
|
||||
}
|
||||
|
||||
notify_window =
|
||||
mg_create_generic_tab ("Notify", _("XChat: Friends List"), FALSE, TRUE,
|
||||
notify_closegui, NULL, 400, 250, &vbox, 0);
|
||||
|
||||
view = notify_treeview_new (vbox);
|
||||
g_object_set_data (G_OBJECT (notify_window), "view", view);
|
||||
|
||||
bbox = gtk_hbutton_box_new ();
|
||||
gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_SPREAD);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (bbox), 5);
|
||||
gtk_box_pack_end (GTK_BOX (vbox), bbox, 0, 0, 0);
|
||||
gtk_widget_show (bbox);
|
||||
|
||||
gtkutil_button (bbox, GTK_STOCK_NEW, 0, notify_add_clicked, 0,
|
||||
_("Add..."));
|
||||
|
||||
notify_button_remove =
|
||||
gtkutil_button (bbox, GTK_STOCK_DELETE, 0, notify_remove_clicked, 0,
|
||||
_("Remove"));
|
||||
|
||||
notify_button_opendialog =
|
||||
gtkutil_button (bbox, NULL, 0, notify_opendialog_clicked, 0,
|
||||
_("Open Dialog"));
|
||||
|
||||
gtk_widget_set_sensitive (notify_button_opendialog, FALSE);
|
||||
gtk_widget_set_sensitive (notify_button_remove, FALSE);
|
||||
|
||||
notify_gui_update ();
|
||||
|
||||
gtk_widget_show (notify_window);
|
||||
}
|
||||
2
src/fe-gtk/notifygui.h
Normal file
2
src/fe-gtk/notifygui.h
Normal file
@@ -0,0 +1,2 @@
|
||||
void notify_gui_update (void);
|
||||
void notify_opengui (void);
|
||||
226
src/fe-gtk/palette.c
Normal file
226
src/fe-gtk/palette.c
Normal file
@@ -0,0 +1,226 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
#include "palette.h"
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/util.h"
|
||||
#include "../common/cfgfiles.h"
|
||||
|
||||
|
||||
GdkColor colors[] = {
|
||||
/* colors for xtext */
|
||||
{0, 0xcccc, 0xcccc, 0xcccc}, /* 16 white */
|
||||
{0, 0x0000, 0x0000, 0x0000}, /* 17 black */
|
||||
{0, 0x35c2, 0x35c2, 0xb332}, /* 18 blue */
|
||||
{0, 0x2a3d, 0x8ccc, 0x2a3d}, /* 19 green */
|
||||
{0, 0xc3c3, 0x3b3b, 0x3b3b}, /* 20 red */
|
||||
{0, 0xc7c7, 0x3232, 0x3232}, /* 21 light red */
|
||||
{0, 0x8000, 0x2666, 0x7fff}, /* 22 purple */
|
||||
{0, 0x6666, 0x3636, 0x1f1f}, /* 23 orange */
|
||||
{0, 0xd999, 0xa6d3, 0x4147}, /* 24 yellow */
|
||||
{0, 0x3d70, 0xcccc, 0x3d70}, /* 25 green */
|
||||
{0, 0x199a, 0x5555, 0x5555}, /* 26 aqua */
|
||||
{0, 0x2eef, 0x8ccc, 0x74df}, /* 27 light aqua */
|
||||
{0, 0x451e, 0x451e, 0xe666}, /* 28 blue */
|
||||
{0, 0xb0b0, 0x3737, 0xb0b0}, /* 29 light purple */
|
||||
{0, 0x4c4c, 0x4c4c, 0x4c4c}, /* 30 grey */
|
||||
{0, 0x9595, 0x9595, 0x9595}, /* 31 light grey */
|
||||
|
||||
{0, 0xcccc, 0xcccc, 0xcccc}, /* 16 white */
|
||||
{0, 0x0000, 0x0000, 0x0000}, /* 17 black */
|
||||
{0, 0x35c2, 0x35c2, 0xb332}, /* 18 blue */
|
||||
{0, 0x2a3d, 0x8ccc, 0x2a3d}, /* 19 green */
|
||||
{0, 0xc3c3, 0x3b3b, 0x3b3b}, /* 20 red */
|
||||
{0, 0xc7c7, 0x3232, 0x3232}, /* 21 light red */
|
||||
{0, 0x8000, 0x2666, 0x7fff}, /* 22 purple */
|
||||
{0, 0x6666, 0x3636, 0x1f1f}, /* 23 orange */
|
||||
{0, 0xd999, 0xa6d3, 0x4147}, /* 24 yellow */
|
||||
{0, 0x3d70, 0xcccc, 0x3d70}, /* 25 green */
|
||||
{0, 0x199a, 0x5555, 0x5555}, /* 26 aqua */
|
||||
{0, 0x2eef, 0x8ccc, 0x74df}, /* 27 light aqua */
|
||||
{0, 0x451e, 0x451e, 0xe666}, /* 28 blue */
|
||||
{0, 0xb0b0, 0x3737, 0xb0b0}, /* 29 light purple */
|
||||
{0, 0x4c4c, 0x4c4c, 0x4c4c}, /* 30 grey */
|
||||
{0, 0x9595, 0x9595, 0x9595}, /* 31 light grey */
|
||||
|
||||
{0, 0xffff, 0xffff, 0xffff}, /* 32 marktext Fore (white) */
|
||||
{0, 0x3535, 0x6e6e, 0xc1c1}, /* 33 marktext Back (blue) */
|
||||
{0, 0x0000, 0x0000, 0x0000}, /* 34 foreground (black) */
|
||||
{0, 0xf0f0, 0xf0f0, 0xf0f0}, /* 35 background (white) */
|
||||
{0, 0xcccc, 0x1010, 0x1010}, /* 36 marker line (red) */
|
||||
|
||||
/* colors for GUI */
|
||||
{0, 0x9999, 0x0000, 0x0000}, /* 37 tab New Data (dark red) */
|
||||
{0, 0x0000, 0x0000, 0xffff}, /* 38 tab Nick Mentioned (blue) */
|
||||
{0, 0xffff, 0x0000, 0x0000}, /* 39 tab New Message (red) */
|
||||
{0, 0x9595, 0x9595, 0x9595}, /* 40 away user (grey) */
|
||||
};
|
||||
#define MAX_COL 40
|
||||
|
||||
|
||||
void
|
||||
palette_alloc (GtkWidget * widget)
|
||||
{
|
||||
int i;
|
||||
static int done_alloc = FALSE;
|
||||
GdkColormap *cmap;
|
||||
|
||||
if (!done_alloc) /* don't do it again */
|
||||
{
|
||||
done_alloc = TRUE;
|
||||
cmap = gtk_widget_get_colormap (widget);
|
||||
for (i = MAX_COL; i >= 0; i--)
|
||||
gdk_colormap_alloc_color (cmap, &colors[i], FALSE, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/* maps XChat 2.0.x colors to current */
|
||||
static const int remap[] =
|
||||
{
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
|
||||
33, /* 16:marktextback */
|
||||
32, /* 17:marktextfore */
|
||||
34, /* 18: fg */
|
||||
35, /* 19: bg */
|
||||
37, /* 20: newdata */
|
||||
38, /* 21: blue */
|
||||
39, /* 22: newmsg */
|
||||
40 /* 23: away */
|
||||
};
|
||||
|
||||
void
|
||||
palette_load (void)
|
||||
{
|
||||
int i, j, l, fh, res;
|
||||
char prefname[256];
|
||||
struct stat st;
|
||||
char *cfg;
|
||||
int red, green, blue;
|
||||
int upgrade = FALSE;
|
||||
|
||||
fh = xchat_open_file ("colors.conf", O_RDONLY, 0, 0);
|
||||
if (fh == -1)
|
||||
{
|
||||
fh = xchat_open_file ("palette.conf", O_RDONLY, 0, 0);
|
||||
upgrade = TRUE;
|
||||
}
|
||||
|
||||
if (fh != -1)
|
||||
{
|
||||
fstat (fh, &st);
|
||||
cfg = malloc (st.st_size + 1);
|
||||
if (cfg)
|
||||
{
|
||||
cfg[0] = '\0';
|
||||
l = read (fh, cfg, st.st_size);
|
||||
if (l >= 0)
|
||||
cfg[l] = '\0';
|
||||
|
||||
if (!upgrade)
|
||||
{
|
||||
/* mIRC colors 0-31 are here */
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
snprintf (prefname, sizeof prefname, "color_%d", i);
|
||||
cfg_get_color (cfg, prefname, &red, &green, &blue);
|
||||
colors[i].red = red;
|
||||
colors[i].green = green;
|
||||
colors[i].blue = blue;
|
||||
}
|
||||
|
||||
/* our special colors are mapped at 256+ */
|
||||
for (i = 256, j = 32; j < MAX_COL+1; i++, j++)
|
||||
{
|
||||
snprintf (prefname, sizeof prefname, "color_%d", i);
|
||||
cfg_get_color (cfg, prefname, &red, &green, &blue);
|
||||
colors[j].red = red;
|
||||
colors[j].green = green;
|
||||
colors[j].blue = blue;
|
||||
}
|
||||
|
||||
} else
|
||||
{
|
||||
/* loading 2.0.x palette.conf */
|
||||
for (i = 0; i < MAX_COL+1; i++)
|
||||
{
|
||||
snprintf (prefname, sizeof prefname, "color_%d_red", i);
|
||||
red = cfg_get_int (cfg, prefname);
|
||||
|
||||
snprintf (prefname, sizeof prefname, "color_%d_grn", i);
|
||||
green = cfg_get_int (cfg, prefname);
|
||||
|
||||
snprintf (prefname, sizeof prefname, "color_%d_blu", i);
|
||||
blue = cfg_get_int_with_result (cfg, prefname, &res);
|
||||
|
||||
if (res)
|
||||
{
|
||||
colors[remap[i]].red = red;
|
||||
colors[remap[i]].green = green;
|
||||
colors[remap[i]].blue = blue;
|
||||
}
|
||||
}
|
||||
|
||||
/* copy 0-15 to 16-31 */
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
colors[i+16].red = colors[i].red;
|
||||
colors[i+16].green = colors[i].green;
|
||||
colors[i+16].blue = colors[i].blue;
|
||||
}
|
||||
}
|
||||
free (cfg);
|
||||
}
|
||||
close (fh);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
palette_save (void)
|
||||
{
|
||||
int i, j, fh;
|
||||
char prefname[256];
|
||||
|
||||
fh = xchat_open_file ("colors.conf", O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
|
||||
if (fh != -1)
|
||||
{
|
||||
/* mIRC colors 0-31 are here */
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
snprintf (prefname, sizeof prefname, "color_%d", i);
|
||||
cfg_put_color (fh, colors[i].red, colors[i].green, colors[i].blue, prefname);
|
||||
}
|
||||
|
||||
/* our special colors are mapped at 256+ */
|
||||
for (i = 256, j = 32; j < MAX_COL+1; i++, j++)
|
||||
{
|
||||
snprintf (prefname, sizeof prefname, "color_%d", i);
|
||||
cfg_put_color (fh, colors[j].red, colors[j].green, colors[j].blue, prefname);
|
||||
}
|
||||
|
||||
close (fh);
|
||||
}
|
||||
}
|
||||
15
src/fe-gtk/palette.h
Normal file
15
src/fe-gtk/palette.h
Normal file
@@ -0,0 +1,15 @@
|
||||
extern GdkColor colors[];
|
||||
|
||||
#define COL_MARK_FG 32
|
||||
#define COL_MARK_BG 33
|
||||
#define COL_FG 34
|
||||
#define COL_BG 35
|
||||
#define COL_MARKER 36
|
||||
#define COL_NEW_DATA 37
|
||||
#define COL_HILIGHT 38
|
||||
#define COL_NEW_MSG 39
|
||||
#define COL_AWAY 40
|
||||
|
||||
void palette_alloc (GtkWidget * widget);
|
||||
void palette_load (void);
|
||||
void palette_save (void);
|
||||
123
src/fe-gtk/pixmaps.c
Normal file
123
src/fe-gtk/pixmaps.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/fe.h"
|
||||
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include <gdk-pixbuf/gdk-pixdata.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
|
||||
#include "../pixmaps/inline_pngs.h"
|
||||
|
||||
GdkPixbuf *pix_xchat;
|
||||
GdkPixbuf *pix_book;
|
||||
|
||||
GdkPixbuf *pix_purple;
|
||||
GdkPixbuf *pix_red;
|
||||
GdkPixbuf *pix_op;
|
||||
GdkPixbuf *pix_hop;
|
||||
GdkPixbuf *pix_voice;
|
||||
|
||||
GdkPixbuf *pix_tray_msg;
|
||||
GdkPixbuf *pix_tray_hilight;
|
||||
GdkPixbuf *pix_tray_file;
|
||||
|
||||
GdkPixbuf *pix_channel;
|
||||
GdkPixbuf *pix_dialog;
|
||||
GdkPixbuf *pix_server;
|
||||
GdkPixbuf *pix_util;
|
||||
|
||||
|
||||
static GdkPixmap *
|
||||
pixmap_load_from_file_real (char *file)
|
||||
{
|
||||
GdkPixbuf *img;
|
||||
GdkPixmap *pixmap;
|
||||
|
||||
img = gdk_pixbuf_new_from_file (file, 0);
|
||||
if (!img)
|
||||
return NULL;
|
||||
gdk_pixbuf_render_pixmap_and_mask (img, &pixmap, NULL, 128);
|
||||
gdk_pixbuf_unref (img);
|
||||
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
GdkPixmap *
|
||||
pixmap_load_from_file (char *filename)
|
||||
{
|
||||
char buf[256];
|
||||
GdkPixmap *pix;
|
||||
|
||||
if (filename[0] == '\0')
|
||||
return NULL;
|
||||
|
||||
pix = pixmap_load_from_file_real (filename);
|
||||
if (pix == NULL)
|
||||
{
|
||||
strcpy (buf, "Cannot open:\n\n");
|
||||
strncpy (buf + 14, filename, sizeof (buf) - 14);
|
||||
buf[sizeof (buf) - 1] = 0;
|
||||
fe_message (buf, FE_MSG_ERROR);
|
||||
}
|
||||
|
||||
return pix;
|
||||
}
|
||||
|
||||
#define LOADPIX(vv,pp,ff) \
|
||||
vv = gdk_pixbuf_new_from_file (XCHATSHAREDIR"/xchat/"ff, 0); \
|
||||
if (!vv) \
|
||||
vv = gdk_pixbuf_new_from_inline (-1, pp, FALSE, 0);
|
||||
|
||||
#define LOADPIX_DISKONLY(vv,ff) \
|
||||
vv = gdk_pixbuf_new_from_file (XCHATSHAREDIR"/xchat/"ff, 0);
|
||||
|
||||
#define EXT ".png"
|
||||
|
||||
void
|
||||
pixmaps_init (void)
|
||||
{
|
||||
pix_book = gdk_pixbuf_new_from_inline (-1, bookpng, FALSE, 0);
|
||||
|
||||
/* used in About window, tray icon and WindowManager icon. */
|
||||
LOADPIX (pix_xchat, xchatpng, "xchat"EXT);
|
||||
|
||||
/* userlist icons, with inlined defaults */
|
||||
LOADPIX (pix_hop, hoppng, "hop"EXT);
|
||||
LOADPIX (pix_purple, purplepng, "purple"EXT);
|
||||
LOADPIX (pix_red, redpng, "red"EXT);
|
||||
LOADPIX (pix_op, oppng, "op"EXT);
|
||||
LOADPIX (pix_voice, voicepng, "voice"EXT);
|
||||
|
||||
/* tray icons, with inlined defaults */
|
||||
LOADPIX (pix_tray_msg, traymsgpng, "message"EXT);
|
||||
LOADPIX (pix_tray_hilight, trayhilightpng, "highlight"EXT);
|
||||
LOADPIX (pix_tray_file, trayfilepng, "fileoffer"EXT);
|
||||
|
||||
/* treeview icons, no defaults, load from disk only */
|
||||
LOADPIX_DISKONLY (pix_channel, "channel"EXT);
|
||||
LOADPIX_DISKONLY (pix_dialog, "dialog"EXT);
|
||||
LOADPIX_DISKONLY (pix_server, "server"EXT);
|
||||
LOADPIX_DISKONLY (pix_util, "util"EXT);
|
||||
}
|
||||
19
src/fe-gtk/pixmaps.h
Normal file
19
src/fe-gtk/pixmaps.h
Normal file
@@ -0,0 +1,19 @@
|
||||
extern GdkPixbuf *pix_book;
|
||||
extern GdkPixbuf *pix_hop;
|
||||
extern GdkPixbuf *pix_purple;
|
||||
extern GdkPixbuf *pix_red;
|
||||
extern GdkPixbuf *pix_op;
|
||||
extern GdkPixbuf *pix_voice;
|
||||
extern GdkPixbuf *pix_xchat;
|
||||
|
||||
extern GdkPixbuf *pix_tray_msg;
|
||||
extern GdkPixbuf *pix_tray_hilight;
|
||||
extern GdkPixbuf *pix_tray_file;
|
||||
|
||||
extern GdkPixbuf *pix_channel;
|
||||
extern GdkPixbuf *pix_dialog;
|
||||
extern GdkPixbuf *pix_server;
|
||||
extern GdkPixbuf *pix_util;
|
||||
|
||||
extern GdkPixmap *pixmap_load_from_file (char *file);
|
||||
extern void pixmaps_init (void);
|
||||
852
src/fe-gtk/plugin-tray.c
Normal file
852
src/fe-gtk/plugin-tray.c
Normal file
@@ -0,0 +1,852 @@
|
||||
/* Copyright (C) 2006-2007 Peter Zelezny. */
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "../common/xchat-plugin.h"
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "../common/inbound.h"
|
||||
#include "../common/server.h"
|
||||
#include "../common/fe.h"
|
||||
#include "../common/util.h"
|
||||
#include "fe-gtk.h"
|
||||
#include "pixmaps.h"
|
||||
#include "maingui.h"
|
||||
#include "menu.h"
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define LIBNOTIFY
|
||||
|
||||
typedef enum /* current icon status */
|
||||
{
|
||||
TS_NONE,
|
||||
TS_MESSAGE,
|
||||
TS_HIGHLIGHT,
|
||||
TS_FILEOFFER,
|
||||
TS_CUSTOM /* plugin */
|
||||
} TrayStatus;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
WS_FOCUSED,
|
||||
WS_NORMAL,
|
||||
WS_HIDDEN
|
||||
} WinStatus;
|
||||
|
||||
typedef GdkPixbuf* TrayIcon;
|
||||
#define tray_icon_from_file(f) gdk_pixbuf_new_from_file(f,NULL)
|
||||
#define tray_icon_free(i) g_object_unref(i)
|
||||
|
||||
#define ICON_NORMAL pix_xchat
|
||||
#define ICON_MSG pix_tray_msg
|
||||
#define ICON_HILIGHT pix_tray_hilight
|
||||
#define ICON_FILE pix_tray_file
|
||||
#define TIMEOUT 500
|
||||
|
||||
static GtkStatusIcon *sticon;
|
||||
static gint flash_tag;
|
||||
static TrayStatus tray_status;
|
||||
static xchat_plugin *ph;
|
||||
|
||||
static TrayIcon custom_icon1;
|
||||
static TrayIcon custom_icon2;
|
||||
|
||||
static int tray_priv_count = 0;
|
||||
static int tray_pub_count = 0;
|
||||
static int tray_hilight_count = 0;
|
||||
static int tray_file_count = 0;
|
||||
|
||||
|
||||
void tray_apply_setup (void);
|
||||
|
||||
|
||||
static WinStatus
|
||||
tray_get_window_status (void)
|
||||
{
|
||||
const char *st;
|
||||
|
||||
st = xchat_get_info (ph, "win_status");
|
||||
|
||||
if (!st)
|
||||
return WS_HIDDEN;
|
||||
|
||||
if (!strcmp (st, "active"))
|
||||
return WS_FOCUSED;
|
||||
|
||||
if (!strcmp (st, "hidden"))
|
||||
return WS_HIDDEN;
|
||||
|
||||
return WS_NORMAL;
|
||||
}
|
||||
|
||||
static int
|
||||
tray_count_channels (void)
|
||||
{
|
||||
int cons = 0;
|
||||
GSList *list;
|
||||
session *sess;
|
||||
|
||||
for (list = sess_list; list; list = list->next)
|
||||
{
|
||||
sess = list->data;
|
||||
if (sess->server->connected && sess->channel[0] &&
|
||||
sess->type == SESS_CHANNEL)
|
||||
cons++;
|
||||
}
|
||||
return cons;
|
||||
}
|
||||
|
||||
static int
|
||||
tray_count_networks (void)
|
||||
{
|
||||
int cons = 0;
|
||||
GSList *list;
|
||||
|
||||
for (list = serv_list; list; list = list->next)
|
||||
{
|
||||
if (((server *)list->data)->connected)
|
||||
cons++;
|
||||
}
|
||||
return cons;
|
||||
}
|
||||
|
||||
void
|
||||
fe_tray_set_tooltip (const char *text)
|
||||
{
|
||||
if (sticon)
|
||||
gtk_status_icon_set_tooltip (sticon, text);
|
||||
}
|
||||
|
||||
#ifdef LIBNOTIFY
|
||||
|
||||
/* dynamic access to libnotify.so */
|
||||
|
||||
static void *nn_mod = NULL;
|
||||
/* prototypes */
|
||||
static gboolean (*nn_init) (char *);
|
||||
static void (*nn_uninit) (void);
|
||||
/* recent versions of libnotify don't take the fourth GtkWidget argument, but passing an
|
||||
* extra NULL argument will be fine */
|
||||
static void *(*nn_new) (const gchar *summary, const gchar *message, const gchar *icon, gpointer dummy);
|
||||
static gboolean (*nn_show) (void *noti, GError **error);
|
||||
static void (*nn_set_timeout) (void *noti, gint timeout);
|
||||
|
||||
static void
|
||||
libnotify_cleanup (void)
|
||||
{
|
||||
if (nn_mod)
|
||||
{
|
||||
nn_uninit ();
|
||||
g_module_close (nn_mod);
|
||||
nn_mod = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
libnotify_notify_new (const char *title, const char *text, GtkStatusIcon *icon)
|
||||
{
|
||||
void *noti;
|
||||
|
||||
if (!nn_mod)
|
||||
{
|
||||
nn_mod = g_module_open ("libnotify", G_MODULE_BIND_LAZY);
|
||||
if (!nn_mod)
|
||||
{
|
||||
nn_mod = g_module_open ("libnotify.so.1", G_MODULE_BIND_LAZY);
|
||||
if (!nn_mod)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!g_module_symbol (nn_mod, "notify_init", (gpointer)&nn_init))
|
||||
goto bad;
|
||||
if (!g_module_symbol (nn_mod, "notify_uninit", (gpointer)&nn_uninit))
|
||||
goto bad;
|
||||
if (!g_module_symbol (nn_mod, "notify_notification_new", (gpointer)&nn_new))
|
||||
goto bad;
|
||||
if (!g_module_symbol (nn_mod, "notify_notification_show", (gpointer)&nn_show))
|
||||
goto bad;
|
||||
if (!g_module_symbol (nn_mod, "notify_notification_set_timeout", (gpointer)&nn_set_timeout))
|
||||
goto bad;
|
||||
if (!nn_init (PACKAGE_NAME))
|
||||
goto bad;
|
||||
}
|
||||
|
||||
text = strip_color (text, -1, STRIP_ALL|STRIP_ESCMARKUP);
|
||||
title = strip_color (title, -1, STRIP_ALL);
|
||||
noti = nn_new (title, text, XCHATSHAREDIR"/pixmaps/xchat.png", NULL);
|
||||
g_free ((char *)title);
|
||||
g_free ((char *)text);
|
||||
|
||||
nn_set_timeout (noti, prefs.input_balloon_time*1000);
|
||||
nn_show (noti, NULL);
|
||||
g_object_unref (G_OBJECT (noti));
|
||||
|
||||
return TRUE;
|
||||
|
||||
bad:
|
||||
g_module_close (nn_mod);
|
||||
nn_mod = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
fe_tray_set_balloon (const char *title, const char *text)
|
||||
{
|
||||
#ifndef WIN32
|
||||
const char *argv[8];
|
||||
const char *path;
|
||||
char time[16];
|
||||
WinStatus ws;
|
||||
|
||||
/* no balloons if the window is focused */
|
||||
ws = tray_get_window_status ();
|
||||
if (ws == WS_FOCUSED)
|
||||
return;
|
||||
|
||||
/* bit 1 of flags means "no balloons unless hidden/iconified" */
|
||||
if (ws != WS_HIDDEN && (prefs.gui_tray_flags & 2))
|
||||
return;
|
||||
|
||||
/* FIXME: this should close the current balloon */
|
||||
if (!text)
|
||||
return;
|
||||
|
||||
#ifdef LIBNOTIFY
|
||||
/* try it via libnotify.so */
|
||||
if (libnotify_notify_new (title, text, sticon))
|
||||
return; /* success */
|
||||
#endif
|
||||
|
||||
/* try it the crude way */
|
||||
path = g_find_program_in_path ("notify-send");
|
||||
if (path)
|
||||
{
|
||||
sprintf(time, "%d000",prefs.input_balloon_time);
|
||||
argv[0] = path;
|
||||
argv[1] = "-i";
|
||||
argv[2] = "gtk-dialog-info";
|
||||
if (access (XCHATSHAREDIR"/pixmaps/xchat.png", R_OK) == 0)
|
||||
argv[2] = XCHATSHAREDIR"/pixmaps/xchat.png";
|
||||
argv[3] = "-t";
|
||||
argv[4] = time;
|
||||
argv[5] = title;
|
||||
text = strip_color (text, -1, STRIP_ALL|STRIP_ESCMARKUP);
|
||||
argv[6] = text;
|
||||
argv[7] = NULL;
|
||||
xchat_execv (argv);
|
||||
g_free ((char *)path);
|
||||
g_free ((char *)text);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* show this error only once */
|
||||
static unsigned char said_it = FALSE;
|
||||
if (!said_it)
|
||||
{
|
||||
said_it = TRUE;
|
||||
fe_message (_("Cannot find 'notify-send' to open balloon alerts.\nPlease install libnotify."), FE_MSG_ERROR);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
tray_set_balloonf (const char *text, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *buf;
|
||||
|
||||
va_start (args, format);
|
||||
buf = g_strdup_vprintf (format, args);
|
||||
va_end (args);
|
||||
|
||||
fe_tray_set_balloon (buf, text);
|
||||
g_free (buf);
|
||||
}
|
||||
|
||||
static void
|
||||
tray_set_tipf (const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *buf;
|
||||
|
||||
va_start (args, format);
|
||||
buf = g_strdup_vprintf (format, args);
|
||||
va_end (args);
|
||||
|
||||
fe_tray_set_tooltip (buf);
|
||||
g_free (buf);
|
||||
}
|
||||
|
||||
static void
|
||||
tray_stop_flash (void)
|
||||
{
|
||||
int nets, chans;
|
||||
|
||||
if (flash_tag)
|
||||
{
|
||||
g_source_remove (flash_tag);
|
||||
flash_tag = 0;
|
||||
}
|
||||
|
||||
if (sticon)
|
||||
{
|
||||
gtk_status_icon_set_from_pixbuf (sticon, ICON_NORMAL);
|
||||
nets = tray_count_networks ();
|
||||
chans = tray_count_channels ();
|
||||
if (nets)
|
||||
tray_set_tipf (_("XChat: Connected to %u networks and %u channels"),
|
||||
nets, chans);
|
||||
else
|
||||
tray_set_tipf ("XChat: %s", _("Not connected."));
|
||||
}
|
||||
|
||||
if (custom_icon1)
|
||||
{
|
||||
tray_icon_free (custom_icon1);
|
||||
custom_icon1 = NULL;
|
||||
}
|
||||
|
||||
if (custom_icon2)
|
||||
{
|
||||
tray_icon_free (custom_icon2);
|
||||
custom_icon2 = NULL;
|
||||
}
|
||||
|
||||
tray_status = TS_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
tray_reset_counts (void)
|
||||
{
|
||||
tray_priv_count = 0;
|
||||
tray_pub_count = 0;
|
||||
tray_hilight_count = 0;
|
||||
tray_file_count = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tray_timeout_cb (TrayIcon icon)
|
||||
{
|
||||
if (custom_icon1)
|
||||
{
|
||||
if (gtk_status_icon_get_pixbuf (sticon) == custom_icon1)
|
||||
{
|
||||
if (custom_icon2)
|
||||
gtk_status_icon_set_from_pixbuf (sticon, custom_icon2);
|
||||
else
|
||||
gtk_status_icon_set_from_pixbuf (sticon, ICON_NORMAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_status_icon_set_from_pixbuf (sticon, custom_icon1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gtk_status_icon_get_pixbuf (sticon) == ICON_NORMAL)
|
||||
gtk_status_icon_set_from_pixbuf (sticon, icon);
|
||||
else
|
||||
gtk_status_icon_set_from_pixbuf (sticon, ICON_NORMAL);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
tray_set_flash (TrayIcon icon)
|
||||
{
|
||||
if (!sticon)
|
||||
return;
|
||||
|
||||
/* already flashing the same icon */
|
||||
if (flash_tag && gtk_status_icon_get_pixbuf (sticon) == icon)
|
||||
return;
|
||||
|
||||
/* no flashing if window is focused */
|
||||
if (tray_get_window_status () == WS_FOCUSED)
|
||||
return;
|
||||
|
||||
tray_stop_flash ();
|
||||
|
||||
gtk_status_icon_set_from_pixbuf (sticon, icon);
|
||||
flash_tag = g_timeout_add (TIMEOUT, (GSourceFunc) tray_timeout_cb, icon);
|
||||
}
|
||||
|
||||
void
|
||||
fe_tray_set_flash (const char *filename1, const char *filename2, int tout)
|
||||
{
|
||||
tray_apply_setup ();
|
||||
if (!sticon)
|
||||
return;
|
||||
|
||||
tray_stop_flash ();
|
||||
|
||||
if (tout == -1)
|
||||
tout = TIMEOUT;
|
||||
|
||||
custom_icon1 = tray_icon_from_file (filename1);
|
||||
if (filename2)
|
||||
custom_icon2 = tray_icon_from_file (filename2);
|
||||
|
||||
gtk_status_icon_set_from_pixbuf (sticon, custom_icon1);
|
||||
flash_tag = g_timeout_add (tout, (GSourceFunc) tray_timeout_cb, NULL);
|
||||
tray_status = TS_CUSTOM;
|
||||
}
|
||||
|
||||
void
|
||||
fe_tray_set_icon (feicon icon)
|
||||
{
|
||||
tray_apply_setup ();
|
||||
if (!sticon)
|
||||
return;
|
||||
|
||||
tray_stop_flash ();
|
||||
|
||||
switch (icon)
|
||||
{
|
||||
case FE_ICON_NORMAL:
|
||||
break;
|
||||
case FE_ICON_MESSAGE:
|
||||
tray_set_flash (ICON_MSG);
|
||||
break;
|
||||
case FE_ICON_HIGHLIGHT:
|
||||
case FE_ICON_PRIVMSG:
|
||||
tray_set_flash (ICON_HILIGHT);
|
||||
break;
|
||||
case FE_ICON_FILEOFFER:
|
||||
tray_set_flash (ICON_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fe_tray_set_file (const char *filename)
|
||||
{
|
||||
tray_apply_setup ();
|
||||
if (!sticon)
|
||||
return;
|
||||
|
||||
tray_stop_flash ();
|
||||
|
||||
if (filename)
|
||||
{
|
||||
custom_icon1 = tray_icon_from_file (filename);
|
||||
gtk_status_icon_set_from_pixbuf (sticon, custom_icon1);
|
||||
tray_status = TS_CUSTOM;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
tray_toggle_visibility (gboolean force_hide)
|
||||
{
|
||||
static int x, y;
|
||||
static GdkScreen *screen;
|
||||
GtkWindow *win;
|
||||
|
||||
if (!sticon)
|
||||
return FALSE;
|
||||
|
||||
/* ph may have an invalid context now */
|
||||
xchat_set_context (ph, xchat_find_context (ph, NULL, NULL));
|
||||
|
||||
win = (GtkWindow *)xchat_get_info (ph, "win_ptr");
|
||||
|
||||
tray_stop_flash ();
|
||||
tray_reset_counts ();
|
||||
|
||||
if (!win)
|
||||
return FALSE;
|
||||
|
||||
#if GTK_CHECK_VERSION(2,20,0)
|
||||
if (force_hide || gtk_widget_get_visible (GTK_WIDGET (win)))
|
||||
#else
|
||||
if (force_hide || GTK_WIDGET_VISIBLE (win))
|
||||
#endif
|
||||
{
|
||||
gtk_window_get_position (win, &x, &y);
|
||||
screen = gtk_window_get_screen (win);
|
||||
gtk_widget_hide (GTK_WIDGET (win));
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_window_set_screen (win, screen);
|
||||
gtk_window_move (win, x, y);
|
||||
gtk_widget_show (GTK_WIDGET (win));
|
||||
gtk_window_present (win);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
tray_menu_restore_cb (GtkWidget *item, gpointer userdata)
|
||||
{
|
||||
tray_toggle_visibility (FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
tray_menu_quit_cb (GtkWidget *item, gpointer userdata)
|
||||
{
|
||||
mg_open_quit_dialog (FALSE);
|
||||
}
|
||||
|
||||
/* returns 0-mixed 1-away 2-back */
|
||||
|
||||
static int
|
||||
tray_find_away_status (void)
|
||||
{
|
||||
GSList *list;
|
||||
server *serv;
|
||||
int away = 0;
|
||||
int back = 0;
|
||||
|
||||
for (list = serv_list; list; list = list->next)
|
||||
{
|
||||
serv = list->data;
|
||||
|
||||
if (serv->is_away || serv->reconnect_away)
|
||||
away++;
|
||||
else
|
||||
back++;
|
||||
}
|
||||
|
||||
if (away && back)
|
||||
return 0;
|
||||
|
||||
if (away)
|
||||
return 1;
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static void
|
||||
tray_foreach_server (GtkWidget *item, char *cmd)
|
||||
{
|
||||
GSList *list;
|
||||
server *serv;
|
||||
|
||||
for (list = serv_list; list; list = list->next)
|
||||
{
|
||||
serv = list->data;
|
||||
if (serv->connected)
|
||||
handle_command (serv->server_session, cmd, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
tray_make_item (GtkWidget *menu, char *label, void *callback, void *userdata)
|
||||
{
|
||||
GtkWidget *item;
|
||||
|
||||
if (label)
|
||||
item = gtk_menu_item_new_with_mnemonic (label);
|
||||
else
|
||||
item = gtk_menu_item_new ();
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
|
||||
g_signal_connect (G_OBJECT (item), "activate",
|
||||
G_CALLBACK (callback), userdata);
|
||||
gtk_widget_show (item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static void
|
||||
tray_toggle_cb (GtkCheckMenuItem *item, unsigned int *setting)
|
||||
{
|
||||
*setting = item->active;
|
||||
}
|
||||
|
||||
static void
|
||||
blink_item (unsigned int *setting, GtkWidget *menu, char *label)
|
||||
{
|
||||
menu_toggle_item (label, menu, tray_toggle_cb, setting, *setting);
|
||||
}
|
||||
|
||||
static void
|
||||
tray_menu_destroy (GtkWidget *menu, gpointer userdata)
|
||||
{
|
||||
gtk_widget_destroy (menu);
|
||||
g_object_unref (menu);
|
||||
}
|
||||
|
||||
static void
|
||||
tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata)
|
||||
{
|
||||
GtkWidget *menu;
|
||||
GtkWidget *submenu;
|
||||
GtkWidget *item;
|
||||
int away_status;
|
||||
|
||||
/* ph may have an invalid context now */
|
||||
xchat_set_context (ph, xchat_find_context (ph, NULL, NULL));
|
||||
|
||||
menu = gtk_menu_new ();
|
||||
/*gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));*/
|
||||
|
||||
if (tray_get_window_status () == WS_HIDDEN)
|
||||
tray_make_item (menu, _("_Restore"), tray_menu_restore_cb, NULL);
|
||||
else
|
||||
tray_make_item (menu, _("_Hide"), tray_menu_restore_cb, NULL);
|
||||
tray_make_item (menu, NULL, tray_menu_quit_cb, NULL);
|
||||
|
||||
submenu = mg_submenu (menu, _("_Blink on"));
|
||||
blink_item (&prefs.input_tray_chans, submenu, _("Channel Message"));
|
||||
blink_item (&prefs.input_tray_priv, submenu, _("Private Message"));
|
||||
blink_item (&prefs.input_tray_hilight, submenu, _("Highlighted Message"));
|
||||
/*blink_item (BIT_FILEOFFER, submenu, _("File Offer"));*/
|
||||
|
||||
submenu = mg_submenu (menu, _("_Change status"));
|
||||
away_status = tray_find_away_status ();
|
||||
item = tray_make_item (submenu, _("_Away"), tray_foreach_server, "away");
|
||||
if (away_status == 1)
|
||||
gtk_widget_set_sensitive (item, FALSE);
|
||||
item = tray_make_item (submenu, _("_Back"), tray_foreach_server, "back");
|
||||
if (away_status == 2)
|
||||
gtk_widget_set_sensitive (item, FALSE);
|
||||
|
||||
tray_make_item (menu, NULL, tray_menu_quit_cb, NULL);
|
||||
mg_create_icon_item (_("_Quit"), GTK_STOCK_QUIT, menu, tray_menu_quit_cb, NULL);
|
||||
|
||||
menu_add_plugin_items (menu, "\x5$TRAY", NULL);
|
||||
|
||||
g_object_ref (menu);
|
||||
g_object_ref_sink (menu);
|
||||
g_object_unref (menu);
|
||||
g_signal_connect (G_OBJECT (menu), "selection-done",
|
||||
G_CALLBACK (tray_menu_destroy), NULL);
|
||||
|
||||
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, gtk_status_icon_position_menu,
|
||||
userdata, button, time);
|
||||
}
|
||||
|
||||
static void
|
||||
tray_init (void)
|
||||
{
|
||||
flash_tag = 0;
|
||||
tray_status = TS_NONE;
|
||||
custom_icon1 = NULL;
|
||||
custom_icon2 = NULL;
|
||||
|
||||
sticon = gtk_status_icon_new_from_pixbuf (ICON_NORMAL);
|
||||
if (!sticon)
|
||||
return;
|
||||
g_signal_connect (G_OBJECT (sticon), "popup-menu",
|
||||
G_CALLBACK (tray_menu_cb), sticon);
|
||||
g_signal_connect (G_OBJECT (sticon), "activate",
|
||||
G_CALLBACK (tray_menu_restore_cb), NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
tray_hilight_cb (char *word[], void *userdata)
|
||||
{
|
||||
/*if (tray_status == TS_HIGHLIGHT)
|
||||
return XCHAT_EAT_NONE;*/
|
||||
|
||||
if (prefs.input_tray_hilight)
|
||||
{
|
||||
tray_set_flash (ICON_HILIGHT);
|
||||
|
||||
/* FIXME: hides any previous private messages */
|
||||
tray_hilight_count++;
|
||||
if (tray_hilight_count == 1)
|
||||
tray_set_tipf (_("XChat: Highlighted message from: %s (%s)"),
|
||||
word[1], xchat_get_info (ph, "channel"));
|
||||
else
|
||||
tray_set_tipf (_("XChat: %u highlighted messages, latest from: %s (%s)"),
|
||||
tray_hilight_count, word[1], xchat_get_info (ph, "channel"));
|
||||
}
|
||||
|
||||
if (prefs.input_balloon_hilight)
|
||||
tray_set_balloonf (word[2], _("XChat: Highlighted message from: %s (%s)"),
|
||||
word[1], xchat_get_info (ph, "channel"));
|
||||
|
||||
return XCHAT_EAT_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
tray_message_cb (char *word[], void *userdata)
|
||||
{
|
||||
if (/*tray_status == TS_MESSAGE ||*/ tray_status == TS_HIGHLIGHT)
|
||||
return XCHAT_EAT_NONE;
|
||||
|
||||
if (prefs.input_tray_chans)
|
||||
{
|
||||
tray_set_flash (ICON_MSG);
|
||||
|
||||
tray_pub_count++;
|
||||
if (tray_pub_count == 1)
|
||||
tray_set_tipf (_("XChat: New public message from: %s (%s)"),
|
||||
word[1], xchat_get_info (ph, "channel"));
|
||||
else
|
||||
tray_set_tipf (_("XChat: %u new public messages."), tray_pub_count);
|
||||
}
|
||||
|
||||
if (prefs.input_balloon_chans)
|
||||
tray_set_balloonf (word[2], _("XChat: New public message from: %s (%s)"),
|
||||
word[1], xchat_get_info (ph, "channel"));
|
||||
|
||||
return XCHAT_EAT_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
tray_priv (char *from, char *text)
|
||||
{
|
||||
const char *network;
|
||||
|
||||
if (alert_match_word (from, prefs.irc_no_hilight))
|
||||
return;
|
||||
|
||||
tray_set_flash (ICON_HILIGHT);
|
||||
|
||||
network = xchat_get_info (ph, "network");
|
||||
if (!network)
|
||||
network = xchat_get_info (ph, "server");
|
||||
|
||||
tray_priv_count++;
|
||||
if (tray_priv_count == 1)
|
||||
tray_set_tipf (_("XChat: Private message from: %s (%s)"),
|
||||
from, network);
|
||||
else
|
||||
tray_set_tipf (_("XChat: %u private messages, latest from: %s (%s)"),
|
||||
tray_priv_count, from, network);
|
||||
|
||||
if (prefs.input_balloon_priv)
|
||||
tray_set_balloonf (text, _("XChat: Private message from: %s (%s)"),
|
||||
from, network);
|
||||
}
|
||||
|
||||
static int
|
||||
tray_priv_cb (char *word[], void *userdata)
|
||||
{
|
||||
/*if (tray_status == TS_HIGHLIGHT)
|
||||
return XCHAT_EAT_NONE;*/
|
||||
|
||||
if (prefs.input_tray_priv)
|
||||
tray_priv (word[1], word[2]);
|
||||
|
||||
return XCHAT_EAT_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
tray_invited_cb (char *word[], void *userdata)
|
||||
{
|
||||
/*if (tray_status == TS_HIGHLIGHT)
|
||||
return XCHAT_EAT_NONE;*/
|
||||
|
||||
if (prefs.input_tray_priv)
|
||||
tray_priv (word[2], "Invited");
|
||||
|
||||
return XCHAT_EAT_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
tray_dcc_cb (char *word[], void *userdata)
|
||||
{
|
||||
const char *network;
|
||||
|
||||
/* if (tray_status == TS_FILEOFFER)
|
||||
return XCHAT_EAT_NONE;*/
|
||||
|
||||
network = xchat_get_info (ph, "network");
|
||||
if (!network)
|
||||
network = xchat_get_info (ph, "server");
|
||||
|
||||
if (prefs.input_tray_priv)
|
||||
{
|
||||
tray_set_flash (ICON_FILE);
|
||||
|
||||
tray_file_count++;
|
||||
if (tray_file_count == 1)
|
||||
tray_set_tipf (_("XChat: File offer from: %s (%s)"),
|
||||
word[1], network);
|
||||
else
|
||||
tray_set_tipf (_("XChat: %u file offers, latest from: %s (%s)"),
|
||||
tray_file_count, word[1], network);
|
||||
}
|
||||
|
||||
if (prefs.input_balloon_priv)
|
||||
tray_set_balloonf ("", _("XChat: File offer from: %s (%s)"),
|
||||
word[1], network);
|
||||
|
||||
return XCHAT_EAT_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
tray_focus_cb (char *word[], void *userdata)
|
||||
{
|
||||
tray_stop_flash ();
|
||||
tray_reset_counts ();
|
||||
return XCHAT_EAT_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
tray_cleanup (void)
|
||||
{
|
||||
tray_stop_flash ();
|
||||
|
||||
if (sticon)
|
||||
{
|
||||
g_object_unref ((GObject *)sticon);
|
||||
sticon = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tray_apply_setup (void)
|
||||
{
|
||||
if (sticon)
|
||||
{
|
||||
if (!prefs.gui_tray)
|
||||
tray_cleanup ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prefs.gui_tray)
|
||||
tray_init ();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
tray_plugin_init (xchat_plugin *plugin_handle, char **plugin_name,
|
||||
char **plugin_desc, char **plugin_version, char *arg)
|
||||
{
|
||||
/* we need to save this for use with any xchat_* functions */
|
||||
ph = plugin_handle;
|
||||
|
||||
*plugin_name = "";
|
||||
*plugin_desc = "";
|
||||
*plugin_version = "";
|
||||
|
||||
xchat_hook_print (ph, "Channel Msg Hilight", -1, tray_hilight_cb, NULL);
|
||||
xchat_hook_print (ph, "Channel Action Hilight", -1, tray_hilight_cb, NULL);
|
||||
|
||||
xchat_hook_print (ph, "Channel Message", -1, tray_message_cb, NULL);
|
||||
xchat_hook_print (ph, "Channel Action", -1, tray_message_cb, NULL);
|
||||
xchat_hook_print (ph, "Channel Notice", -1, tray_message_cb, NULL);
|
||||
|
||||
xchat_hook_print (ph, "Private Message", -1, tray_priv_cb, NULL);
|
||||
xchat_hook_print (ph, "Private Message to Dialog", -1, tray_priv_cb, NULL);
|
||||
xchat_hook_print (ph, "Notice", -1, tray_priv_cb, NULL);
|
||||
xchat_hook_print (ph, "Invited", -1, tray_invited_cb, NULL);
|
||||
|
||||
xchat_hook_print (ph, "DCC Offer", -1, tray_dcc_cb, NULL);
|
||||
|
||||
xchat_hook_print (ph, "Focus Window", -1, tray_focus_cb, NULL);
|
||||
|
||||
if (prefs.gui_tray)
|
||||
tray_init ();
|
||||
|
||||
return 1; /* return 1 for success */
|
||||
}
|
||||
|
||||
int
|
||||
tray_plugin_deinit (xchat_plugin *plugin_handle)
|
||||
{
|
||||
#ifdef WIN32
|
||||
tray_cleanup ();
|
||||
#elif defined(LIBNOTIFY)
|
||||
libnotify_cleanup ();
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
4
src/fe-gtk/plugin-tray.h
Normal file
4
src/fe-gtk/plugin-tray.h
Normal file
@@ -0,0 +1,4 @@
|
||||
int tray_plugin_init (void *, char **, char **, char **, char *);
|
||||
int tray_plugin_deinit (void *);
|
||||
gboolean tray_toggle_visibility (gboolean force_hide);
|
||||
void tray_apply_setup (void);
|
||||
239
src/fe-gtk/plugingui.c
Normal file
239
src/fe-gtk/plugingui.c
Normal file
@@ -0,0 +1,239 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkdialog.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkbox.h>
|
||||
#include <gtk/gtkscrolledwindow.h>
|
||||
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gtk/gtktreeview.h>
|
||||
#include <gtk/gtktreeselection.h>
|
||||
#include <gtk/gtkcellrenderertext.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#define PLUGIN_C
|
||||
typedef struct session xchat_context;
|
||||
#include "../common/xchat-plugin.h"
|
||||
#include "../common/plugin.h"
|
||||
#include "../common/util.h"
|
||||
#include "../common/outbound.h"
|
||||
#include "../common/fe.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "gtkutil.h"
|
||||
|
||||
/* model for the plugin treeview */
|
||||
enum
|
||||
{
|
||||
NAME_COLUMN,
|
||||
VERSION_COLUMN,
|
||||
FILE_COLUMN,
|
||||
DESC_COLUMN,
|
||||
N_COLUMNS
|
||||
};
|
||||
|
||||
static GtkWidget *plugin_window = NULL;
|
||||
|
||||
|
||||
static GtkWidget *
|
||||
plugingui_treeview_new (GtkWidget *box)
|
||||
{
|
||||
GtkListStore *store;
|
||||
GtkWidget *view;
|
||||
GtkTreeViewColumn *col;
|
||||
int col_id;
|
||||
|
||||
store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
|
||||
G_TYPE_STRING, G_TYPE_STRING);
|
||||
g_return_val_if_fail (store != NULL, NULL);
|
||||
view = gtkutil_treeview_new (box, GTK_TREE_MODEL (store), NULL,
|
||||
NAME_COLUMN, _("Name"),
|
||||
VERSION_COLUMN, _("Version"),
|
||||
FILE_COLUMN, _("File"),
|
||||
DESC_COLUMN, _("Description"), -1);
|
||||
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
|
||||
for (col_id=0; (col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), col_id));
|
||||
col_id++)
|
||||
gtk_tree_view_column_set_alignment (col, 0.5);
|
||||
|
||||
gtk_widget_show (view);
|
||||
return view;
|
||||
}
|
||||
|
||||
static void
|
||||
plugingui_close_button (GtkWidget * wid, gpointer a)
|
||||
{
|
||||
gtk_widget_destroy (plugin_window);
|
||||
}
|
||||
|
||||
static void
|
||||
plugingui_close (GtkWidget * wid, gpointer a)
|
||||
{
|
||||
plugin_window = NULL;
|
||||
}
|
||||
|
||||
extern GSList *plugin_list;
|
||||
|
||||
void
|
||||
fe_pluginlist_update (void)
|
||||
{
|
||||
xchat_plugin *pl;
|
||||
GSList *list;
|
||||
GtkTreeView *view;
|
||||
GtkListStore *store;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!plugin_window)
|
||||
return;
|
||||
|
||||
view = g_object_get_data (G_OBJECT (plugin_window), "view");
|
||||
store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
|
||||
gtk_list_store_clear (store);
|
||||
|
||||
list = plugin_list;
|
||||
while (list)
|
||||
{
|
||||
pl = list->data;
|
||||
if (pl->version[0] != 0)
|
||||
{
|
||||
gtk_list_store_append (store, &iter);
|
||||
gtk_list_store_set (store, &iter, NAME_COLUMN, pl->name,
|
||||
VERSION_COLUMN, pl->version,
|
||||
FILE_COLUMN, file_part (pl->filename),
|
||||
DESC_COLUMN, pl->desc, -1);
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
plugingui_load_cb (session *sess, char *file)
|
||||
{
|
||||
if (file)
|
||||
{
|
||||
char *buf = malloc (strlen (file) + 9);
|
||||
|
||||
if (strchr (file, ' '))
|
||||
sprintf (buf, "LOAD \"%s\"", file);
|
||||
else
|
||||
sprintf (buf, "LOAD %s", file);
|
||||
handle_command (sess, buf, FALSE);
|
||||
free (buf);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
plugingui_load (void)
|
||||
{
|
||||
gtkutil_file_req (_("Select a Plugin or Script to load"), plugingui_load_cb,
|
||||
current_sess, NULL, FRF_ADDFOLDER);
|
||||
}
|
||||
|
||||
static void
|
||||
plugingui_loadbutton_cb (GtkWidget * wid, gpointer unused)
|
||||
{
|
||||
plugingui_load ();
|
||||
}
|
||||
|
||||
static void
|
||||
plugingui_unload (GtkWidget * wid, gpointer unused)
|
||||
{
|
||||
int len;
|
||||
char *modname, *file, *buf;
|
||||
GtkTreeView *view;
|
||||
GtkTreeIter iter;
|
||||
|
||||
view = g_object_get_data (G_OBJECT (plugin_window), "view");
|
||||
if (!gtkutil_treeview_get_selected (view, &iter, NAME_COLUMN, &modname,
|
||||
FILE_COLUMN, &file, -1))
|
||||
return;
|
||||
|
||||
len = strlen (file);
|
||||
#ifdef WIN32
|
||||
if (len > 4 && strcasecmp (file + len - 4, ".dll") == 0)
|
||||
#else
|
||||
#if defined(__hpux)
|
||||
if (len > 3 && strcasecmp (file + len - 3, ".sl") == 0)
|
||||
#else
|
||||
if (len > 3 && strcasecmp (file + len - 3, ".so") == 0)
|
||||
#endif
|
||||
#endif
|
||||
{
|
||||
if (plugin_kill (modname, FALSE) == 2)
|
||||
fe_message (_("That plugin is refusing to unload.\n"), FE_MSG_ERROR);
|
||||
} else
|
||||
{
|
||||
/* let python.so or perl.so handle it */
|
||||
buf = malloc (strlen (file) + 10);
|
||||
if (strchr (file, ' '))
|
||||
sprintf (buf, "UNLOAD \"%s\"", file);
|
||||
else
|
||||
sprintf (buf, "UNLOAD %s", file);
|
||||
handle_command (current_sess, buf, FALSE);
|
||||
free (buf);
|
||||
}
|
||||
|
||||
g_free (modname);
|
||||
g_free (file);
|
||||
}
|
||||
|
||||
void
|
||||
plugingui_open (void)
|
||||
{
|
||||
GtkWidget *view;
|
||||
GtkWidget *vbox, *action_area;
|
||||
|
||||
if (plugin_window)
|
||||
{
|
||||
gtk_window_present (GTK_WINDOW (plugin_window));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin_window = gtk_dialog_new ();
|
||||
g_signal_connect (G_OBJECT (plugin_window), "destroy",
|
||||
G_CALLBACK (plugingui_close), 0);
|
||||
gtk_window_set_default_size (GTK_WINDOW (plugin_window), 500, 250);
|
||||
vbox = GTK_DIALOG (plugin_window)->vbox;
|
||||
action_area = GTK_DIALOG (plugin_window)->action_area;
|
||||
gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
|
||||
gtk_window_set_position (GTK_WINDOW (plugin_window), GTK_WIN_POS_CENTER);
|
||||
gtk_window_set_title (GTK_WINDOW (plugin_window), _("XChat: Plugins and Scripts"));
|
||||
|
||||
view = plugingui_treeview_new (vbox);
|
||||
g_object_set_data (G_OBJECT (plugin_window), "view", view);
|
||||
|
||||
gtkutil_button (action_area, GTK_STOCK_REVERT_TO_SAVED, NULL,
|
||||
plugingui_loadbutton_cb, NULL, _("_Load..."));
|
||||
|
||||
gtkutil_button (action_area, GTK_STOCK_DELETE, NULL,
|
||||
plugingui_unload, NULL, _("_UnLoad"));
|
||||
|
||||
gtkutil_button (action_area,
|
||||
GTK_STOCK_CLOSE, NULL, plugingui_close_button,
|
||||
NULL, _("_Close"));
|
||||
|
||||
fe_pluginlist_update ();
|
||||
|
||||
gtk_widget_show (plugin_window);
|
||||
}
|
||||
2
src/fe-gtk/plugingui.h
Normal file
2
src/fe-gtk/plugingui.h
Normal file
@@ -0,0 +1,2 @@
|
||||
void plugingui_open (void);
|
||||
void plugingui_load (void);
|
||||
152
src/fe-gtk/rawlog.c
Normal file
152
src/fe-gtk/rawlog.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtkhbbox.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkvscrollbar.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "../common/cfgfiles.h"
|
||||
#include "../common/server.h"
|
||||
#include "gtkutil.h"
|
||||
#include "palette.h"
|
||||
#include "maingui.h"
|
||||
#include "rawlog.h"
|
||||
#include "xtext.h"
|
||||
|
||||
|
||||
static void
|
||||
close_rawlog (GtkWidget *wid, server *serv)
|
||||
{
|
||||
if (is_server (serv))
|
||||
serv->gui->rawlog_window = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
rawlog_save (server *serv, char *file)
|
||||
{
|
||||
int fh = -1;
|
||||
|
||||
if (file)
|
||||
{
|
||||
if (serv->gui->rawlog_window)
|
||||
fh = xchat_open_file (file, O_TRUNC | O_WRONLY | O_CREAT,
|
||||
0600, XOF_DOMODE | XOF_FULLPATH);
|
||||
if (fh != -1)
|
||||
{
|
||||
gtk_xtext_save (GTK_XTEXT (serv->gui->rawlog_textlist), fh);
|
||||
close (fh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
rawlog_clearbutton (GtkWidget * wid, server *serv)
|
||||
{
|
||||
gtk_xtext_clear (GTK_XTEXT (serv->gui->rawlog_textlist)->buffer, 0);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
rawlog_savebutton (GtkWidget * wid, server *serv)
|
||||
{
|
||||
gtkutil_file_req (_("Save As..."), rawlog_save, serv, NULL, FRF_WRITE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
open_rawlog (struct server *serv)
|
||||
{
|
||||
GtkWidget *hbox, *vscrollbar, *vbox;
|
||||
char tbuf[256];
|
||||
|
||||
if (serv->gui->rawlog_window)
|
||||
{
|
||||
mg_bring_tofront (serv->gui->rawlog_window);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf (tbuf, sizeof tbuf, _("XChat: Rawlog (%s)"), serv->servername);
|
||||
serv->gui->rawlog_window =
|
||||
mg_create_generic_tab ("RawLog", tbuf, FALSE, TRUE, close_rawlog, serv,
|
||||
640, 320, &vbox, serv);
|
||||
|
||||
hbox = gtk_hbox_new (FALSE, 2);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), hbox);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (hbox), 4);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
serv->gui->rawlog_textlist = gtk_xtext_new (colors, 0);
|
||||
gtk_xtext_set_tint (GTK_XTEXT (serv->gui->rawlog_textlist), prefs.tint_red, prefs.tint_green, prefs.tint_blue);
|
||||
gtk_xtext_set_background (GTK_XTEXT (serv->gui->rawlog_textlist),
|
||||
channelwin_pix, prefs.transparent);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (hbox), serv->gui->rawlog_textlist);
|
||||
gtk_xtext_set_font (GTK_XTEXT (serv->gui->rawlog_textlist), prefs.font_normal);
|
||||
GTK_XTEXT (serv->gui->rawlog_textlist)->ignore_hidden = 1;
|
||||
gtk_widget_show (serv->gui->rawlog_textlist);
|
||||
|
||||
vscrollbar = gtk_vscrollbar_new (GTK_XTEXT (serv->gui->rawlog_textlist)->adj);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), vscrollbar, FALSE, FALSE, 0);
|
||||
show_and_unfocus (vscrollbar);
|
||||
|
||||
hbox = gtk_hbutton_box_new ();
|
||||
gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_SPREAD);
|
||||
gtk_box_pack_end (GTK_BOX (vbox), hbox, 0, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
gtkutil_button (hbox, GTK_STOCK_CLEAR, NULL, rawlog_clearbutton,
|
||||
serv, _("Clear rawlog"));
|
||||
|
||||
gtkutil_button (hbox, GTK_STOCK_SAVE_AS, NULL, rawlog_savebutton,
|
||||
serv, _("Save As..."));
|
||||
|
||||
gtk_widget_show (serv->gui->rawlog_window);
|
||||
}
|
||||
|
||||
void
|
||||
fe_add_rawlog (server *serv, char *text, int len, int outbound)
|
||||
{
|
||||
char *new_text;
|
||||
|
||||
if (!serv->gui->rawlog_window)
|
||||
return;
|
||||
|
||||
new_text = malloc (len + 7);
|
||||
|
||||
len = sprintf (new_text, "\0033>>\017 %s", text);
|
||||
if (outbound)
|
||||
{
|
||||
new_text[1] = '4';
|
||||
new_text[2] = '<';
|
||||
new_text[3] = '<';
|
||||
}
|
||||
gtk_xtext_append (GTK_XTEXT (serv->gui->rawlog_textlist)->buffer, new_text, len);
|
||||
free (new_text);
|
||||
}
|
||||
1
src/fe-gtk/rawlog.h
Normal file
1
src/fe-gtk/rawlog.h
Normal file
@@ -0,0 +1 @@
|
||||
void open_rawlog (server *serv);
|
||||
159
src/fe-gtk/search.c
Normal file
159
src/fe-gtk/search.c
Normal file
@@ -0,0 +1,159 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkvbox.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkhbbox.h>
|
||||
#include <gtk/gtkhseparator.h>
|
||||
#include <gtk/gtkvseparator.h>
|
||||
#include <gtk/gtkradiobutton.h>
|
||||
#include <gtk/gtktogglebutton.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/fe.h"
|
||||
#include "../common/util.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "gtkutil.h"
|
||||
#include "xtext.h"
|
||||
#include "maingui.h"
|
||||
|
||||
|
||||
static textentry *last; /* our last search pos */
|
||||
static int case_match = 0;
|
||||
static int search_backward = 0;
|
||||
|
||||
|
||||
static void
|
||||
search_search (session * sess, const gchar *text)
|
||||
{
|
||||
if (!is_session (sess))
|
||||
{
|
||||
fe_message (_("The window you opened this Search "
|
||||
"for doesn't exist anymore."), FE_MSG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
last = gtk_xtext_search (GTK_XTEXT (sess->gui->xtext), text,
|
||||
last, case_match, search_backward);
|
||||
if (!last)
|
||||
fe_message (_("Search hit end, not found."), FE_MSG_ERROR);
|
||||
}
|
||||
|
||||
static void
|
||||
search_find_cb (GtkWidget * button, session * sess)
|
||||
{
|
||||
GtkEntry *entry;
|
||||
const gchar *text;
|
||||
|
||||
entry = g_object_get_data (G_OBJECT (button), "e");
|
||||
text = gtk_entry_get_text (entry);
|
||||
search_search (sess, text);
|
||||
}
|
||||
|
||||
static void
|
||||
search_close_cb (GtkWidget * button, GtkWidget * win)
|
||||
{
|
||||
gtk_widget_destroy (win);
|
||||
}
|
||||
|
||||
static void
|
||||
search_entry_cb (GtkWidget * entry, session * sess)
|
||||
{
|
||||
search_search (sess, gtk_entry_get_text (GTK_ENTRY (entry)));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
search_key_cb (GtkWidget * window, GdkEventKey * key, gpointer userdata)
|
||||
{
|
||||
if (key->keyval == GDK_Escape)
|
||||
gtk_widget_destroy (window);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
search_caseign_cb (GtkToggleButton * but, session * sess)
|
||||
{
|
||||
case_match = (but->active)? 1: 0;
|
||||
}
|
||||
|
||||
static void
|
||||
search_dirbwd_cb (GtkToggleButton * but, session * sess)
|
||||
{
|
||||
search_backward = (but->active)? 1: 0;
|
||||
}
|
||||
|
||||
void
|
||||
search_open (session * sess)
|
||||
{
|
||||
GtkWidget *win, *hbox, *vbox, *entry, *wid;
|
||||
|
||||
last = NULL;
|
||||
win = mg_create_generic_tab ("search", _("XChat: Search"), TRUE, FALSE,
|
||||
NULL, NULL, 0, 0, &vbox, 0);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (win), 12);
|
||||
gtk_box_set_spacing (GTK_BOX (vbox), 4);
|
||||
|
||||
hbox = gtk_hbox_new (0, 10);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), hbox);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
gtkutil_label_new (_("Find:"), hbox);
|
||||
|
||||
entry = gtk_entry_new ();
|
||||
g_signal_connect (G_OBJECT (entry), "activate",
|
||||
G_CALLBACK (search_entry_cb), sess);
|
||||
gtk_container_add (GTK_CONTAINER (hbox), entry);
|
||||
gtk_widget_show (entry);
|
||||
gtk_widget_grab_focus (entry);
|
||||
|
||||
wid = gtk_check_button_new_with_mnemonic (_("_Match case"));
|
||||
GTK_TOGGLE_BUTTON (wid)->active = case_match;
|
||||
g_signal_connect (G_OBJECT (wid), "toggled", G_CALLBACK (search_caseign_cb), sess);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), wid);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
wid = gtk_check_button_new_with_mnemonic (_("Search _backwards"));
|
||||
GTK_TOGGLE_BUTTON (wid)->active = search_backward;
|
||||
g_signal_connect (G_OBJECT (wid), "toggled", G_CALLBACK (search_dirbwd_cb), sess);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), wid);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
hbox = gtk_hbutton_box_new ();
|
||||
gtk_box_pack_start (GTK_BOX (vbox), hbox, 0, 0, 4);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
gtkutil_button (hbox, GTK_STOCK_CLOSE, 0, search_close_cb, win,
|
||||
_("_Close"));
|
||||
wid = gtkutil_button (hbox, GTK_STOCK_FIND, 0, search_find_cb, sess,
|
||||
_("_Find"));
|
||||
g_object_set_data (G_OBJECT (wid), "e", entry);
|
||||
|
||||
g_signal_connect (G_OBJECT (win), "key-press-event", G_CALLBACK (search_key_cb), win);
|
||||
|
||||
gtk_widget_show (win);
|
||||
}
|
||||
1
src/fe-gtk/search.h
Normal file
1
src/fe-gtk/search.h
Normal file
@@ -0,0 +1 @@
|
||||
void search_open (session * sess);
|
||||
1918
src/fe-gtk/servlistgui.c
Normal file
1918
src/fe-gtk/servlistgui.c
Normal file
File diff suppressed because it is too large
Load Diff
2137
src/fe-gtk/setup.c
Normal file
2137
src/fe-gtk/setup.c
Normal file
File diff suppressed because it is too large
Load Diff
1329
src/fe-gtk/sexy-spell-entry.c
Normal file
1329
src/fe-gtk/sexy-spell-entry.c
Normal file
File diff suppressed because it is too large
Load Diff
87
src/fe-gtk/sexy-spell-entry.h
Normal file
87
src/fe-gtk/sexy-spell-entry.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* @file libsexy/sexy-spell-entry.h Entry widget
|
||||
*
|
||||
* @Copyright (C) 2004-2006 Christian Hammond.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef _SEXY_SPELL_ENTRY_H_
|
||||
#define _SEXY_SPELL_ENTRY_H_
|
||||
|
||||
typedef struct _SexySpellEntry SexySpellEntry;
|
||||
typedef struct _SexySpellEntryClass SexySpellEntryClass;
|
||||
typedef struct _SexySpellEntryPriv SexySpellEntryPriv;
|
||||
|
||||
#include <gtk/gtkentry.h>
|
||||
|
||||
#define SEXY_TYPE_SPELL_ENTRY (sexy_spell_entry_get_type())
|
||||
#define SEXY_SPELL_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SEXY_TYPE_SPELL_ENTRY, SexySpellEntry))
|
||||
#define SEXY_SPELL_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SEXY_TYPE_SPELL_ENTRY, SexySpellEntryClass))
|
||||
#define SEXY_IS_SPELL_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SEXY_TYPE_SPELL_ENTRY))
|
||||
#define SEXY_IS_SPELL_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SEXY_TYPE_SPELL_ENTRY))
|
||||
#define SEXY_SPELL_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SEXY_TYPE_SPELL_ENTRY, SexySpellEntryClass))
|
||||
|
||||
#define SEXY_SPELL_ERROR (sexy_spell_error_quark())
|
||||
|
||||
typedef enum {
|
||||
SEXY_SPELL_ERROR_BACKEND,
|
||||
} SexySpellError;
|
||||
|
||||
struct _SexySpellEntry
|
||||
{
|
||||
GtkEntry parent_object;
|
||||
|
||||
SexySpellEntryPriv *priv;
|
||||
|
||||
void (*gtk_reserved1)(void);
|
||||
void (*gtk_reserved2)(void);
|
||||
void (*gtk_reserved3)(void);
|
||||
void (*gtk_reserved4)(void);
|
||||
};
|
||||
|
||||
struct _SexySpellEntryClass
|
||||
{
|
||||
GtkEntryClass parent_class;
|
||||
|
||||
/* Signals */
|
||||
gboolean (*word_check)(SexySpellEntry *entry, const gchar *word);
|
||||
|
||||
void (*gtk_reserved1)(void);
|
||||
void (*gtk_reserved2)(void);
|
||||
void (*gtk_reserved3)(void);
|
||||
void (*gtk_reserved4)(void);
|
||||
};
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GType sexy_spell_entry_get_type(void);
|
||||
GtkWidget *sexy_spell_entry_new(void);
|
||||
GQuark sexy_spell_error_quark(void);
|
||||
|
||||
GSList *sexy_spell_entry_get_languages(const SexySpellEntry *entry);
|
||||
gchar *sexy_spell_entry_get_language_name(const SexySpellEntry *entry, const gchar *lang);
|
||||
gboolean sexy_spell_entry_language_is_active(const SexySpellEntry *entry, const gchar *lang);
|
||||
gboolean sexy_spell_entry_activate_language(SexySpellEntry *entry, const gchar *lang, GError **error);
|
||||
void sexy_spell_entry_deactivate_language(SexySpellEntry *entry, const gchar *lang);
|
||||
gboolean sexy_spell_entry_set_active_languages(SexySpellEntry *entry, GSList *langs, GError **error);
|
||||
GSList *sexy_spell_entry_get_active_languages(SexySpellEntry *entry);
|
||||
gboolean sexy_spell_entry_is_checked(SexySpellEntry *entry);
|
||||
void sexy_spell_entry_set_checked(SexySpellEntry *entry, gboolean checked);
|
||||
void sexy_spell_entry_activate_default_languages(SexySpellEntry *entry);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
456
src/fe-gtk/textgui.c
Normal file
456
src/fe-gtk/textgui.c
Normal file
@@ -0,0 +1,456 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkhbbox.h>
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkvbox.h>
|
||||
#include <gtk/gtkvpaned.h>
|
||||
#include <gtk/gtkvscrollbar.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "../common/cfgfiles.h"
|
||||
#include "../common/outbound.h"
|
||||
#include "../common/fe.h"
|
||||
#include "../common/text.h"
|
||||
#include "gtkutil.h"
|
||||
#include "xtext.h"
|
||||
#include "maingui.h"
|
||||
#include "palette.h"
|
||||
#include "textgui.h"
|
||||
|
||||
extern struct text_event te[];
|
||||
extern char *pntevts_text[];
|
||||
extern char *pntevts[];
|
||||
|
||||
static GtkWidget *pevent_dialog = NULL, *pevent_dialog_twid,
|
||||
*pevent_dialog_entry,
|
||||
*pevent_dialog_list, *pevent_dialog_hlist;
|
||||
|
||||
enum
|
||||
{
|
||||
COL_EVENT_NAME,
|
||||
COL_EVENT_TEXT,
|
||||
COL_ROW,
|
||||
N_COLUMNS
|
||||
};
|
||||
|
||||
|
||||
/* this is only used in xtext.c for indented timestamping */
|
||||
int
|
||||
xtext_get_stamp_str (time_t tim, char **ret)
|
||||
{
|
||||
return get_stamp_str (prefs.stamp_format, tim, ret);
|
||||
}
|
||||
|
||||
static void
|
||||
PrintTextLine (xtext_buffer *xtbuf, unsigned char *text, int len, int indent, time_t timet)
|
||||
{
|
||||
unsigned char *tab, *new_text;
|
||||
int leftlen;
|
||||
|
||||
if (len == 0)
|
||||
len = 1;
|
||||
|
||||
if (!indent)
|
||||
{
|
||||
if (prefs.timestamp)
|
||||
{
|
||||
int stamp_size;
|
||||
char *stamp;
|
||||
|
||||
if (timet == 0)
|
||||
timet = time (0);
|
||||
|
||||
stamp_size = get_stamp_str (prefs.stamp_format, timet, &stamp);
|
||||
new_text = malloc (len + stamp_size + 1);
|
||||
memcpy (new_text, stamp, stamp_size);
|
||||
g_free (stamp);
|
||||
memcpy (new_text + stamp_size, text, len);
|
||||
gtk_xtext_append (xtbuf, new_text, len + stamp_size);
|
||||
free (new_text);
|
||||
} else
|
||||
gtk_xtext_append (xtbuf, text, len);
|
||||
return;
|
||||
}
|
||||
|
||||
tab = strchr (text, '\t');
|
||||
if (tab && tab < (text + len))
|
||||
{
|
||||
leftlen = tab - text;
|
||||
gtk_xtext_append_indent (xtbuf,
|
||||
text, leftlen, tab + 1, len - (leftlen + 1), timet);
|
||||
} else
|
||||
gtk_xtext_append_indent (xtbuf, 0, 0, text, len, timet);
|
||||
}
|
||||
|
||||
void
|
||||
PrintTextRaw (void *xtbuf, unsigned char *text, int indent, time_t stamp)
|
||||
{
|
||||
char *last_text = text;
|
||||
int len = 0;
|
||||
int beep_done = FALSE;
|
||||
|
||||
/* split the text into separate lines */
|
||||
while (1)
|
||||
{
|
||||
switch (*text)
|
||||
{
|
||||
case 0:
|
||||
PrintTextLine (xtbuf, last_text, len, indent, stamp);
|
||||
return;
|
||||
case '\n':
|
||||
PrintTextLine (xtbuf, last_text, len, indent, stamp);
|
||||
text++;
|
||||
if (*text == 0)
|
||||
return;
|
||||
last_text = text;
|
||||
len = 0;
|
||||
break;
|
||||
case ATTR_BEEP:
|
||||
*text = ' ';
|
||||
if (!beep_done) /* beeps may be slow, so only do 1 per line */
|
||||
{
|
||||
beep_done = TRUE;
|
||||
if (!prefs.filterbeep)
|
||||
gdk_beep ();
|
||||
}
|
||||
default:
|
||||
text++;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pevent_dialog_close (GtkWidget *wid, gpointer arg)
|
||||
{
|
||||
pevent_dialog = NULL;
|
||||
pevent_save (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
pevent_dialog_update (GtkWidget * wid, GtkWidget * twid)
|
||||
{
|
||||
int len, m;
|
||||
const char *text;
|
||||
char *out;
|
||||
int sig;
|
||||
GtkTreeIter iter;
|
||||
GtkListStore *store;
|
||||
|
||||
if (!gtkutil_treeview_get_selected (GTK_TREE_VIEW (pevent_dialog_list),
|
||||
&iter, COL_ROW, &sig, -1))
|
||||
return;
|
||||
|
||||
text = gtk_entry_get_text (GTK_ENTRY (wid));
|
||||
len = strlen (text);
|
||||
|
||||
if (pevt_build_string (text, &out, &m) != 0)
|
||||
{
|
||||
fe_message (_("There was an error parsing the string"), FE_MSG_ERROR);
|
||||
return;
|
||||
}
|
||||
if (m > (te[sig].num_args & 0x7f))
|
||||
{
|
||||
free (out);
|
||||
out = malloc (4096);
|
||||
snprintf (out, 4096,
|
||||
_("This signal is only passed %d args, $%d is invalid"),
|
||||
te[sig].num_args & 0x7f, m);
|
||||
fe_message (out, FE_MSG_WARN);
|
||||
free (out);
|
||||
return;
|
||||
}
|
||||
|
||||
store = (GtkListStore *)gtk_tree_view_get_model (GTK_TREE_VIEW (pevent_dialog_list));
|
||||
gtk_list_store_set (store, &iter, COL_EVENT_TEXT, text, -1);
|
||||
|
||||
if (pntevts_text[sig])
|
||||
free (pntevts_text[sig]);
|
||||
if (pntevts[sig])
|
||||
free (pntevts[sig]);
|
||||
|
||||
pntevts_text[sig] = malloc (len + 1);
|
||||
memcpy (pntevts_text[sig], text, len + 1);
|
||||
pntevts[sig] = out;
|
||||
|
||||
out = malloc (len + 2);
|
||||
memcpy (out, text, len + 1);
|
||||
out[len] = '\n';
|
||||
out[len + 1] = 0;
|
||||
check_special_chars (out, TRUE);
|
||||
|
||||
PrintTextRaw (GTK_XTEXT (twid)->buffer, out, 0, 0);
|
||||
free (out);
|
||||
|
||||
/* save this when we exit */
|
||||
prefs.save_pevents = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
pevent_dialog_hfill (GtkWidget * list, int e)
|
||||
{
|
||||
int i = 0;
|
||||
char *text;
|
||||
GtkTreeIter iter;
|
||||
GtkListStore *store;
|
||||
|
||||
store = (GtkListStore *)gtk_tree_view_get_model (GTK_TREE_VIEW (pevent_dialog_hlist));
|
||||
gtk_list_store_clear (store);
|
||||
while (i < (te[e].num_args & 0x7f))
|
||||
{
|
||||
text = _(te[e].help[i]);
|
||||
i++;
|
||||
if (text[0] == '\001')
|
||||
text++;
|
||||
gtk_list_store_insert_with_values (store, &iter, -1,
|
||||
0, i,
|
||||
1, text, -1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pevent_dialog_unselect (void)
|
||||
{
|
||||
gtk_entry_set_text (GTK_ENTRY (pevent_dialog_entry), "");
|
||||
gtk_list_store_clear ((GtkListStore *)gtk_tree_view_get_model (GTK_TREE_VIEW (pevent_dialog_hlist)));
|
||||
}
|
||||
|
||||
static void
|
||||
pevent_dialog_select (GtkTreeSelection *sel, gpointer store)
|
||||
{
|
||||
char *text;
|
||||
int sig;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!gtkutil_treeview_get_selected (GTK_TREE_VIEW (pevent_dialog_list),
|
||||
&iter, COL_ROW, &sig, -1))
|
||||
{
|
||||
pevent_dialog_unselect ();
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_tree_model_get (store, &iter, COL_EVENT_TEXT, &text, -1);
|
||||
gtk_entry_set_text (GTK_ENTRY (pevent_dialog_entry), text);
|
||||
g_free (text);
|
||||
pevent_dialog_hfill (pevent_dialog_hlist, sig);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pevent_dialog_fill (GtkWidget * list)
|
||||
{
|
||||
int i;
|
||||
GtkListStore *store;
|
||||
GtkTreeIter iter;
|
||||
|
||||
store = (GtkListStore *)gtk_tree_view_get_model (GTK_TREE_VIEW (list));
|
||||
gtk_list_store_clear (store);
|
||||
|
||||
i = NUM_XP;
|
||||
do
|
||||
{
|
||||
i--;
|
||||
gtk_list_store_insert_with_values (store, &iter, 0,
|
||||
COL_EVENT_NAME, te[i].name,
|
||||
COL_EVENT_TEXT, pntevts_text[i],
|
||||
COL_ROW, i, -1);
|
||||
}
|
||||
while (i != 0);
|
||||
}
|
||||
|
||||
static void
|
||||
pevent_save_req_cb (void *arg1, char *file)
|
||||
{
|
||||
if (file)
|
||||
pevent_save (file);
|
||||
}
|
||||
|
||||
static void
|
||||
pevent_save_cb (GtkWidget * wid, void *data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
gtkutil_file_req (_("Print Texts File"), pevent_save_req_cb, NULL,
|
||||
NULL, FRF_WRITE);
|
||||
return;
|
||||
}
|
||||
pevent_save (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
pevent_load_req_cb (void *arg1, char *file)
|
||||
{
|
||||
if (file)
|
||||
{
|
||||
pevent_load (file);
|
||||
pevent_make_pntevts ();
|
||||
pevent_dialog_fill (pevent_dialog_list);
|
||||
pevent_dialog_unselect ();
|
||||
prefs.save_pevents = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pevent_load_cb (GtkWidget * wid, void *data)
|
||||
{
|
||||
gtkutil_file_req (_("Print Texts File"), pevent_load_req_cb, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
pevent_ok_cb (GtkWidget * wid, void *data)
|
||||
{
|
||||
gtk_widget_destroy (pevent_dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
pevent_test_cb (GtkWidget * wid, GtkWidget * twid)
|
||||
{
|
||||
int len, n;
|
||||
char *out, *text;
|
||||
|
||||
for (n = 0; n < NUM_XP; n++)
|
||||
{
|
||||
text = _(pntevts_text[n]);
|
||||
len = strlen (text);
|
||||
|
||||
out = malloc (len + 2);
|
||||
memcpy (out, text, len + 1);
|
||||
out[len] = '\n';
|
||||
out[len + 1] = 0;
|
||||
check_special_chars (out, TRUE);
|
||||
|
||||
PrintTextRaw (GTK_XTEXT (twid)->buffer, out, 0, 0);
|
||||
free (out);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pevent_dialog_show ()
|
||||
{
|
||||
GtkWidget *vbox, *hbox, *tbox, *wid, *bh, *th;
|
||||
GtkListStore *store, *hstore;
|
||||
GtkTreeSelection *sel;
|
||||
|
||||
if (pevent_dialog)
|
||||
{
|
||||
mg_bring_tofront (pevent_dialog);
|
||||
return;
|
||||
}
|
||||
|
||||
pevent_dialog =
|
||||
mg_create_generic_tab ("edit events", _("Edit Events"),
|
||||
TRUE, FALSE, pevent_dialog_close, NULL,
|
||||
600, 455, &vbox, 0);
|
||||
|
||||
wid = gtk_vpaned_new ();
|
||||
th = gtk_vbox_new (0, 2);
|
||||
bh = gtk_vbox_new (0, 2);
|
||||
gtk_widget_show (th);
|
||||
gtk_widget_show (bh);
|
||||
gtk_paned_pack1 (GTK_PANED (wid), th, 1, 1);
|
||||
gtk_paned_pack2 (GTK_PANED (wid), bh, 0, 1);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), wid, 1, 1, 0);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING,
|
||||
G_TYPE_STRING, G_TYPE_INT);
|
||||
pevent_dialog_list = gtkutil_treeview_new (th, GTK_TREE_MODEL (store), NULL,
|
||||
COL_EVENT_NAME, _("Event"),
|
||||
COL_EVENT_TEXT, _("Text"), -1);
|
||||
sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (pevent_dialog_list));
|
||||
g_signal_connect (G_OBJECT (sel), "changed",
|
||||
G_CALLBACK (pevent_dialog_select), store);
|
||||
|
||||
pevent_dialog_twid = gtk_xtext_new (colors, 0);
|
||||
gtk_xtext_set_tint (GTK_XTEXT (pevent_dialog_twid), prefs.tint_red, prefs.tint_green, prefs.tint_blue);
|
||||
gtk_xtext_set_background (GTK_XTEXT (pevent_dialog_twid),
|
||||
channelwin_pix, prefs.transparent);
|
||||
|
||||
pevent_dialog_entry = gtk_entry_new_with_max_length (255);
|
||||
g_signal_connect (G_OBJECT (pevent_dialog_entry), "activate",
|
||||
G_CALLBACK (pevent_dialog_update), pevent_dialog_twid);
|
||||
gtk_box_pack_start (GTK_BOX (bh), pevent_dialog_entry, 0, 0, 0);
|
||||
gtk_widget_show (pevent_dialog_entry);
|
||||
|
||||
tbox = gtk_hbox_new (0, 0);
|
||||
gtk_container_add (GTK_CONTAINER (bh), tbox);
|
||||
gtk_widget_show (tbox);
|
||||
|
||||
gtk_widget_set_usize (pevent_dialog_twid, 150, 20);
|
||||
gtk_container_add (GTK_CONTAINER (tbox), pevent_dialog_twid);
|
||||
gtk_xtext_set_font (GTK_XTEXT (pevent_dialog_twid), prefs.font_normal);
|
||||
|
||||
wid = gtk_vscrollbar_new (GTK_XTEXT (pevent_dialog_twid)->adj);
|
||||
gtk_box_pack_start (GTK_BOX (tbox), wid, FALSE, FALSE, 0);
|
||||
show_and_unfocus (wid);
|
||||
|
||||
gtk_widget_show (pevent_dialog_twid);
|
||||
|
||||
hstore = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
|
||||
pevent_dialog_hlist = gtkutil_treeview_new (bh, GTK_TREE_MODEL (hstore),
|
||||
NULL,
|
||||
0, _("$ Number"),
|
||||
1, _("Description"), -1);
|
||||
gtk_widget_show (pevent_dialog_hlist);
|
||||
|
||||
pevent_dialog_fill (pevent_dialog_list);
|
||||
gtk_widget_show (pevent_dialog_list);
|
||||
|
||||
hbox = gtk_hbutton_box_new ();
|
||||
gtk_box_pack_end (GTK_BOX (vbox), hbox, 0, 0, 2);
|
||||
/*wid = gtk_button_new_with_label (_("Save"));
|
||||
gtk_box_pack_end (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
gtk_signal_connect (GTK_OBJECT (wid), "clicked",
|
||||
GTK_SIGNAL_FUNC (pevent_save_cb), NULL);
|
||||
gtk_widget_show (wid);*/
|
||||
gtkutil_button (hbox, GTK_STOCK_SAVE_AS, NULL, pevent_save_cb,
|
||||
(void *) 1, _("Save As..."));
|
||||
gtkutil_button (hbox, GTK_STOCK_OPEN, NULL, pevent_load_cb,
|
||||
(void *) 0, _("Load From..."));
|
||||
wid = gtk_button_new_with_label (_("Test All"));
|
||||
gtk_box_pack_end (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
g_signal_connect (G_OBJECT (wid), "clicked",
|
||||
G_CALLBACK (pevent_test_cb), pevent_dialog_twid);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
wid = gtk_button_new_from_stock (GTK_STOCK_OK);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
|
||||
g_signal_connect (G_OBJECT (wid), "clicked",
|
||||
G_CALLBACK (pevent_ok_cb), NULL);
|
||||
gtk_widget_show (wid);
|
||||
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
gtk_widget_show (pevent_dialog);
|
||||
}
|
||||
2
src/fe-gtk/textgui.h
Normal file
2
src/fe-gtk/textgui.h
Normal file
@@ -0,0 +1,2 @@
|
||||
void PrintTextRaw (void *xtbuf, unsigned char *text, int indent, time_t stamp);
|
||||
void pevent_dialog_show (void);
|
||||
208
src/fe-gtk/urlgrab.c
Normal file
208
src/fe-gtk/urlgrab.c
Normal file
@@ -0,0 +1,208 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
#include <gtk/gtkhbbox.h>
|
||||
#include <gtk/gtkscrolledwindow.h>
|
||||
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gtk/gtktreeview.h>
|
||||
#include <gtk/gtktreeselection.h>
|
||||
#include <gtk/gtkcellrenderertext.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/cfgfiles.h"
|
||||
#include "../common/fe.h"
|
||||
#include "../common/url.h"
|
||||
#include "../common/tree.h"
|
||||
#include "gtkutil.h"
|
||||
#include "menu.h"
|
||||
#include "maingui.h"
|
||||
#include "urlgrab.h"
|
||||
|
||||
/* model for the URL treeview */
|
||||
enum
|
||||
{
|
||||
URL_COLUMN,
|
||||
N_COLUMNS
|
||||
};
|
||||
|
||||
static GtkWidget *urlgrabberwindow = 0;
|
||||
|
||||
|
||||
static gboolean
|
||||
url_treeview_url_clicked_cb (GtkWidget *view, GdkEventButton *event,
|
||||
gpointer data)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
gchar *url;
|
||||
|
||||
if (!event ||
|
||||
!gtkutil_treeview_get_selected (GTK_TREE_VIEW (view), &iter,
|
||||
URL_COLUMN, &url, -1))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
switch (event->button)
|
||||
{
|
||||
case 1:
|
||||
if (event->type == GDK_2BUTTON_PRESS)
|
||||
fe_open_url (url);
|
||||
break;
|
||||
case 3:
|
||||
menu_urlmenu (event, url);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
g_free (url);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
url_treeview_new (GtkWidget *box)
|
||||
{
|
||||
GtkListStore *store;
|
||||
GtkWidget *view;
|
||||
|
||||
store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING);
|
||||
g_return_val_if_fail (store != NULL, NULL);
|
||||
|
||||
view = gtkutil_treeview_new (box, GTK_TREE_MODEL (store), NULL,
|
||||
URL_COLUMN, _("URL"), -1);
|
||||
g_signal_connect (G_OBJECT (view), "button_press_event",
|
||||
G_CALLBACK (url_treeview_url_clicked_cb), NULL);
|
||||
/* don't want column headers */
|
||||
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
|
||||
gtk_widget_show (view);
|
||||
return view;
|
||||
}
|
||||
|
||||
static void
|
||||
url_closegui (GtkWidget *wid, gpointer userdata)
|
||||
{
|
||||
urlgrabberwindow = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
url_button_clear (void)
|
||||
{
|
||||
GtkListStore *store;
|
||||
|
||||
url_clear ();
|
||||
store = GTK_LIST_STORE (g_object_get_data (G_OBJECT (urlgrabberwindow),
|
||||
"model"));
|
||||
gtk_list_store_clear (store);
|
||||
}
|
||||
|
||||
static void
|
||||
url_button_copy (GtkWidget *widget, gpointer data)
|
||||
{
|
||||
GtkTreeView *view = GTK_TREE_VIEW (data);
|
||||
GtkTreeIter iter;
|
||||
gchar *url = NULL;
|
||||
|
||||
if (gtkutil_treeview_get_selected (view, &iter, URL_COLUMN, &url, -1))
|
||||
{
|
||||
gtkutil_copy_to_clipboard (GTK_WIDGET (view), NULL, url);
|
||||
g_free (url);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
url_save_callback (void *arg1, char *file)
|
||||
{
|
||||
if (file)
|
||||
url_save (file, "w", TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
url_button_save (void)
|
||||
{
|
||||
gtkutil_file_req (_("Select an output filename"),
|
||||
url_save_callback, NULL, NULL, FRF_WRITE);
|
||||
}
|
||||
|
||||
void
|
||||
fe_url_add (const char *urltext)
|
||||
{
|
||||
GtkListStore *store;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (urlgrabberwindow)
|
||||
{
|
||||
store = GTK_LIST_STORE (g_object_get_data (G_OBJECT (urlgrabberwindow),
|
||||
"model"));
|
||||
gtk_list_store_prepend (store, &iter);
|
||||
gtk_list_store_set (store, &iter,
|
||||
URL_COLUMN, urltext,
|
||||
-1);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
populate_cb (char *urltext, gpointer userdata)
|
||||
{
|
||||
fe_url_add (urltext);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
url_opengui ()
|
||||
{
|
||||
GtkWidget *vbox, *hbox, *view;
|
||||
|
||||
if (urlgrabberwindow)
|
||||
{
|
||||
mg_bring_tofront (urlgrabberwindow);
|
||||
return;
|
||||
}
|
||||
|
||||
urlgrabberwindow =
|
||||
mg_create_generic_tab ("UrlGrabber", _("XChat: URL Grabber"), FALSE,
|
||||
TRUE, url_closegui, NULL, 400, 256, &vbox, 0);
|
||||
view = url_treeview_new (vbox);
|
||||
g_object_set_data (G_OBJECT (urlgrabberwindow), "model",
|
||||
gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
|
||||
|
||||
hbox = gtk_hbutton_box_new ();
|
||||
gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_SPREAD);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
|
||||
gtk_box_pack_end (GTK_BOX (vbox), hbox, 0, 0, 0);
|
||||
gtk_widget_show (hbox);
|
||||
|
||||
gtkutil_button (hbox, GTK_STOCK_CLEAR,
|
||||
_("Clear list"), url_button_clear, 0, _("Clear"));
|
||||
gtkutil_button (hbox, GTK_STOCK_COPY,
|
||||
_("Copy selected URL"), url_button_copy, view, _("Copy"));
|
||||
gtkutil_button (hbox, GTK_STOCK_SAVE_AS,
|
||||
_("Save list to a file"), url_button_save, 0, _("Save As..."));
|
||||
|
||||
gtk_widget_show (urlgrabberwindow);
|
||||
|
||||
tree_foreach (url_tree, (tree_traverse_func *)populate_cb, NULL);
|
||||
}
|
||||
2
src/fe-gtk/urlgrab.h
Normal file
2
src/fe-gtk/urlgrab.h
Normal file
@@ -0,0 +1,2 @@
|
||||
void url_autosave (void);
|
||||
void url_opengui (void);
|
||||
718
src/fe-gtk/userlistgui.c
Normal file
718
src/fe-gtk/userlistgui.c
Normal file
@@ -0,0 +1,718 @@
|
||||
/* X-Chat
|
||||
* Copyright (C) 1998 Peter Zelezny.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fe-gtk.h"
|
||||
|
||||
#include <gtk/gtkbox.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtkdnd.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtktreeview.h>
|
||||
#include <gtk/gtktreeselection.h>
|
||||
#include <gtk/gtkscrolledwindow.h>
|
||||
#include <gtk/gtkcellrendererpixbuf.h>
|
||||
#include <gtk/gtkcellrenderertext.h>
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
|
||||
#include "../common/xchat.h"
|
||||
#include "../common/util.h"
|
||||
#include "../common/userlist.h"
|
||||
#include "../common/modes.h"
|
||||
#include "../common/notify.h"
|
||||
#include "../common/xchatc.h"
|
||||
#include "gtkutil.h"
|
||||
#include "palette.h"
|
||||
#include "maingui.h"
|
||||
#include "menu.h"
|
||||
#include "pixmaps.h"
|
||||
#include "userlistgui.h"
|
||||
|
||||
#ifdef USE_GTKSPELL
|
||||
#include <gtk/gtktextview.h>
|
||||
#endif
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
COL_PIX=0, // GdkPixbuf *
|
||||
COL_NICK=1, // char *
|
||||
COL_HOST=2, // char *
|
||||
COL_USER=3, // struct User *
|
||||
COL_GDKCOLOR=4 // GdkColor *
|
||||
};
|
||||
|
||||
|
||||
GdkPixbuf *
|
||||
get_user_icon (server *serv, struct User *user)
|
||||
{
|
||||
char *pre;
|
||||
int level;
|
||||
|
||||
if (!user)
|
||||
return NULL;
|
||||
|
||||
/* these ones are hardcoded */
|
||||
switch (user->prefix[0])
|
||||
{
|
||||
case 0: return NULL;
|
||||
case '@': return pix_op;
|
||||
case '%': return pix_hop;
|
||||
case '+': return pix_voice;
|
||||
}
|
||||
|
||||
/* find out how many levels above Op this user is */
|
||||
pre = strchr (serv->nick_prefixes, '@');
|
||||
if (pre && pre != serv->nick_prefixes)
|
||||
{
|
||||
pre--;
|
||||
level = 0;
|
||||
while (1)
|
||||
{
|
||||
if (pre[0] == user->prefix[0])
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case 0: return pix_red; /* 1 level above op */
|
||||
case 1: return pix_purple; /* 2 levels above op */
|
||||
}
|
||||
break; /* 3+, no icons */
|
||||
}
|
||||
level++;
|
||||
if (pre == serv->nick_prefixes)
|
||||
break;
|
||||
pre--;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
fe_userlist_numbers (session *sess)
|
||||
{
|
||||
char tbuf[256];
|
||||
|
||||
if (sess == current_tab || !sess->gui->is_tab)
|
||||
{
|
||||
if (sess->total)
|
||||
{
|
||||
snprintf (tbuf, sizeof (tbuf), _("%d ops, %d total"), sess->ops, sess->total);
|
||||
tbuf[sizeof (tbuf) - 1] = 0;
|
||||
gtk_label_set_text (GTK_LABEL (sess->gui->namelistinfo), tbuf);
|
||||
} else
|
||||
{
|
||||
gtk_label_set_text (GTK_LABEL (sess->gui->namelistinfo), NULL);
|
||||
}
|
||||
|
||||
if (sess->type == SESS_CHANNEL && prefs.gui_tweaks & 1)
|
||||
fe_set_title (sess);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
scroll_to_iter (GtkTreeIter *iter, GtkTreeView *treeview, GtkTreeModel *model)
|
||||
{
|
||||
GtkTreePath *path = gtk_tree_model_get_path (model, iter);
|
||||
if (path)
|
||||
{
|
||||
gtk_tree_view_scroll_to_cell (treeview, path, NULL, TRUE, 0.5, 0.5);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
}
|
||||
|
||||
/* select a row in the userlist by nick-name */
|
||||
|
||||
void
|
||||
userlist_select (session *sess, char *name)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GtkTreeView *treeview = GTK_TREE_VIEW (sess->gui->user_tree);
|
||||
GtkTreeModel *model = gtk_tree_view_get_model (treeview);
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
|
||||
struct User *row_user;
|
||||
|
||||
if (gtk_tree_model_get_iter_first (model, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
|
||||
if (sess->server->p_cmp (row_user->nick, name) == 0)
|
||||
{
|
||||
if (gtk_tree_selection_iter_is_selected (selection, &iter))
|
||||
gtk_tree_selection_unselect_iter (selection, &iter);
|
||||
else
|
||||
gtk_tree_selection_select_iter (selection, &iter);
|
||||
|
||||
/* and make sure it's visible */
|
||||
scroll_to_iter (&iter, treeview, model);
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
}
|
||||
|
||||
char **
|
||||
userlist_selection_list (GtkWidget *widget, int *num_ret)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GtkTreeView *treeview = (GtkTreeView *) widget;
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
|
||||
GtkTreeModel *model = gtk_tree_view_get_model (treeview);
|
||||
struct User *user;
|
||||
int i, num_sel;
|
||||
char **nicks;
|
||||
|
||||
*num_ret = 0;
|
||||
/* first, count the number of selections */
|
||||
num_sel = 0;
|
||||
if (gtk_tree_model_get_iter_first (model, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
if (gtk_tree_selection_iter_is_selected (selection, &iter))
|
||||
num_sel++;
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
|
||||
if (num_sel < 1)
|
||||
return NULL;
|
||||
|
||||
nicks = malloc (sizeof (char *) * (num_sel + 1));
|
||||
|
||||
i = 0;
|
||||
gtk_tree_model_get_iter_first (model, &iter);
|
||||
do
|
||||
{
|
||||
if (gtk_tree_selection_iter_is_selected (selection, &iter))
|
||||
{
|
||||
gtk_tree_model_get (model, &iter, COL_USER, &user, -1);
|
||||
nicks[i] = g_strdup (user->nick);
|
||||
i++;
|
||||
nicks[i] = NULL;
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
|
||||
*num_ret = i;
|
||||
return nicks;
|
||||
}
|
||||
|
||||
void
|
||||
fe_userlist_set_selected (struct session *sess)
|
||||
{
|
||||
GtkListStore *store = sess->res->user_model;
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sess->gui->user_tree));
|
||||
GtkTreeIter iter;
|
||||
struct User *user;
|
||||
|
||||
/* if it's not front-most tab it doesn't own the GtkTreeView! */
|
||||
if (store != (GtkListStore*) gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree)))
|
||||
return;
|
||||
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COL_USER, &user, -1);
|
||||
|
||||
if (gtk_tree_selection_iter_is_selected (selection, &iter))
|
||||
user->selected = 1;
|
||||
else
|
||||
user->selected = 0;
|
||||
|
||||
} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
|
||||
}
|
||||
}
|
||||
|
||||
static GtkTreeIter *
|
||||
find_row (GtkTreeView *treeview, GtkTreeModel *model, struct User *user,
|
||||
int *selected)
|
||||
{
|
||||
static GtkTreeIter iter;
|
||||
struct User *row_user;
|
||||
|
||||
*selected = FALSE;
|
||||
if (gtk_tree_model_get_iter_first (model, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
|
||||
if (row_user == user)
|
||||
{
|
||||
if (gtk_tree_view_get_model (treeview) == model)
|
||||
{
|
||||
if (gtk_tree_selection_iter_is_selected (gtk_tree_view_get_selection (treeview), &iter))
|
||||
*selected = TRUE;
|
||||
}
|
||||
return &iter;
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
userlist_set_value (GtkWidget *treeview, gfloat val)
|
||||
{
|
||||
gtk_adjustment_set_value (
|
||||
gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (treeview)), val);
|
||||
}
|
||||
|
||||
gfloat
|
||||
userlist_get_value (GtkWidget *treeview)
|
||||
{
|
||||
return gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (treeview))->value;
|
||||
}
|
||||
|
||||
int
|
||||
fe_userlist_remove (session *sess, struct User *user)
|
||||
{
|
||||
GtkTreeIter *iter;
|
||||
/* GtkAdjustment *adj;
|
||||
gfloat val, end;*/
|
||||
int sel;
|
||||
|
||||
iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree),
|
||||
sess->res->user_model, user, &sel);
|
||||
if (!iter)
|
||||
return 0;
|
||||
|
||||
/* adj = gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (sess->gui->user_tree));
|
||||
val = adj->value;*/
|
||||
|
||||
gtk_list_store_remove (sess->res->user_model, iter);
|
||||
|
||||
/* is it the front-most tab? */
|
||||
/* if (gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree))
|
||||
== sess->res->user_model)
|
||||
{
|
||||
end = adj->upper - adj->lower - adj->page_size;
|
||||
if (val > end)
|
||||
val = end;
|
||||
gtk_adjustment_set_value (adj, val);
|
||||
}*/
|
||||
|
||||
return sel;
|
||||
}
|
||||
|
||||
void
|
||||
fe_userlist_rehash (session *sess, struct User *user)
|
||||
{
|
||||
GtkTreeIter *iter;
|
||||
int sel;
|
||||
int do_away = TRUE;
|
||||
|
||||
iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree),
|
||||
sess->res->user_model, user, &sel);
|
||||
if (!iter)
|
||||
return;
|
||||
|
||||
if (prefs.away_size_max < 1 || !prefs.away_track)
|
||||
do_away = FALSE;
|
||||
|
||||
gtk_list_store_set (GTK_LIST_STORE (sess->res->user_model), iter,
|
||||
COL_HOST, user->hostname,
|
||||
COL_GDKCOLOR, (do_away)
|
||||
? (user->away ? &colors[COL_AWAY] : NULL)
|
||||
: (NULL),
|
||||
-1);
|
||||
}
|
||||
|
||||
void
|
||||
fe_userlist_insert (session *sess, struct User *newuser, int row, int sel)
|
||||
{
|
||||
GtkTreeModel *model = sess->res->user_model;
|
||||
GdkPixbuf *pix = get_user_icon (sess->server, newuser);
|
||||
GtkTreeIter iter;
|
||||
int do_away = TRUE;
|
||||
char *nick;
|
||||
|
||||
if (prefs.away_size_max < 1 || !prefs.away_track)
|
||||
do_away = FALSE;
|
||||
|
||||
nick = newuser->nick;
|
||||
if (prefs.gui_tweaks & 64)
|
||||
{
|
||||
nick = malloc (strlen (newuser->nick) + 2);
|
||||
nick[0] = newuser->prefix[0];
|
||||
if (!nick[0] || nick[0] == ' ')
|
||||
strcpy (nick, newuser->nick);
|
||||
else
|
||||
strcpy (nick + 1, newuser->nick);
|
||||
pix = NULL;
|
||||
}
|
||||
|
||||
gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, row,
|
||||
COL_PIX, pix,
|
||||
COL_NICK, nick,
|
||||
COL_HOST, newuser->hostname,
|
||||
COL_USER, newuser,
|
||||
COL_GDKCOLOR, (do_away)
|
||||
? (newuser->away ? &colors[COL_AWAY] : NULL)
|
||||
: (NULL),
|
||||
-1);
|
||||
|
||||
if (prefs.gui_tweaks & 64)
|
||||
free (nick);
|
||||
|
||||
/* is it me? */
|
||||
if (newuser->me && sess->gui->nick_box)
|
||||
{
|
||||
if (!sess->gui->is_tab || sess == current_tab)
|
||||
mg_set_access_icon (sess->gui, pix, sess->server->is_away);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (prefs.hilitenotify && notify_isnotify (sess, newuser->nick))
|
||||
{
|
||||
gtk_clist_set_foreground ((GtkCList *) sess->gui->user_clist, row,
|
||||
&colors[prefs.nu_color]);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* is it the front-most tab? */
|
||||
if (gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree))
|
||||
== model)
|
||||
{
|
||||
if (sel)
|
||||
gtk_tree_selection_select_iter (gtk_tree_view_get_selection
|
||||
(GTK_TREE_VIEW (sess->gui->user_tree)), &iter);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fe_userlist_move (session *sess, struct User *user, int new_row)
|
||||
{
|
||||
fe_userlist_insert (sess, user, new_row, fe_userlist_remove (sess, user));
|
||||
}
|
||||
|
||||
void
|
||||
fe_userlist_clear (session *sess)
|
||||
{
|
||||
gtk_list_store_clear (sess->res->user_model);
|
||||
}
|
||||
|
||||
static void
|
||||
userlist_dnd_drop (GtkTreeView *widget, GdkDragContext *context,
|
||||
gint x, gint y, GtkSelectionData *selection_data,
|
||||
guint info, guint ttime, gpointer userdata)
|
||||
{
|
||||
struct User *user;
|
||||
GtkTreePath *path;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (!gtk_tree_view_get_path_at_pos (widget, x, y, &path, NULL, NULL, NULL))
|
||||
return;
|
||||
|
||||
model = gtk_tree_view_get_model (widget);
|
||||
if (!gtk_tree_model_get_iter (model, &iter, path))
|
||||
return;
|
||||
gtk_tree_model_get (model, &iter, COL_USER, &user, -1);
|
||||
|
||||
mg_dnd_drop_file (current_sess, user->nick, selection_data->data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
userlist_dnd_motion (GtkTreeView *widget, GdkDragContext *context, gint x,
|
||||
gint y, guint ttime, gpointer tree)
|
||||
{
|
||||
GtkTreePath *path;
|
||||
GtkTreeSelection *sel;
|
||||
|
||||
if (!tree)
|
||||
return FALSE;
|
||||
|
||||
if (gtk_tree_view_get_path_at_pos (widget, x, y, &path, NULL, NULL, NULL))
|
||||
{
|
||||
sel = gtk_tree_view_get_selection (widget);
|
||||
gtk_tree_selection_unselect_all (sel);
|
||||
gtk_tree_selection_select_path (sel, path);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
userlist_dnd_leave (GtkTreeView *widget, GdkDragContext *context, guint ttime)
|
||||
{
|
||||
gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (widget));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void *
|
||||
userlist_create_model (void)
|
||||
{
|
||||
return gtk_list_store_new (5, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
|
||||
G_TYPE_POINTER, GDK_TYPE_COLOR);
|
||||
}
|
||||
|
||||
static void
|
||||
userlist_add_columns (GtkTreeView * treeview)
|
||||
{
|
||||
GtkCellRenderer *renderer;
|
||||
|
||||
/* icon column */
|
||||
renderer = gtk_cell_renderer_pixbuf_new ();
|
||||
if (prefs.gui_tweaks & 32)
|
||||
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
|
||||
-1, NULL, renderer,
|
||||
"pixbuf", 0, NULL);
|
||||
|
||||
/* nick column */
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
if (prefs.gui_tweaks & 32)
|
||||
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
|
||||
gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
|
||||
-1, NULL, renderer,
|
||||
"text", 1, "foreground-gdk", 4, NULL);
|
||||
|
||||
if (prefs.showhostname_in_userlist)
|
||||
{
|
||||
/* hostname column */
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
if (prefs.gui_tweaks & 32)
|
||||
g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
|
||||
gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
|
||||
-1, NULL, renderer,
|
||||
"text", 2, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
userlist_click_cb (GtkWidget *widget, GdkEventButton *event, gpointer userdata)
|
||||
{
|
||||
char **nicks;
|
||||
int i;
|
||||
GtkTreeSelection *sel;
|
||||
GtkTreePath *path;
|
||||
|
||||
if (!event)
|
||||
return FALSE;
|
||||
|
||||
if (!(event->state & GDK_CONTROL_MASK) &&
|
||||
event->type == GDK_2BUTTON_PRESS && prefs.doubleclickuser[0])
|
||||
{
|
||||
nicks = userlist_selection_list (widget, &i);
|
||||
if (nicks)
|
||||
{
|
||||
nick_command_parse (current_sess, prefs.doubleclickuser, nicks[0],
|
||||
nicks[0]);
|
||||
while (i)
|
||||
{
|
||||
i--;
|
||||
g_free (nicks[i]);
|
||||
}
|
||||
free (nicks);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (event->button == 3)
|
||||
{
|
||||
/* do we have a multi-selection? */
|
||||
nicks = userlist_selection_list (widget, &i);
|
||||
if (nicks && i > 1)
|
||||
{
|
||||
menu_nickmenu (current_sess, event, nicks[0], i);
|
||||
while (i)
|
||||
{
|
||||
i--;
|
||||
g_free (nicks[i]);
|
||||
}
|
||||
free (nicks);
|
||||
return TRUE;
|
||||
}
|
||||
if (nicks)
|
||||
{
|
||||
g_free (nicks[0]);
|
||||
free (nicks);
|
||||
}
|
||||
|
||||
sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
|
||||
if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
|
||||
event->x, event->y, &path, 0, 0, 0))
|
||||
{
|
||||
gtk_tree_selection_unselect_all (sel);
|
||||
gtk_tree_selection_select_path (sel, path);
|
||||
gtk_tree_path_free (path);
|
||||
nicks = userlist_selection_list (widget, &i);
|
||||
if (nicks)
|
||||
{
|
||||
menu_nickmenu (current_sess, event, nicks[0], i);
|
||||
while (i)
|
||||
{
|
||||
i--;
|
||||
g_free (nicks[i]);
|
||||
}
|
||||
free (nicks);
|
||||
}
|
||||
} else
|
||||
{
|
||||
gtk_tree_selection_unselect_all (sel);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
userlist_key_cb (GtkWidget *wid, GdkEventKey *evt, gpointer userdata)
|
||||
{
|
||||
if (evt->keyval >= GDK_asterisk && evt->keyval <= GDK_z)
|
||||
{
|
||||
/* dirty trick to avoid auto-selection */
|
||||
SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, FALSE);
|
||||
gtk_widget_grab_focus (current_sess->gui->input_box);
|
||||
SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, TRUE);
|
||||
gtk_widget_event (current_sess->gui->input_box, (GdkEvent *)evt);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
userlist_create (GtkWidget *box)
|
||||
{
|
||||
GtkWidget *sw, *treeview;
|
||||
static const GtkTargetEntry dnd_dest_targets[] =
|
||||
{
|
||||
{"text/uri-list", 0, 1},
|
||||
{"XCHAT_CHANVIEW", GTK_TARGET_SAME_APP, 75 }
|
||||
};
|
||||
static const GtkTargetEntry dnd_src_target[] =
|
||||
{
|
||||
{"XCHAT_USERLIST", GTK_TARGET_SAME_APP, 75 }
|
||||
};
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
|
||||
GTK_SHADOW_ETCHED_IN);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
|
||||
prefs.showhostname_in_userlist ?
|
||||
GTK_POLICY_AUTOMATIC :
|
||||
GTK_POLICY_NEVER,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);
|
||||
gtk_widget_show (sw);
|
||||
|
||||
treeview = gtk_tree_view_new ();
|
||||
gtk_widget_set_name (treeview, "xchat-userlist");
|
||||
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
|
||||
gtk_tree_selection_set_mode (gtk_tree_view_get_selection
|
||||
(GTK_TREE_VIEW (treeview)),
|
||||
GTK_SELECTION_MULTIPLE);
|
||||
|
||||
/* set up drops */
|
||||
gtk_drag_dest_set (treeview, GTK_DEST_DEFAULT_ALL, dnd_dest_targets, 2,
|
||||
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
|
||||
gtk_drag_source_set (treeview, GDK_BUTTON1_MASK, dnd_src_target, 1, GDK_ACTION_MOVE);
|
||||
|
||||
/* file DND (for DCC) */
|
||||
g_signal_connect (G_OBJECT (treeview), "drag_motion",
|
||||
G_CALLBACK (userlist_dnd_motion), treeview);
|
||||
g_signal_connect (G_OBJECT (treeview), "drag_leave",
|
||||
G_CALLBACK (userlist_dnd_leave), 0);
|
||||
g_signal_connect (G_OBJECT (treeview), "drag_data_received",
|
||||
G_CALLBACK (userlist_dnd_drop), treeview);
|
||||
|
||||
g_signal_connect (G_OBJECT (treeview), "button_press_event",
|
||||
G_CALLBACK (userlist_click_cb), 0);
|
||||
g_signal_connect (G_OBJECT (treeview), "key_press_event",
|
||||
G_CALLBACK (userlist_key_cb), 0);
|
||||
|
||||
/* tree/chanview DND */
|
||||
#ifndef WIN32 /* leaks GDI pool memory, don't enable */
|
||||
g_signal_connect (G_OBJECT (treeview), "drag_begin",
|
||||
G_CALLBACK (mg_drag_begin_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (treeview), "drag_drop",
|
||||
G_CALLBACK (mg_drag_drop_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (treeview), "drag_motion",
|
||||
G_CALLBACK (mg_drag_motion_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (treeview), "drag_end",
|
||||
G_CALLBACK (mg_drag_end_cb), NULL);
|
||||
#endif
|
||||
|
||||
userlist_add_columns (GTK_TREE_VIEW (treeview));
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (sw), treeview);
|
||||
gtk_widget_show (treeview);
|
||||
|
||||
return treeview;
|
||||
}
|
||||
|
||||
void
|
||||
userlist_show (session *sess)
|
||||
{
|
||||
gtk_tree_view_set_model (GTK_TREE_VIEW (sess->gui->user_tree),
|
||||
sess->res->user_model);
|
||||
}
|
||||
|
||||
void
|
||||
fe_uselect (session *sess, char *word[], int do_clear, int scroll_to)
|
||||
{
|
||||
int thisname;
|
||||
char *name;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeView *treeview = GTK_TREE_VIEW (sess->gui->user_tree);
|
||||
GtkTreeModel *model = gtk_tree_view_get_model (treeview);
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
|
||||
struct User *row_user;
|
||||
|
||||
if (gtk_tree_model_get_iter_first (model, &iter))
|
||||
{
|
||||
if (do_clear)
|
||||
gtk_tree_selection_unselect_all (selection);
|
||||
|
||||
do
|
||||
{
|
||||
if (*word[0])
|
||||
{
|
||||
gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
|
||||
thisname = 0;
|
||||
while ( *(name = word[thisname++]) )
|
||||
{
|
||||
if (sess->server->p_cmp (row_user->nick, name) == 0)
|
||||
{
|
||||
gtk_tree_selection_select_iter (selection, &iter);
|
||||
if (scroll_to)
|
||||
scroll_to_iter (&iter, treeview, model);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
}
|
||||
8
src/fe-gtk/userlistgui.h
Normal file
8
src/fe-gtk/userlistgui.h
Normal file
@@ -0,0 +1,8 @@
|
||||
void userlist_set_value (GtkWidget *treeview, gfloat val);
|
||||
gfloat userlist_get_value (GtkWidget *treeview);
|
||||
GtkWidget *userlist_create (GtkWidget *box);
|
||||
void *userlist_create_model (void);
|
||||
void userlist_show (session *sess);
|
||||
void userlist_select (session *sess, char *name);
|
||||
char **userlist_selection_list (GtkWidget *widget, int *num_ret);
|
||||
GdkPixbuf *get_user_icon (server *serv, struct User *user);
|
||||
5478
src/fe-gtk/xtext.c
Normal file
5478
src/fe-gtk/xtext.c
Normal file
File diff suppressed because it is too large
Load Diff
275
src/fe-gtk/xtext.h
Normal file
275
src/fe-gtk/xtext.h
Normal file
@@ -0,0 +1,275 @@
|
||||
#ifndef __XTEXT_H__
|
||||
#define __XTEXT_H__
|
||||
|
||||
#include <gtk/gtkadjustment.h>
|
||||
#ifdef USE_XFT
|
||||
#include <X11/Xft/Xft.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_SHM
|
||||
#include <X11/Xlib.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
#endif
|
||||
|
||||
#define GTK_TYPE_XTEXT (gtk_xtext_get_type ())
|
||||
#define GTK_XTEXT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_XTEXT, GtkXText))
|
||||
#define GTK_XTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_XTEXT, GtkXTextClass))
|
||||
#define GTK_IS_XTEXT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GTK_TYPE_XTEXT))
|
||||
#define GTK_IS_XTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_XTEXT))
|
||||
#define GTK_XTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_XTEXT, GtkXTextClass))
|
||||
|
||||
#define ATTR_BOLD '\002'
|
||||
#define ATTR_COLOR '\003'
|
||||
#define ATTR_BLINK '\006'
|
||||
#define ATTR_BEEP '\007'
|
||||
#define ATTR_HIDDEN '\010'
|
||||
#define ATTR_ITALICS2 '\011'
|
||||
#define ATTR_RESET '\017'
|
||||
#define ATTR_REVERSE '\026'
|
||||
#define ATTR_ITALICS '\035'
|
||||
#define ATTR_UNDERLINE '\037'
|
||||
|
||||
/* these match palette.h */
|
||||
#define XTEXT_MIRC_COLS 32
|
||||
#define XTEXT_COLS 37 /* 32 plus 5 for extra stuff below */
|
||||
#define XTEXT_MARK_FG 32 /* for marking text */
|
||||
#define XTEXT_MARK_BG 33
|
||||
#define XTEXT_FG 34
|
||||
#define XTEXT_BG 35
|
||||
#define XTEXT_MARKER 36 /* for marker line */
|
||||
|
||||
typedef struct _GtkXText GtkXText;
|
||||
typedef struct _GtkXTextClass GtkXTextClass;
|
||||
typedef struct textentry textentry;
|
||||
|
||||
typedef struct {
|
||||
GtkXText *xtext; /* attached to this widget */
|
||||
|
||||
gfloat old_value; /* last known adj->value */
|
||||
textentry *text_first;
|
||||
textentry *text_last;
|
||||
guint16 grid_offset[256];
|
||||
|
||||
textentry *last_ent_start; /* this basically describes the last rendered */
|
||||
textentry *last_ent_end; /* selection. */
|
||||
int last_offset_start;
|
||||
int last_offset_end;
|
||||
|
||||
int last_pixel_pos;
|
||||
|
||||
int pagetop_line;
|
||||
int pagetop_subline;
|
||||
textentry *pagetop_ent; /* what's at xtext->adj->value */
|
||||
|
||||
int num_lines;
|
||||
int indent; /* position of separator (pixels) from left */
|
||||
|
||||
textentry *marker_pos;
|
||||
|
||||
int window_width; /* window size when last rendered. */
|
||||
int window_height;
|
||||
|
||||
unsigned int time_stamp:1;
|
||||
unsigned int scrollbar_down:1;
|
||||
unsigned int needs_recalc:1;
|
||||
unsigned int grid_dirty:1;
|
||||
unsigned int marker_seen:1;
|
||||
unsigned int reset_marker_pos:1;
|
||||
} xtext_buffer;
|
||||
|
||||
struct _GtkXText
|
||||
{
|
||||
GtkWidget widget;
|
||||
|
||||
xtext_buffer *buffer;
|
||||
xtext_buffer *orig_buffer;
|
||||
xtext_buffer *selection_buffer;
|
||||
|
||||
#ifdef USE_SHM
|
||||
XShmSegmentInfo shminfo;
|
||||
#endif
|
||||
|
||||
GtkAdjustment *adj;
|
||||
GdkPixmap *pixmap; /* 0 = use palette[19] */
|
||||
GdkDrawable *draw_buf; /* points to ->window */
|
||||
GdkCursor *hand_cursor;
|
||||
GdkCursor *resize_cursor;
|
||||
|
||||
int pixel_offset; /* amount of pixels the top line is chopped by */
|
||||
|
||||
int last_win_x;
|
||||
int last_win_y;
|
||||
int last_win_h;
|
||||
int last_win_w;
|
||||
|
||||
int tint_red;
|
||||
int tint_green;
|
||||
int tint_blue;
|
||||
|
||||
GdkGC *bgc; /* backing pixmap */
|
||||
GdkGC *fgc; /* text foreground color */
|
||||
GdkGC *light_gc; /* sep bar */
|
||||
GdkGC *dark_gc;
|
||||
GdkGC *thin_gc;
|
||||
GdkGC *marker_gc;
|
||||
gulong palette[XTEXT_COLS];
|
||||
|
||||
gint io_tag; /* for delayed refresh events */
|
||||
gint add_io_tag; /* "" when adding new text */
|
||||
gint scroll_tag; /* marking-scroll timeout */
|
||||
gulong vc_signal_tag; /* signal handler for "value_changed" adj */
|
||||
|
||||
int select_start_adj; /* the adj->value when the selection started */
|
||||
int select_start_x;
|
||||
int select_start_y;
|
||||
int select_end_x;
|
||||
int select_end_y;
|
||||
|
||||
int max_lines;
|
||||
|
||||
int col_fore;
|
||||
int col_back;
|
||||
|
||||
int depth; /* gdk window depth */
|
||||
|
||||
char num[8]; /* for parsing mirc color */
|
||||
int nc; /* offset into xtext->num */
|
||||
|
||||
textentry *hilight_ent;
|
||||
int hilight_start;
|
||||
int hilight_end;
|
||||
|
||||
guint16 fontwidth[128]; /* each char's width, only the ASCII ones */
|
||||
|
||||
#ifdef USE_XFT
|
||||
XftColor color[XTEXT_COLS];
|
||||
XftColor *xft_fg;
|
||||
XftColor *xft_bg; /* both point into color[20] */
|
||||
XftDraw *xftdraw;
|
||||
XftFont *font;
|
||||
XftFont *ifont; /* italics */
|
||||
#else
|
||||
struct pangofont
|
||||
{
|
||||
PangoFontDescription *font;
|
||||
PangoFontDescription *ifont; /* italics */
|
||||
int ascent;
|
||||
int descent;
|
||||
} *font, pango_font;
|
||||
PangoLayout *layout;
|
||||
#endif
|
||||
|
||||
int fontsize;
|
||||
int space_width; /* width (pixels) of the space " " character */
|
||||
int stamp_width; /* width of "[88:88:88]" */
|
||||
int max_auto_indent;
|
||||
|
||||
unsigned char scratch_buffer[4096];
|
||||
|
||||
void (*error_function) (int type);
|
||||
int (*urlcheck_function) (GtkWidget * xtext, char *word, int len);
|
||||
|
||||
int jump_out_offset; /* point at which to stop rendering */
|
||||
int jump_in_offset; /* "" start rendering */
|
||||
|
||||
int ts_x; /* ts origin for ->bgc GC */
|
||||
int ts_y;
|
||||
|
||||
int clip_x; /* clipping (x directions) */
|
||||
int clip_x2; /* from x to x2 */
|
||||
|
||||
int clip_y; /* clipping (y directions) */
|
||||
int clip_y2; /* from y to y2 */
|
||||
|
||||
/* current text states */
|
||||
unsigned int bold:1;
|
||||
unsigned int underline:1;
|
||||
unsigned int italics:1;
|
||||
unsigned int hidden:1;
|
||||
|
||||
/* text parsing states */
|
||||
unsigned int parsing_backcolor:1;
|
||||
unsigned int parsing_color:1;
|
||||
unsigned int backcolor:1;
|
||||
|
||||
/* various state information */
|
||||
unsigned int moving_separator:1;
|
||||
unsigned int word_or_line_select:1;
|
||||
unsigned int button_down:1;
|
||||
unsigned int hilighting:1;
|
||||
unsigned int dont_render:1;
|
||||
unsigned int dont_render2:1;
|
||||
unsigned int cursor_hand:1;
|
||||
unsigned int cursor_resize:1;
|
||||
unsigned int skip_border_fills:1;
|
||||
unsigned int skip_stamp:1;
|
||||
unsigned int mark_stamp:1; /* Cut&Paste with stamps? */
|
||||
unsigned int force_stamp:1; /* force redrawing it */
|
||||
unsigned int render_hilights_only:1;
|
||||
unsigned int in_hilight:1;
|
||||
unsigned int un_hilight:1;
|
||||
unsigned int recycle:1;
|
||||
unsigned int avoid_trans:1;
|
||||
unsigned int force_render:1;
|
||||
unsigned int shm:1;
|
||||
unsigned int color_paste:1; /* CTRL was pressed when selection finished */
|
||||
|
||||
/* settings/prefs */
|
||||
unsigned int auto_indent:1;
|
||||
unsigned int thinline:1;
|
||||
unsigned int transparent:1;
|
||||
unsigned int shaded:1;
|
||||
unsigned int marker:1;
|
||||
unsigned int separator:1;
|
||||
unsigned int wordwrap:1;
|
||||
unsigned int overdraw:1;
|
||||
unsigned int ignore_hidden:1; /* rawlog uses this */
|
||||
};
|
||||
|
||||
struct _GtkXTextClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
void (*word_click) (GtkXText * xtext, char *word, GdkEventButton * event);
|
||||
};
|
||||
|
||||
GtkWidget *gtk_xtext_new (GdkColor palette[], int separator);
|
||||
void gtk_xtext_append (xtext_buffer *buf, unsigned char *text, int len);
|
||||
void gtk_xtext_append_indent (xtext_buffer *buf,
|
||||
unsigned char *left_text, int left_len,
|
||||
unsigned char *right_text, int right_len,
|
||||
time_t stamp);
|
||||
int gtk_xtext_set_font (GtkXText *xtext, char *name);
|
||||
void gtk_xtext_set_background (GtkXText * xtext, GdkPixmap * pixmap, gboolean trans);
|
||||
void gtk_xtext_set_palette (GtkXText * xtext, GdkColor palette[]);
|
||||
void gtk_xtext_clear (xtext_buffer *buf, int lines);
|
||||
void gtk_xtext_save (GtkXText * xtext, int fh);
|
||||
void gtk_xtext_refresh (GtkXText * xtext, int do_trans);
|
||||
int gtk_xtext_lastlog (xtext_buffer *out, xtext_buffer *search_area, int (*cmp_func) (char *, void *userdata), void *userdata);
|
||||
textentry *gtk_xtext_search (GtkXText * xtext, const gchar *text, textentry *start, gboolean case_match, gboolean backward);
|
||||
void gtk_xtext_reset_marker_pos (GtkXText *xtext);
|
||||
void gtk_xtext_check_marker_visibility(GtkXText *xtext);
|
||||
|
||||
gboolean gtk_xtext_is_empty (xtext_buffer *buf);
|
||||
typedef void (*GtkXTextForeach) (GtkXText *xtext, unsigned char *text, void *data);
|
||||
void gtk_xtext_foreach (xtext_buffer *buf, GtkXTextForeach func, void *data);
|
||||
|
||||
void gtk_xtext_set_error_function (GtkXText *xtext, void (*error_function) (int));
|
||||
void gtk_xtext_set_indent (GtkXText *xtext, gboolean indent);
|
||||
void gtk_xtext_set_max_indent (GtkXText *xtext, int max_auto_indent);
|
||||
void gtk_xtext_set_max_lines (GtkXText *xtext, int max_lines);
|
||||
void gtk_xtext_set_show_marker (GtkXText *xtext, gboolean show_marker);
|
||||
void gtk_xtext_set_show_separator (GtkXText *xtext, gboolean show_separator);
|
||||
void gtk_xtext_set_thin_separator (GtkXText *xtext, gboolean thin_separator);
|
||||
void gtk_xtext_set_time_stamp (xtext_buffer *buf, gboolean timestamp);
|
||||
void gtk_xtext_set_tint (GtkXText *xtext, int tint_red, int tint_green, int tint_blue);
|
||||
void gtk_xtext_set_urlcheck_function (GtkXText *xtext, int (*urlcheck_function) (GtkWidget *, char *, int));
|
||||
void gtk_xtext_set_wordwrap (GtkXText *xtext, gboolean word_wrap);
|
||||
|
||||
xtext_buffer *gtk_xtext_buffer_new (GtkXText *xtext);
|
||||
void gtk_xtext_buffer_free (xtext_buffer *buf);
|
||||
void gtk_xtext_buffer_show (GtkXText *xtext, xtext_buffer *buf, int render);
|
||||
GType gtk_xtext_get_type (void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user