443 lines
12 KiB
C
443 lines
12 KiB
C
|
/* 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);
|
||
|
}
|