add xchat r1489

This commit is contained in:
berkeviktor@aol.com
2011-02-24 04:14:30 +01:00
parent f16af8be94
commit 4a6ceffb98
245 changed files with 324678 additions and 0 deletions

32
src/fe-gtk/Makefile.am Normal file
View 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
View 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 &lt;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
View File

@@ -0,0 +1 @@
void menu_about (GtkWidget * wid, gpointer sess);

177
src/fe-gtk/ascii.c Normal file
View 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
View File

@@ -0,0 +1 @@
void ascii_open (void);

418
src/fe-gtk/banlist.c Normal file
View 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
View File

@@ -0,0 +1 @@
void banlist_opengui (session *sess);

943
src/fe-gtk/chanlist.c Normal file
View 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
View File

@@ -0,0 +1 @@
void chanlist_opengui (server *serv, int do_refresh);

779
src/fe-gtk/chanview-tabs.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

409
src/fe-gtk/editlist.c Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

197
src/fe-gtk/fe-gtk.h Normal file
View 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

File diff suppressed because it is too large Load Diff

5
src/fe-gtk/fkeys.h Normal file
View 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
View 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
View 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
View 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, &noti,
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
View 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
View File

@@ -0,0 +1,2 @@
void joind_open (server *serv);
void joind_close (server *serv);

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
View 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

File diff suppressed because it is too large Load Diff

41
src/fe-gtk/menu.h Normal file
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,2 @@
void notify_gui_update (void);
void notify_opengui (void);

226
src/fe-gtk/palette.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,2 @@
void plugingui_open (void);
void plugingui_load (void);

152
src/fe-gtk/rawlog.c Normal file
View 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
View File

@@ -0,0 +1 @@
void open_rawlog (server *serv);

159
src/fe-gtk/search.c Normal file
View 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
View File

@@ -0,0 +1 @@
void search_open (session * sess);

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,2 @@
void url_autosave (void);
void url_opengui (void);

718
src/fe-gtk/userlistgui.c Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

275
src/fe-gtk/xtext.h Normal file
View 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