Guifications - The notification plugin to end all notification plugins! Copyright (C) 2003 Gary Kramlich 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 <gdk-pixbuf/gdk-pixbuf.h> #include "conversation.h" #include "guifications.h" /********************************************************************** ***********************************************************************/ GtkWidget * gf_window = NULL ; GtkWidget * gf_box = NULL ; /*********************************************************************** ***********************************************************************/ static gboolean gf_load ( GaimPlugin * plugin ); static gboolean gf_unload ( GaimPlugin * plugin ); static void init_plugin ( GaimPlugin * plugin ); static void gf_account ( GaimAccount * account , void * data ); static gint gf_rem_account ( gpointer account ); static void gf_event_cb ( GaimBuddy * buddy , gpointer data ); static void make_gf_window (); static gint rem_guification ( gpointer pguification ); static void destroy_guification ( GtkWidget * guification ); static void gf_show_conv ( GaimBuddy * buddy ); static void gf_clicked ( GtkWidget * widget , GdkEventButton * event_button , gpointer data ); static void gf_menu_info_cb ( GtkWidget * widget , GaimBuddy * buddy ); static void gf_menu_im_cb ( GtkWidget * widget , GaimBuddy * buddy ); static void gf_menu_pounce_cb ( GtkWidget * widget , GaimBuddy * buddy ); static void gf_menu_log_cb ( GtkWidget * widget , GaimBuddy * buddy ); static void gf_proto_menu_cb ( GtkMenuItem * item , GaimBuddy * buddy ); static void gf_menu_destroy ( GtkWidget * widget , gpointer data ); static void gf_signed_on ( GaimConnection * gc , void * data ); static void gf_release_check (); static void gf_release_check_cb ( void * userdata , const char * data , size_t len ); /*********************************************************************** ***********************************************************************/ init_plugin ( GaimPlugin * plugin ) { /**********************************************************************/ static GaimGtkPluginUiInfo ui_info = { get_config_frame }; /**********************************************************************/ static GaimPluginInfo gf_info = { GAIM_PLUGIN_STANDARD , /* type */ GAIM_GTK_PLUGIN_TYPE , /* ui requirement */ GAIM_PRIORITY_DEFAULT , /* priority */ my_version , /* version */ my_summary , /* summary */ my_description , /* description */ my_homepage , /* homepage */ /**********************************************************************/ GAIM_INIT_PLUGIN ( my_name , init_plugin , gf_info ) /**********************************************************************/ gf_load ( GaimPlugin * plugin ) { void * blist = gaim_blist_get_handle (); gaim_signal_connect ( gaim_accounts_get_handle (), "account-connecting" , plugin , GAIM_CALLBACK ( gf_account ), NULL ); gaim_signal_connect ( gaim_connections_get_handle (), "signed-on" , plugin , GAIM_CALLBACK ( gf_signed_on ), NULL ); gaim_signal_connect ( blist , "buddy-signed-on" , plugin , GAIM_CALLBACK ( gf_event_cb ), ( gpointer ) gf_event_signon ); gaim_signal_connect ( blist , "buddy-signed-off" , plugin , GAIM_CALLBACK ( gf_event_cb ), ( gpointer ) gf_event_signoff ); gaim_signal_connect ( blist , "buddy-away" , plugin , GAIM_CALLBACK ( gf_event_cb ), ( gpointer ) gf_event_away ); gaim_signal_connect ( blist , "buddy-back" , plugin , GAIM_CALLBACK ( gf_event_cb ), ( gpointer ) gf_event_back ); gaim_signal_connect ( blist , "buddy-idle" , plugin , GAIM_CALLBACK ( gf_event_cb ), ( gpointer ) gf_event_idle ); gaim_signal_connect ( blist , "buddy-unidle" , plugin , GAIM_CALLBACK ( gf_event_cb ), ( gpointer ) gf_event_unidle ); if ( gaim_connections_get_all ()) /**********************************************************************/ gf_unload ( GaimPlugin * plugin ) { g_slist_free ( connecting ); /**********************************************************************/ gf_event_cb ( GaimBuddy * buddy , gpointer data ) { gf_event gf_new_event = ( gf_event ) data ; GtkWidget * new_guification , * new_guification_box ; GdkPixbuf * pb_guification ; GdkPixmap * pm_guification ; dimensions img_size , zoom ; if ( g_slist_find ( connecting , buddy -> account ) != NULL ) if ( ! gaim_prefs_get_bool ( GF_PREF_BEHAVIOR_SHOWAWAY )) if ( buddy -> account -> gc -> away_state != NULL ) if ( ! gaim_prefs_get_bool ( GF_PREF_BEHAVIOR_SHOW_SIGN_ON )) if ( ! gaim_prefs_get_bool ( GF_PREF_BEHAVIOR_SHOW_SIGN_OFF )) if ( ! gaim_prefs_get_bool ( GF_PREF_BEHAVIOR_SHOW_AWAY )) if ( ! gaim_prefs_get_bool ( GF_PREF_BEHAVIOR_SHOW_BACK )) if ( ! gaim_prefs_get_bool ( GF_PREF_BEHAVIOR_SHOW_IDLE )) if ( ! gaim_prefs_get_bool ( GF_PREF_BEHAVIOR_SHOW_UNIDLE )) get_image_size ( & img_size ); pm_guification = make_guification ( buddy , gf_new_event ); get_zoom_size ( img_size , & zoom ); pb_guification = gdk_pixbuf_get_from_drawable ( NULL , pm_guification , NULL , 0 , 0 , 0 , 0 , -1 , -1 ); g_object_unref ( pm_guification ); if ( zoom . width != img_size . width || zoom . height != img_size . height ) pb_guification = gdk_pixbuf_scale_simple ( pb_guification , zoom . width , zoom . height , GDK_INTERP_BILINEAR ); new_guification = gtk_image_new_from_pixbuf ( pb_guification ); g_object_unref ( pb_guification ); gtk_widget_set_size_request ( new_guification , zoom . width , zoom . height ); new_guification_box = gtk_event_box_new (); gtk_widget_set_events ( new_guification_box , GDK_BUTTON_PRESS ); g_object_set_data ( G_OBJECT ( new_guification_box ), "buddy" , buddy ); gtk_container_add ( GTK_CONTAINER ( new_guification_box ), new_guification ); vertical = gaim_prefs_get_bool ( GF_PREF_BEHAVIOR_WINDOW_VERTICAL ); switch ( gaim_prefs_get_int ( GF_PREF_BEHAVIOR_WINDOW_POS )) { gtk_box_pack_start ( GTK_BOX ( gf_box ), new_guification_box , FALSE , FALSE , 0 ); gtk_box_pack_start ( GTK_BOX ( gf_box ), new_guification_box , FALSE , FALSE , 0 ); gtk_box_pack_end ( GTK_BOX ( gf_box ), new_guification_box , FALSE , FALSE , 0 ); gtk_box_pack_end ( GTK_BOX ( gf_box ), new_guification_box , FALSE , FALSE , 0 ); gtk_box_pack_start ( GTK_BOX ( gf_box ), new_guification_box , FALSE , FALSE , 0 ); gtk_box_pack_end ( GTK_BOX ( gf_box ), new_guification_box , FALSE , FALSE , 0 ); gtk_widget_show ( new_guification ); gtk_widget_show ( new_guification_box ); gf_resize ( gf_window , gf_box ); g_signal_connect ( G_OBJECT ( new_guification_box ), "button-press-event" , G_CALLBACK ( gf_clicked ), new_guification_box ); timeout_id = gtk_timeout_add ( gaim_prefs_get_int ( GF_PREF_BEHAVIOR_TIMEOUT ) * 1000 , rem_guification , new_guification_box ); g_object_set_data ( G_OBJECT ( new_guification_box ), "timeout" , GUINT_TO_POINTER ( timeout_id )); if ( ! GTK_WIDGET_VISIBLE ( gf_window )) gtk_widget_show_all ( gf_window ); /**********************************************************************/ rem_guification ( gpointer pguification ) { GtkWidget * guification = ( GtkWidget * ) pguification ; destroy_guification ( guification ); /**********************************************************************/ destroy_guification ( GtkWidget * guification ) { if ( guification != NULL ) { gtk_widget_destroy ( guification ); gf_list = gtk_container_get_children ( GTK_CONTAINER ( gf_box )); gtk_widget_destroy ( gf_box ); gtk_widget_destroy ( gf_window ); gf_resize ( gf_window , gf_box ); /**********************************************************************/ gf_window = gtk_window_new ( GTK_WINDOW_POPUP ); get_image_size ( & def_size ); gtk_window_set_default_size ( GTK_WINDOW ( gf_window ), def_size . width , def_size . height ); gtk_widget_realize ( gf_window ); if ( gaim_prefs_get_bool ( GF_PREF_BEHAVIOR_WINDOW_VERTICAL )) gf_box = gtk_vbox_new ( FALSE , 0 ); gf_box = gtk_hbox_new ( FALSE , 0 ); gtk_container_add ( GTK_CONTAINER ( gf_window ), gf_box ); /**********************************************************************/ make_guification ( GaimBuddy * buddy , gf_event event ) { GdkPixbuf * background = NULL , * protocol = NULL ; PangoFontDescription * font_desc = NULL ; PangoLayout * layout_name , * layout_status ; gchar * name = "" , * message = "" ; if ( gaim_prefs_get_bool ( GF_PREF_APPEARANCE_IMAGE )) background = gdk_pixbuf_new_from_file ( gaim_prefs_get_string ( GF_PREF_APPEARANCE_IMAGE_FILE ), NULL ); background = get_def_background_image (); gdk_pixbuf_render_pixmap_and_mask ( background , & memmap , NULL , 100 ); g_object_unref ( background ); /* if (gaim_prefs_get_int(GF_PREF_APPEARANCE_PROT_POS) != ipos_none) { protocol = get_proto_icon(buddy); gdk_draw_pixbuf(GDK_DRAWABLE(memmap), gf_window->style->bg_gc[GTK_STATE_NORMAL], protocol, 0, 0, px, py, -1, -1, GDK_RGB_DITHER_NORMAL, 0, 0); g_object_unref(protocol); if ( gaim_prefs_get_bool ( GF_PREF_BEHAVIOR_ALIAS )) name = g_strdup ( gaim_get_buddy_alias ( buddy )); name = g_strdup ( buddy -> name ); /* get pango context and setup layouts */ context = gtk_widget_get_pango_context ( gf_window ); layout_name = pango_layout_new ( context ); pango_layout_set_width ( layout_name , -1 ); layout_status = pango_layout_new ( context ); pango_layout_set_width ( layout_status , -1 ); if ( gaim_prefs_get_bool ( GF_PREF_APPEARANCE_FONT )) { font_desc = pango_font_description_from_string ( gaim_prefs_get_string ( GF_PREF_APPEARANCE_FONT_FACE )); pango_layout_set_font_description ( layout_name , font_desc ); pango_layout_set_font_description ( layout_status , font_desc ); pango_font_description_free ( font_desc ); case gf_event_signon : message = g_strdup ( gaim_prefs_get_string ( GF_PREF_MESSAGES_SIGN_ON )); break ; case gf_event_signoff : message = g_strdup ( gaim_prefs_get_string ( GF_PREF_MESSAGES_SIGN_OFF )); break ; case gf_event_away : message = g_strdup ( gaim_prefs_get_string ( GF_PREF_MESSAGES_AWAY )); break ; case gf_event_back : message = g_strdup ( gaim_prefs_get_string ( GF_PREF_MESSAGES_BACK )); break ; case gf_event_idle : message = g_strdup ( gaim_prefs_get_string ( GF_PREF_MESSAGES_IDLE )); break ; case gf_event_unidle : message = g_strdup ( gaim_prefs_get_string ( GF_PREF_MESSAGES_UNIDLE )); break ; pango_layout_set_text ( layout_name , name , -1 ); pango_layout_set_text ( layout_status , message , -1 ); clip_layout ( & layout_name , gaim_prefs_get_bool ( GF_PREF_APPEARANCE_COLOR ), GF_PREF_APPEARANCE_TEXT_X , GF_PREF_COLORS_FOREGROUND ); clip_layout ( & layout_status , gaim_prefs_get_bool ( GF_PREF_APPEARANCE_COLOR ), GF_PREF_APPEARANCE_TEXT_X , GF_PREF_COLORS_FOREGROUND ); get_text_pos ( & x , & y , TRUE , layout_name ); gdk_draw_layout ( GDK_DRAWABLE ( memmap ), gf_window -> style -> bg_gc [ GTK_STATE_NORMAL ], x , y , layout_name ); g_object_unref ( layout_name ); get_text_pos ( & x , & y , FALSE , layout_status ); gdk_draw_layout ( GDK_DRAWABLE ( memmap ), gf_window -> style -> bg_gc [ GTK_STATE_NORMAL ], x , y , layout_status ); g_object_unref ( layout_status ); /**********************************************************************/ gf_account ( GaimAccount * account , void * data ) { if ( g_slist_find ( connecting , account ) == NULL ) { connecting = g_slist_append ( connecting , account ); gtk_timeout_add ( gaim_prefs_get_int ( GF_PREF_ADVANCED_DELAY ), gf_rem_account , account ); /**********************************************************************/ gf_rem_account ( void * paccount ) { GaimAccount * account = ( GaimAccount * ) paccount ; if ( account == NULL || account -> gc == NULL ) { connecting = g_slist_remove ( connecting , account ); if ( account -> gc -> state == GAIM_CONNECTING ) connecting = g_slist_remove ( connecting , account ); /**********************************************************************/ gf_clicked ( GtkWidget * widget , GdkEventButton * event_button , gpointer data ) { GtkWidget * guification = ( GtkWidget * ) data , * menu , * item ; guint timeout_id = GPOINTER_TO_UINT ( g_object_get_data ( G_OBJECT ( guification ), "timeout" )); GaimBuddy * buddy = g_object_get_data ( G_OBJECT ( guification ), "buddy" ); gboolean destroy = FALSE ; GaimPluginProtocolInfo * prplinfo = NULL ; switch ( event_button -> button ) { case 1 : action = gaim_prefs_get_int ( GF_PREF_BEHAVIOR_MOUSE1 ); break ; case 2 : action = gaim_prefs_get_int ( GF_PREF_BEHAVIOR_MOUSE2 ); break ; case 3 : action = gaim_prefs_get_int ( GF_PREF_BEHAVIOR_MOUSE3 ); break ; default : action = window_mouse_destroy ; break ; case window_mouse_destroy : serv_get_info ( buddy -> account -> gc , buddy -> name ); case window_mouse_conversation : gaim_gtk_log_show ( buddy -> name , buddy -> account ); case window_mouse_context : prplinfo = GAIM_PLUGIN_PROTOCOL_INFO ( gaim_find_prpl ( gaim_account_get_protocol ( buddy -> account ))); if ( prplinfo && prplinfo -> get_info ) gaim_new_item_from_stock ( menu , _ ( "_Get Info" ), GAIM_STOCK_INFO , G_CALLBACK ( gf_menu_info_cb ), buddy , 0 , 0 , NULL ); gaim_new_item_from_stock ( menu , _ ( "_IM" ), GAIM_STOCK_IM , G_CALLBACK ( gf_menu_im_cb ), buddy , 0 , 0 , NULL ); gaim_new_item_from_stock ( menu , _ ( "Add Buddy _Pounce" ), NULL , G_CALLBACK ( gf_menu_pounce_cb ), buddy , 0 , 0 , NULL ); gaim_new_item_from_stock ( menu , _ ( "View _Log" ), NULL , G_CALLBACK ( gf_menu_log_cb ), buddy , 0 , 0 , NULL ); if ( prplinfo && prplinfo -> buddy_menu ) { list = prplinfo -> buddy_menu ( buddy -> account -> gc , buddy -> name ); struct proto_buddy_menu * pbm = list -> data ; item = gtk_menu_item_new_with_mnemonic ( pbm -> label ); g_object_set_data ( G_OBJECT ( item ), "gaimcallback" , pbm ); g_signal_connect ( G_OBJECT ( item ), "activate" , G_CALLBACK ( gf_proto_menu_cb ), buddy ); gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ), item ); g_source_remove ( timeout_id ); g_signal_connect ( G_OBJECT ( menu ), "hide" , G_CALLBACK ( gf_menu_destroy ), guification ); gtk_widget_show_all ( menu ); gtk_menu_popup ( GTK_MENU ( menu ), NULL , NULL , NULL , NULL , event_button -> button , event_button -> time ); if ( g_source_remove ( timeout_id )) /* destroy if timer was found, else let timer destroy */ destroy_guification ( guification ); /**********************************************************************/ children = gtk_container_get_children ( GTK_CONTAINER ( gf_box )); children = children -> next ; timeout_id = GPOINTER_TO_UINT ( g_object_get_data ( G_OBJECT ( child ), "timeout" )); g_source_remove ( timeout_id ); destroy_guification ( child ); /**********************************************************************/ gf_show_conv ( GaimBuddy * buddy ) { conv = gaim_conversation_new ( GAIM_CONV_IM , buddy -> account , buddy -> name ); win = gaim_conversation_get_window ( conv ); gaim_conv_window_raise ( win ); gaim_conv_window_switch_conversation ( gaim_conversation_get_window ( conv ), gaim_conversation_get_index ( conv )); if ( GAIM_IS_GTK_WINDOW ( win )) gtk_window_present ( GTK_WINDOW ( GAIM_GTK_WINDOW ( win ) -> window )); /**********************************************************************/ gf_menu_info_cb ( GtkWidget * widget , GaimBuddy * buddy ) { serv_get_info ( buddy -> account -> gc , buddy -> name ); /**********************************************************************/ gf_menu_im_cb ( GtkWidget * widget , GaimBuddy * buddy ) { /**********************************************************************/ gf_menu_pounce_cb ( GtkWidget * widget , GaimBuddy * buddy ) { gaim_gtkpounce_dialog_show ( buddy -> account , buddy -> name , NULL ); /**********************************************************************/ gf_menu_log_cb ( GtkWidget * widget , GaimBuddy * buddy ) { gaim_gtk_log_show ( buddy -> name , buddy -> account ); /**********************************************************************/ gf_proto_menu_cb ( GtkMenuItem * item , GaimBuddy * buddy ) { struct proto_buddy_menu * pbm = g_object_get_data ( G_OBJECT ( item ), "gaimcallback" ); pbm -> callback ( pbm -> gc , buddy -> name ); /**********************************************************************/ gf_menu_destroy ( GtkWidget * widget , gpointer data ) { GtkWidget * guification = ( GtkWidget * ) data ; gint timeout = ( gaim_prefs_get_int ( GF_PREF_BEHAVIOR_TIMEOUT ) * 1000 ) / 2 ; timeout_id = gtk_timeout_add ( timeout , rem_guification , guification ); g_object_set_data ( G_OBJECT ( guification ), "timeout" , GUINT_TO_POINTER ( timeout_id )); /**********************************************************************/ gf_signed_on ( GaimConnection * gc , void * data ) { /**********************************************************************/ int last_checked = gaim_prefs_get_int ( GF_PREF_ADVANCED_RELEASE_CHECK ); if ( gaim_prefs_get_bool ( GF_PREF_ADVANCED_RELEASE )) { if ( ! last_checked || time ( NULL ) - last_checked > 86400 /* one day */ ) { url = g_strdup_printf ( "http://guifications.sourceforge.net/version.php?version=%s" , my_version ); gaim_url_fetch ( url , TRUE , NULL , FALSE , gf_release_check_cb , NULL ); gaim_prefs_set_int ( GF_PREF_ADVANCED_RELEASE_CHECK , time ( NULL )); /**********************************************************************/ gf_release_check_cb ( void * userdata , const char * data , size_t len ) { const gchar * changelog = data ; gchar * cur_ver , * formatted ; /* I wasn't trying to rip Nathan's code.. but I couldn't find a better way to do it... */ while ( changelog [ i ] && changelog [ i ] != '\n' ) cur_ver = g_strndup ( changelog , i ); while ( * changelog == '\n' ) notification = g_string_new ( "" ); g_string_append_printf ( notification , "There is a new version of Guifications (%s) available, you are running version %s.<hr>" , cur_ver , my_version ); formatted = gaim_strdup_withhtml ( changelog ); g_string_append_printf ( notification , "<b>ChangeLog</b><br>%s<br>" , formatted ); g_string_append_printf ( notification , "You can download version %s from <a href= \" http://guifications.sourceforge.net/ \" >http://guifications.sourceforge.net</a>." , cur_ver ); gaim_notify_formatted ( NULL , "New version of Guifications available" , "New version of Guifications available" , NULL , notification -> str , NULL , NULL ); g_string_free ( notification , TRUE ); /**********************************************************************/