Files
zoitechat/src/fe-gtk/notifygui.c
deepend 1bb0451d75 Updated GTK3 palette color parsing to use full 16-bit hex strings when converting to RGBA in palette handling and setup color dialogs.
Switched GtkCellRendererText foreground bindings to the palette foreground property macro in user list, notify list, and DCC views.
2026-01-23 00:23:00 -07:00

471 lines
13 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include "fe-gtk.h"
#include "../common/zoitechat.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 "../common/outbound.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;
notify_save ();
}
/* Need this to be able to set the foreground colour property of a row
* from a PaletteColor * in the model -Vince
*/
static void
notify_treecell_property_mapper (GtkTreeViewColumn *col, GtkCellRenderer *cell,
GtkTreeModel *model, GtkTreeIter *iter,
gpointer data)
{
gchar *text;
PaletteColor *colour;
int model_column = GPOINTER_TO_INT (data);
gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
COLOUR_COLUMN, &colour,
model_column, &text, -1);
#if GTK_CHECK_VERSION(3,0,0)
g_object_set (G_OBJECT (cell), "text", text,
PALETTE_FOREGROUND_PROPERTY, colour, NULL);
if (colour)
gdk_rgba_free (colour);
#else
g_object_set (G_OBJECT (cell), "text", text,
PALETTE_FOREGROUND_PROPERTY, colour, NULL);
if (colour)
gdk_color_free (colour);
#endif
g_free (text);
}
static void
notify_store_color (GtkListStore *store, GtkTreeIter *iter, const PaletteColor *color)
{
#if GTK_CHECK_VERSION(3,0,0)
if (color)
{
GdkRGBA rgba = *color;
gtk_list_store_set (store, iter, COLOUR_COLUMN, &rgba, -1);
}
else
{
gtk_list_store_set (store, iter, COLOUR_COLUMN, NULL, -1);
}
#else
gtk_list_store_set (store, iter, COLOUR_COLUMN, color, -1);
#endif
}
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,
PALETTE_GDK_TYPE,
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, lastseenminutes;
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
{
lastseenminutes = (int)(time (0) - lastseen) / 60;
if (lastseenminutes < 60)
g_snprintf (agobuf, sizeof (agobuf), _("%d minutes ago"), lastseenminutes);
else if (lastseenminutes < 120)
g_snprintf (agobuf, sizeof (agobuf), _("An hour ago"));
else
g_snprintf (agobuf, sizeof (agobuf), _("%d hours ago"), lastseenminutes / 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, 5, NULL, -1);
notify_store_color (store, &iter, &colors[4]);
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);
g_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, 5, servnot, -1);
notify_store_color (store, &iter, &colors[3]);
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);
}
notify_row_cb (gtk_tree_view_get_selection (view), view);
}
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 = (char *)gtk_entry_get_text (GTK_ENTRY (entry));
if (text[0] && response == GTK_RESPONSE_ACCEPT)
{
networks = (char*)gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (entry), "net")));
if (g_ascii_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_get_content_area (GTK_DIALOG (dialog))), 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);
g_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;
char buf[128];
if (notify_window)
{
mg_bring_tofront (notify_window);
return;
}
g_snprintf(buf, sizeof(buf), _("Friends List - %s"), _(DISPLAY_NAME));
notify_window =
mg_create_generic_tab ("Notify", buf, FALSE, TRUE, notify_closegui, NULL, 400,
250, &vbox, 0);
gtkutil_destroy_on_esc (notify_window);
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);
}