diff options
-rw-r--r-- | src/common/cfgfiles.c | 5 | ||||
-rw-r--r-- | src/common/hexchat.c | 98 | ||||
-rw-r--r-- | src/common/hexchat.h | 14 | ||||
-rw-r--r-- | src/common/hexchatc.h | 3 | ||||
-rw-r--r-- | src/common/inbound.c | 5 | ||||
-rw-r--r-- | src/common/textevents.in | 6 | ||||
-rw-r--r-- | src/fe-gtk/fe-gtk.c | 1 | ||||
-rw-r--r-- | src/fe-gtk/fkeys.c | 44 | ||||
-rw-r--r-- | src/fe-gtk/maingui.c | 2 | ||||
-rw-r--r-- | src/fe-gtk/plugin-tray.c | 42 | ||||
-rw-r--r-- | src/fe-gtk/setup.c | 1 |
11 files changed, 214 insertions, 7 deletions
diff --git a/src/common/cfgfiles.c b/src/common/cfgfiles.c index 6748fbbd..fd037525 100644 --- a/src/common/cfgfiles.c +++ b/src/common/cfgfiles.c @@ -330,7 +330,7 @@ get_xdir (void) xdir = g_strdup_printf ("%s\\" "HexChat", out); } #else - xdir = g_strdup_printf ("%s/.config/" HEXCHAT_DIR, g_get_home_dir ()); + xdir = g_strdup_printf ("%s/" HEXCHAT_DIR, g_get_user_config_dir ()); #endif } @@ -460,6 +460,8 @@ const struct prefs vars[] = {"gui_throttlemeter", P_OFFINT (hex_gui_throttlemeter), TYPE_INT}, {"gui_topicbar", P_OFFINT (hex_gui_topicbar), TYPE_BOOL}, {"gui_tray", P_OFFINT (hex_gui_tray), TYPE_BOOL}, + {"gui_tray_away", P_OFFINT (hex_gui_tray_away), TYPE_BOOL}, + {"gui_tray_blink", P_OFFINT (hex_gui_tray_blink), TYPE_BOOL}, {"gui_tray_close", P_OFFINT (hex_gui_tray_close), TYPE_BOOL}, {"gui_tray_minimize", P_OFFINT (hex_gui_tray_minimize), TYPE_BOOL}, {"gui_tray_quiet", P_OFFINT (hex_gui_tray_quiet), TYPE_BOOL}, @@ -668,6 +670,7 @@ load_config (void) prefs.hex_gui_tab_sort = 1; prefs.hex_gui_topicbar = 1; prefs.hex_gui_tray = 1; + prefs.hex_gui_tray_blink = 1; prefs.hex_gui_ulist_count = 1; prefs.hex_gui_ulist_icons = 1; prefs.hex_gui_ulist_resizable = 1; diff --git a/src/common/hexchat.c b/src/common/hexchat.c index da46f7f5..57e3fc86 100644 --- a/src/common/hexchat.c +++ b/src/common/hexchat.c @@ -77,6 +77,24 @@ GSList *usermenu_list = 0; GSList *urlhandler_list = 0; GSList *tabmenu_list = 0; +/* + * This array contains 5 double linked lists, one for each priority in the + * "interesting session" queue ("channel" stands for everything but + * SESS_DIALOG): + * + * [0] queries with hilight + * [1] queries + * [2] channels with hilight + * [3] channels with dialogue + * [4] channels with other data + * + * Each time activity happens the corresponding session is put at the + * beginning of one of the lists. The aim is to be able to switch to the + * session with the most important/recent activity. + */ +GList *sess_list_by_lastact[5] = {NULL, NULL, NULL, NULL, NULL}; + + static int in_hexchat_exit = FALSE; int hexchat_is_quitting = FALSE; /* command-line args */ @@ -103,6 +121,79 @@ SSL_CTX *ctx = NULL; pxProxyFactory *libproxy_factory; #endif +/* + * Update the priority queue of the "interesting sessions" + * (sess_list_by_lastact). + */ +void +lastact_update(session *sess) +{ + int oldidx = sess->lastact_idx; + int newidx = LACT_NONE; + int dia = (sess->type == SESS_DIALOG); + + if (sess->nick_said) + newidx = dia? LACT_QUERY_HI: LACT_CHAN_HI; + else if (sess->msg_said) + newidx = dia? LACT_QUERY: LACT_CHAN; + else if (sess->new_data) + newidx = dia? LACT_QUERY: LACT_CHAN_DATA; + + /* If already first at the right position, just return */ + if (oldidx == newidx && + (newidx == LACT_NONE || g_list_index(sess_list_by_lastact[newidx], sess) == 0)) + return; + + /* Remove from the old position */ + if (oldidx != LACT_NONE) + sess_list_by_lastact[oldidx] = g_list_remove(sess_list_by_lastact[oldidx], sess); + + /* Add at the new position */ + sess->lastact_idx = newidx; + if (newidx != LACT_NONE) + sess_list_by_lastact[newidx] = g_list_prepend(sess_list_by_lastact[newidx], sess); + return; +} + +/* + * Extract the first session from the priority queue of sessions with recent + * activity. Return NULL if no such session can be found. + * + * If filter is specified, skip a session if filter(session) returns 0. This + * can be used for UI-specific needs, e.g. in fe-gtk we want to filter out + * detached sessions. + */ +session * +lastact_getfirst(int (*filter) (session *sess)) +{ + int i; + session *sess = NULL; + GList *curitem; + + /* 5 is the number of priority classes LACT_ */ + for (i = 0; i < 5 && !sess; i++) + { + curitem = sess_list_by_lastact[i]; + while (curitem && !sess) + { + sess = g_list_nth_data(curitem, 0); + if (!sess || (filter && !filter(sess))) + { + sess = NULL; + curitem = g_list_next(curitem); + } + } + + if (sess) + { + sess_list_by_lastact[i] = g_list_remove(sess_list_by_lastact[i], sess); + sess->lastact_idx = LACT_NONE; + } + } + + return sess; +} + int is_session (session * sess) { @@ -372,6 +463,8 @@ session_new (server *serv, char *from, int type, int focus) sess->text_logging = SET_DEFAULT; sess->text_scrollback = SET_DEFAULT; + sess->lastact_idx = LACT_NONE; + if (from != NULL) safe_strcpy (sess->channel, from, CHANLEN); @@ -489,6 +582,7 @@ session_free (session *killsess) server *killserv = killsess->server; session *sess; GSList *list; + int oldidx; plugin_emit_dummy_print (killsess, "Close Context"); @@ -525,6 +619,10 @@ session_free (session *killsess) if (killsess->type == SESS_CHANNEL) userlist_free (killsess); + oldidx = killsess->lastact_idx; + if (oldidx != LACT_NONE) + sess_list_by_lastact[oldidx] = g_list_remove(sess_list_by_lastact[oldidx], killsess); + exec_notify_kill (killsess); log_close (killsess); diff --git a/src/common/hexchat.h b/src/common/hexchat.h index d84e2761..dfd2fd5a 100644 --- a/src/common/hexchat.h +++ b/src/common/hexchat.h @@ -152,6 +152,8 @@ struct hexchatprefs unsigned int hex_gui_tab_utils; unsigned int hex_gui_topicbar; unsigned int hex_gui_tray; + unsigned int hex_gui_tray_away; + unsigned int hex_gui_tray_blink; unsigned int hex_gui_tray_close; unsigned int hex_gui_tray_minimize; unsigned int hex_gui_tray_quiet; @@ -348,6 +350,15 @@ struct hexchatprefs #define SET_ON 1 #define SET_DEFAULT 2 /* use global setting */ +/* Priorities in the "interesting sessions" priority queue + * (see xchat.c:sess_list_by_lastact) */ +#define LACT_NONE -1 /* no queues */ +#define LACT_QUERY_HI 0 /* query with hilight */ +#define LACT_QUERY 1 /* query with messages */ +#define LACT_CHAN_HI 2 /* channel with hilight */ +#define LACT_CHAN 3 /* channel with messages */ +#define LACT_CHAN_DATA 4 /* channel with other data */ + /* Moved from fe-gtk for use in outbound.c as well -- */ typedef enum gtk_xtext_search_flags_e { case_match = 1, @@ -406,6 +417,9 @@ typedef struct session int type; /* SESS_* */ + int lastact_idx; /* the sess_list_by_lastact[] index of the list we're in. + * For valid values, see defines of LACT_*. */ + int new_data:1; /* new data avail? (purple tab) */ int nick_said:1; /* your nick mentioned? (blue tab) */ int msg_said:1; /* new msg available? (red tab) */ diff --git a/src/common/hexchatc.h b/src/common/hexchatc.h index 207a97cd..9650dc10 100644 --- a/src/common/hexchatc.h +++ b/src/common/hexchatc.h @@ -25,10 +25,13 @@ extern GSList *ignore_list; extern GSList *usermenu_list; extern GSList *urlhandler_list; extern GSList *tabmenu_list; +extern GList *sess_list_by_lastact[]; session * find_channel (server *serv, char *chan); session * find_dialog (server *serv, char *nick); session * new_ircwindow (server *serv, char *name, int type, int focus); +void lastact_update (session * sess); +session * lastact_getfirst (int (*filter) (session *sess)); int is_session (session * sess); void session_free (session *killsess); void lag_check (void); diff --git a/src/common/inbound.c b/src/common/inbound.c index 41b36296..54e5bff0 100644 --- a/src/common/inbound.c +++ b/src/common/inbound.c @@ -301,7 +301,10 @@ is_hilight (char *from, char *text, session *sess, server *serv) { g_free (text); if (sess != current_tab) + { sess->nick_said = TRUE; + lastact_update (sess); + } fe_set_hilight (sess); return 1; } @@ -364,6 +367,7 @@ inbound_action (session *sess, char *chan, char *from, char *ip, char *text, int sess->msg_said = TRUE; sess->new_data = FALSE; } + lastact_update (sess); } user = userlist_find (sess, from); @@ -421,6 +425,7 @@ inbound_chanmsg (server *serv, session *sess, char *chan, char *from, char *text { sess->msg_said = TRUE; sess->new_data = FALSE; + lastact_update (sess); } user = userlist_find (sess, from); diff --git a/src/common/textevents.in b/src/common/textevents.in index 827900ff..69d42526 100644 --- a/src/common/textevents.in +++ b/src/common/textevents.in @@ -67,7 +67,7 @@ pevt_chanban_help Channel Creation XP_TE_CHANDATE pevt_chandate_help -%C22*%O$tChannel %C22$1%O created +%C22*%O$tChannel %C22$1%O created on %C24$2%O 2 Channel DeHalfOp @@ -589,13 +589,13 @@ n0 Part XP_TE_PART pevt_part_help -%C24*$t$1 has left +%C24*$t$1 ($2) has left 3 Part with Reason XP_TE_PARTREASON pevt_partreason_help -%C24*%O$t%C18$1%C ($2) has left (%C24$4%O) +%C24*$t$1 ($2) has left ($4) 4 Ping Reply diff --git a/src/fe-gtk/fe-gtk.c b/src/fe-gtk/fe-gtk.c index 204b7570..2767fc52 100644 --- a/src/fe-gtk/fe-gtk.c +++ b/src/fe-gtk/fe-gtk.c @@ -674,6 +674,7 @@ fe_print_text (struct session *sess, char *text, time_t stamp) sess->gui->is_tab && !sess->nick_said && stamp == 0) { sess->new_data = TRUE; + lastact_update (sess); if (sess->msg_said) fe_set_tab_color (sess, 2); else diff --git a/src/fe-gtk/fkeys.c b/src/fe-gtk/fkeys.c index e606dfd5..99d244ea 100644 --- a/src/fe-gtk/fkeys.c +++ b/src/fe-gtk/fkeys.c @@ -159,9 +159,9 @@ static struct key_binding *keys_root = NULL; static const struct key_action key_actions[KEY_MAX_ACTIONS + 1] = { {key_action_handle_command, "Run Command", - N_("The \002Run Command\002 action runs the data in Data 1 as if it has been typed into the entry box where you pressed the key sequence. Thus it can contain text (which will be sent to the channel/person), commands or user commands. When run all \002\\n\002 characters in Data 1 are used to deliminate seperate commands so it is possible to run more than one command. If you want a \002\\\002 in the actual text run then enter \002\\\\\002")}, + N_("The \002Run Command\002 action runs the data in Data 1 as if it had been typed into the entry box where you pressed the key sequence. Thus it can contain text (which will be sent to the channel/person), commands or user commands. When run all \002\\n\002 characters in Data 1 are used to deliminate seperate commands so it is possible to run more than one command. If you want a \002\\\002 in the actual text run then enter \002\\\\\002")}, {key_action_page_switch, "Change Page", - N_("The \002Change Page\002 command switches between pages in the notebook. Set Data 1 to the page you want to switch to. If Data 2 is set to anything then the switch will be relative to the current position")}, + N_("The \002Change Page\002 command switches between pages in the notebook. Set Data 1 to the page you want to switch to. If Data 2 is set to anything then the switch will be relative to the current position. Set Data 1 to auto to switch to the page with the most recent and important activity (queries first, then channels with hilight, channels with dialogue, channels with other data)")}, {key_action_insert, "Insert in Buffer", N_("The \002Insert in Buffer\002 command will insert the contents of Data 1 into the entry where the key sequence was pressed at the current cursor position")}, {key_action_scroll_page, "Scroll Page", @@ -405,6 +405,7 @@ key_load_defaults () "A\n3\nChange Page\nD1:3\nD2!\n\n"\ "A\n2\nChange Page\nD1:2\nD2!\n\n"\ "A\n1\nChange Page\nD1:1\nD2!\n\n"\ + "A\ngrave\nChange Page\nD1:auto\nD2!\n\n"\ "C\no\nInsert in Buffer\nD1:\nD2!\n\n"\ "C\nb\nInsert in Buffer\nD1:\nD2!\n\n"\ "C\nk\nInsert in Buffer\nD1:\nD2!\n\n"\ @@ -1199,10 +1200,25 @@ key_action_handle_command (GtkWidget * wid, GdkEventKey * evt, char *d1, return 0; } +/* + * Check if the given session is inside the main window. This predicate + * is passed to lastact_getfirst() as a way to filter out detached sessions. + * XXX: Consider moving this in a different file? + */ +static int +session_check_is_tab(session *sess) +{ + if (!sess || !sess->gui) + return FALSE; + + return (sess->gui->is_tab); +} + static int key_action_page_switch (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2, struct session *sess) { + session *newsess; int len, i, num; if (!d1) @@ -1212,6 +1228,30 @@ key_action_page_switch (GtkWidget * wid, GdkEventKey * evt, char *d1, if (!len) return 1; + if (strcasecmp(d1, "auto") == 0) + { + /* Auto switch makes no sense in detached sessions */ + if (!sess->gui->is_tab) + return 1; + + /* Obtain a session with recent activity */ + newsess = lastact_getfirst(session_check_is_tab); + + if (newsess) + { + /* + * Only sessions in the current window should be considered (i.e. + * we don't want to move the focus on a different window). This + * call could, in theory, do this, but we checked before that + * newsess->gui->is_tab and sess->gui->is_tab. + */ + mg_bring_tofront_sess(newsess); + return 0; + } + else + return 1; + } + for (i = 0; i < len; i++) { if (d1[i] < '0' || d1[i] > '9') diff --git a/src/fe-gtk/maingui.c b/src/fe-gtk/maingui.c index 4947b38c..fee8f320 100644 --- a/src/fe-gtk/maingui.c +++ b/src/fe-gtk/maingui.c @@ -307,6 +307,7 @@ fe_set_tab_color (struct session *sess, int col) break; } + lastact_update (sess); } } @@ -604,6 +605,7 @@ mg_focus (session *sess) sess->nick_said = FALSE; sess->msg_said = FALSE; sess->new_data = FALSE; + lastact_update (sess); /* when called via mg_changui_new, is_tab might be true, but sess->res->tab is still NULL. */ if (sess->res->tab) diff --git a/src/fe-gtk/plugin-tray.c b/src/fe-gtk/plugin-tray.c index 294920da..c5109fb8 100644 --- a/src/fe-gtk/plugin-tray.c +++ b/src/fe-gtk/plugin-tray.c @@ -73,9 +73,13 @@ static int tray_priv_count = 0; static int tray_pub_count = 0; static int tray_hilight_count = 0; static int tray_file_count = 0; +static int tray_restore_timer = 0; void tray_apply_setup (void); +static gboolean tray_menu_try_restore (); +static void tray_cleanup (void); +static void tray_init (void); static WinStatus @@ -301,7 +305,8 @@ tray_set_flash (TrayIcon icon) tray_stop_flash (); gtk_status_icon_set_from_pixbuf (sticon, icon); - flash_tag = g_timeout_add (TIMEOUT, (GSourceFunc) tray_timeout_cb, icon); + if (prefs.hex_gui_tray_blink) + flash_tag = g_timeout_add (TIMEOUT, (GSourceFunc) tray_timeout_cb, icon); } void @@ -395,6 +400,8 @@ tray_toggle_visibility (gboolean force_hide) if (force_hide || GTK_WIDGET_VISIBLE (win)) #endif { + if (prefs.hex_gui_tray_away) + hexchat_command (ph, "ALLSERV AWAY"); gtk_window_get_position (win, &x, &y); screen = gtk_window_get_screen (win); maximized = prefs.hex_gui_win_state; @@ -402,6 +409,8 @@ tray_toggle_visibility (gboolean force_hide) } else { + if (prefs.hex_gui_tray_away) + hexchat_command (ph, "ALLSERV BACK"); gtk_window_set_screen (win, screen); gtk_window_move (win, x, y); if (maximized) @@ -420,6 +429,34 @@ tray_menu_restore_cb (GtkWidget *item, gpointer userdata) } static void +tray_menu_notify_cb (GObject *tray, GParamSpec *pspec, gpointer user_data) +{ + if (sticon) + { + if (!gtk_status_icon_is_embedded (sticon)) + { + tray_restore_timer = g_timeout_add(500, (GSourceFunc)tray_menu_try_restore, NULL); + } + else + { + if (tray_restore_timer) + { + g_source_remove (tray_restore_timer); + tray_restore_timer = 0; + } + } + } +} + +static gboolean +tray_menu_try_restore () +{ + tray_cleanup(); + tray_init(); + return TRUE; +} + +static void tray_menu_quit_cb (GtkWidget *item, gpointer userdata) { mg_open_quit_dialog (FALSE); @@ -626,6 +663,9 @@ tray_init (void) g_signal_connect (G_OBJECT (sticon), "activate", G_CALLBACK (tray_menu_restore_cb), NULL); + + g_signal_connect (G_OBJECT (sticon), "notify::embedded", + G_CALLBACK (tray_menu_notify_cb), NULL); } static int diff --git a/src/fe-gtk/setup.c b/src/fe-gtk/setup.c index df31376d..87a943a5 100644 --- a/src/fe-gtk/setup.c +++ b/src/fe-gtk/setup.c @@ -435,6 +435,7 @@ static const setting alert_settings[] = {ST_TOGGLE, N_("Enable system tray icon"), P_OFFINTNL(hex_gui_tray), 0, 0, 0}, {ST_TOGGLE, N_("Minimize to tray"), P_OFFINTNL(hex_gui_tray_minimize), 0, 0, 0}, {ST_TOGGLE, N_("Close to tray"), P_OFFINTNL(hex_gui_tray_close), 0, 0, 0}, + {ST_TOGGLE, N_("Automatically mark away/back"), P_OFFINTNL(hex_gui_tray_away), N_("When hiding to tray automatically change status."), 0, 0}, #ifndef WIN32 {ST_TOGGLE, N_("Only show tray balloons when hidden or iconified"), P_OFFINTNL(hex_gui_tray_quiet), 0, 0, 0}, #endif |