pidgin/purple-plugin-pack
Clone
Summary
Browse
Changes
Graph
Housekeeping for release.
2009-08-30, John Bailey
9efa061b0f39
Housekeeping for release.
/*--------------------------------------------------------------------------*
* AUTOPROFILE *
* *
* A Purple away message and profile manager that supports dynamic text *
* *
* AutoProfile is the legal property of its developers. Please refer to *
* the COPYRIGHT file distributed with this source distribution. *
* *
* 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
"comp_rss.h"
#include
"autoprofile.h"
#include
"gtkimhtml.h"
#include
<ctype.h>
static
GtkWidget
*
entry_username
=
NULL
;
static
GtkWidget
*
entry_url
=
NULL
;
GHashTable
*
rss_entries
=
NULL
;
static
GHashTable
*
rss_timeouts
=
NULL
;
GStaticMutex
rss_mutex
=
G_STATIC_MUTEX_INIT
;
/* Core functions */
static
char
*
get_rss_data
(
struct
widget
*
w
,
const
char
*
field
,
int
index
,
struct
tm
**
time
)
{
GList
*
tmp
;
const
struct
rss_entry
*
e
;
char
*
ret
;
int
max
;
g_static_mutex_lock
(
&
rss_mutex
);
tmp
=
(
GList
*
)
g_hash_table_lookup
(
rss_entries
,
w
);
if
(
index
<
0
)
{
g_static_mutex_unlock
(
&
rss_mutex
);
return
strdup
(
_
(
"[ERROR: Invalid entry number]"
));
}
if
(
tmp
==
NULL
)
{
g_static_mutex_unlock
(
&
rss_mutex
);
return
strdup
(
_
(
"[ERROR: No data, invalid URL/account?]"
));
}
if
(
index
!=
0
)
{
while
(
index
--
!=
1
)
{
tmp
=
tmp
->
next
;
if
(
tmp
==
NULL
)
{
g_static_mutex_unlock
(
&
rss_mutex
);
return
strdup
(
_
(
"[ERROR: Insufficient number of entries]"
));
}
}
}
e
=
(
struct
rss_entry
*
)
tmp
->
data
;
if
(
!
strcmp
(
field
,
"link"
))
{
if
(
e
->
url
)
ret
=
strdup
(
e
->
url
);
else
ret
=
NULL
;
}
else
if
(
!
strcmp
(
field
,
"title"
))
{
if
(
e
->
title
)
ret
=
strdup
(
e
->
title
);
else
ret
=
NULL
;
}
else
if
(
!
strcmp
(
field
,
"entry"
))
{
if
(
e
->
entry
)
{
max
=
ap_prefs_get_int
(
w
,
"entry_limit"
);
ret
=
strdup
(
e
->
entry
);
if
(
max
<
g_utf8_strlen
(
ret
,
-1
))
{
gchar
*
tmp
=
g_utf8_offset_to_pointer
(
ret
,
max
);
*
tmp
=
'\0'
;
}
}
else
{
ret
=
NULL
;
}
}
else
if
(
!
strcmp
(
field
,
"time"
))
{
*
time
=
e
->
t
;
ret
=
NULL
;
}
else
{
ret
=
NULL
;
}
g_static_mutex_unlock
(
&
rss_mutex
);
return
ret
;
}
static
char
*
rss_generate
(
struct
widget
*
w
)
{
GString
*
output
;
gchar
*
result
;
char
*
tmp
,
*
time_tmp
;
int
state
;
int
count
;
const
char
*
format
;
char
fmt_char
[
3
];
struct
tm
*
time
;
fmt_char
[
0
]
=
'%'
;
fmt_char
[
2
]
=
'\0'
;
format
=
ap_prefs_get_string
(
w
,
"format"
);
output
=
g_string_new
(
""
);
time_tmp
=
(
char
*
)
malloc
(
sizeof
(
char
)
*
AP_SIZE_MAXIMUM
);
state
=
0
;
count
=
0
;
while
(
*
format
)
{
if
(
state
==
1
)
{
if
(
isdigit
(
*
format
))
{
count
=
(
count
*
10
)
+
(
int
)
*
format
-
48
;
format
++
;
}
else
{
switch
(
*
format
)
{
case
'H'
:
case
'I'
:
case
'p'
:
case
'M'
:
case
'S'
:
case
'a'
:
case
'A'
:
case
'b'
:
case
'B'
:
case
'm'
:
case
'd'
:
case
'j'
:
case
'W'
:
case
'w'
:
case
'y'
:
case
'Y'
:
case
'z'
:
time
=
NULL
;
tmp
=
get_rss_data
(
w
,
"time"
,
count
,
&
time
);
if
(
time
)
{
fmt_char
[
1
]
=
*
format
;
strftime
(
time_tmp
,
AP_SIZE_MAXIMUM
,
fmt_char
,
time
);
g_string_append_printf
(
output
,
"%s"
,
time_tmp
);
}
break
;
case
'l'
:
tmp
=
get_rss_data
(
w
,
"link"
,
count
,
NULL
);
if
(
tmp
)
{
g_string_append_printf
(
output
,
"%s"
,
tmp
);
free
(
tmp
);
}
break
;
case
't'
:
tmp
=
get_rss_data
(
w
,
"title"
,
count
,
NULL
);
if
(
tmp
)
{
g_string_append_printf
(
output
,
"%s"
,
tmp
);
free
(
tmp
);
}
break
;
case
'e'
:
tmp
=
get_rss_data
(
w
,
"entry"
,
count
,
NULL
);
if
(
tmp
)
{
g_string_append_printf
(
output
,
"%s"
,
tmp
);
free
(
tmp
);
}
break
;
case
'%'
:
g_string_append_printf
(
output
,
"%c"
,
*
format
);
break
;
default
:
g_string_append_unichar
(
output
,
g_utf8_get_char
(
format
));
break
;
}
format
=
g_utf8_next_char
(
format
);
state
=
0
;
}
}
else
{
if
(
*
format
==
'%'
)
{
state
=
1
;
count
=
0
;
}
else
{
g_string_append_unichar
(
output
,
g_utf8_get_char
(
format
));
}
format
=
g_utf8_next_char
(
format
);
}
}
result
=
output
->
str
;
g_string_free
(
output
,
FALSE
);
return
result
;
}
static
gboolean
rss_update
(
gpointer
data
)
{
parse_rss
((
struct
widget
*
)
data
);
return
TRUE
;
}
static
void
rss_load
(
struct
widget
*
w
)
{
gpointer
rss_timeout
;
g_static_mutex_lock
(
&
rss_mutex
);
if
(
!
rss_entries
)
{
rss_entries
=
g_hash_table_new
(
NULL
,
NULL
);
}
if
(
!
rss_timeouts
)
{
rss_timeouts
=
g_hash_table_new
(
NULL
,
NULL
);
}
rss_timeout
=
GINT_TO_POINTER
(
g_timeout_add
(
ap_prefs_get_int
(
w
,
"update_rate"
)
*
60
*
1000
,
rss_update
,
w
));
g_hash_table_insert
(
rss_timeouts
,
w
,
rss_timeout
);
g_static_mutex_unlock
(
&
rss_mutex
);
rss_update
(
w
);
}
static
void
rss_unload
(
struct
widget
*
w
)
{
gpointer
rss_timeout
;
g_static_mutex_lock
(
&
rss_mutex
);
rss_timeout
=
g_hash_table_lookup
(
rss_timeouts
,
w
);
g_source_remove
(
GPOINTER_TO_INT
(
rss_timeout
));
g_hash_table_remove
(
rss_timeouts
,
w
);
g_static_mutex_unlock
(
&
rss_mutex
);
}
static
void
rss_init
(
struct
widget
*
w
)
{
ap_prefs_add_int
(
w
,
"type"
,
RSS_XANGA
);
ap_prefs_add_string
(
w
,
"location"
,
""
);
ap_prefs_add_string
(
w
,
"username"
,
""
);
ap_prefs_add_string
(
w
,
"format"
,
"My <a href=
\"
%l
\"
>blog</a> was most recently updated on "
"%1B %1d at %I:%M %p"
);
ap_prefs_add_int
(
w
,
"update_rate"
,
5
);
ap_prefs_add_int
(
w
,
"entry_limit"
,
1000
);
}
/* GUI functions */
static
gboolean
update_refresh_rate
(
GtkWidget
*
widget
,
GdkEventFocus
*
evt
,
struct
widget
*
w
)
{
gpointer
timeout
;
int
minutes
;
minutes
=
gtk_spin_button_get_value_as_int
(
GTK_SPIN_BUTTON
(
widget
));
ap_prefs_set_int
(
w
,
"update_rate"
,
minutes
);
// Kill the current timer and run a new one
g_static_mutex_lock
(
&
rss_mutex
);
timeout
=
g_hash_table_lookup
(
rss_timeouts
,
w
);
g_source_remove
(
GPOINTER_TO_INT
(
timeout
));
timeout
=
GINT_TO_POINTER
(
g_timeout_add
(
minutes
*
60
*
1000
,
rss_update
,
w
));
g_hash_table_replace
(
rss_timeouts
,
w
,
timeout
);
g_static_mutex_unlock
(
&
rss_mutex
);
return
FALSE
;
}
static
void
rss_data_update
(
GtkWidget
*
widget
,
struct
widget
*
w
)
{
rss_update
(
w
);
}
static
void
sensitivity_cb
(
const
char
*
name
,
PurplePrefType
type
,
gconstpointer
val
,
gpointer
data
)
{
int
real_val
=
GPOINTER_TO_INT
(
val
);
if
(
real_val
==
RSS_XANGA
||
real_val
==
RSS_LIVEJOURNAL
)
{
gtk_widget_set_sensitive
(
entry_username
,
TRUE
);
gtk_widget_set_sensitive
(
entry_url
,
FALSE
);
}
else
{
gtk_widget_set_sensitive
(
entry_username
,
FALSE
);
gtk_widget_set_sensitive
(
entry_url
,
TRUE
);
}
}
static
GtkWidget
*
entry
;
static
void
event_cb
(
GtkWidget
*
widget
,
struct
widget
*
w
)
{
ap_prefs_set_string
(
w
,
"format"
,
gtk_imhtml_get_markup
(
GTK_IMHTML
(
entry
)));
}
static
void
formatting_toggle_cb
(
GtkIMHtml
*
imhtml
,
GtkIMHtmlButtons
buttons
,
struct
widget
*
w
)
{
ap_prefs_set_string
(
w
,
"format"
,
gtk_imhtml_get_markup
(
GTK_IMHTML
(
entry
)));
}
static
void
formatting_clear_cb
(
GtkIMHtml
*
imhtml
,
struct
widget
*
w
)
{
ap_prefs_set_string
(
w
,
"format"
,
gtk_imhtml_get_markup
(
GTK_IMHTML
(
entry
)));
}
static
GtkWidget
*
rss_menu
(
struct
widget
*
w
)
{
GtkWidget
*
ret
;
GList
*
options
;
GtkWidget
*
label
,
*
hbox
,
*
button
,
*
spinner
,
*
sw
;
GtkWidget
*
entry_window
,
*
toolbar
;
GtkTextBuffer
*
text_buffer
;
int
value
;
gchar
*
pref
;
ret
=
gtk_vbox_new
(
FALSE
,
5
);
/* Format string */
label
=
gtk_label_new
(
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
"<b>Format string for output</b>"
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0
);
gtk_box_pack_start
(
GTK_BOX
(
ret
),
label
,
FALSE
,
FALSE
,
0
);
hbox
=
gtk_hbox_new
(
FALSE
,
5
);
gtk_box_pack_start
(
GTK_BOX
(
ret
),
hbox
,
TRUE
,
TRUE
,
0
);
entry_window
=
pidgin_create_imhtml
(
TRUE
,
&
entry
,
&
toolbar
,
&
sw
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
entry_window
,
TRUE
,
TRUE
,
0
);
gtk_imhtml_append_text_with_images
(
GTK_IMHTML
(
entry
),
ap_prefs_get_string
(
w
,
"format"
),
0
,
NULL
);
text_buffer
=
gtk_text_view_get_buffer
(
GTK_TEXT_VIEW
(
entry
));
g_signal_connect
(
G_OBJECT
(
text_buffer
),
"changed"
,
G_CALLBACK
(
event_cb
),
w
);
g_signal_connect_after
(
G_OBJECT
(
entry
),
"format_function_toggle"
,
G_CALLBACK
(
formatting_toggle_cb
),
w
);
g_signal_connect_after
(
G_OBJECT
(
entry
),
"format_function_clear"
,
G_CALLBACK
(
formatting_clear_cb
),
w
);
label
=
gtk_label_new
(
_
(
"The following options can be specified with a numerical modifier
\n
"
"(i.e.
\"
%e
\"
can also be written
\"
%1e
\"
or
\"
%2e
\"
). The optional
\n
"
"number specifies which entry to get the data for.
\"
1
\"
refers to the
\n
"
"most recent entry,
\"
2
\"
refers to the second-most recent entry, and so
\n
"
"forth.
\"
1
\"
is used if no number is specified.
\n\n
"
"%e
\t
Starting text of the entry.
\n
"
"%l
\t
Link to the specific entry.
\n
"
"%t
\t
Title of entry (Xanga incompatible)
\n
"
"
\n
Time of entry:
\n
"
"%H
\t
hour of entry(24-hour clock)
\n
"
"%I
\t
hour (12-hour clock)
\n
"
"%p
\t
AM or PM
\n
"
"%M
\t
minute
\n
"
"%S
\t
second
\n
"
"%a
\t
abbreviated weekday name
\n
"
"%A
\t
full weekday name
\n
"
"%b
\t
abbreviated month name
\n
"
"%B
\t
full month name
\n
"
"%m
\t
month (numerical)
\n
"
"%d
\t
day of the month
\n
"
"%j
\t
day of the year
\n
"
"%W
\t
week number of the year
\n
"
"%w
\t
weekday (numerical)
\n
"
"%y
\t
year without century
\n
"
"%Y
\t
year with century
\n
"
"%z
\t
time zone name, if any
\n
"
"%%
\t
%"
));
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0
);
sw
=
gtk_scrolled_window_new
(
NULL
,
NULL
);
gtk_scrolled_window_set_policy
(
GTK_SCROLLED_WINDOW
(
sw
),
GTK_POLICY_NEVER
,
GTK_POLICY_ALWAYS
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
sw
,
TRUE
,
TRUE
,
0
);
gtk_scrolled_window_add_with_viewport
(
GTK_SCROLLED_WINDOW
(
sw
),
label
);
/* Type/URL/Username selection */
label
=
gtk_label_new
(
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
"<b>RSS/Blog location</b>"
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0
);
gtk_box_pack_start
(
GTK_BOX
(
ret
),
label
,
FALSE
,
FALSE
,
0
);
hbox
=
gtk_hbox_new
(
FALSE
,
5
);
gtk_box_pack_start
(
GTK_BOX
(
ret
),
hbox
,
FALSE
,
FALSE
,
0
);
/* Dropdown */
options
=
g_list_append
(
NULL
,
(
char
*
)
_
(
"Xanga"
));
options
=
g_list_append
(
options
,
GINT_TO_POINTER
(
RSS_XANGA
));
options
=
g_list_append
(
options
,
(
char
*
)
_
(
"LiveJournal"
));
options
=
g_list_append
(
options
,
GINT_TO_POINTER
(
RSS_LIVEJOURNAL
));
options
=
g_list_append
(
options
,
(
char
*
)
_
(
"RSS 2.0"
));
options
=
g_list_append
(
options
,
GINT_TO_POINTER
(
RSS_2
));
ap_prefs_dropdown_from_list
(
w
,
hbox
,
NULL
,
PURPLE_PREF_INT
,
"type"
,
options
);
g_list_free
(
options
);
pref
=
ap_prefs_get_pref_name
(
w
,
"type"
);
purple_prefs_connect_callback
(
ap_get_plugin_handle
(),
pref
,
sensitivity_cb
,
w
);
free
(
pref
);
/* Username/URL fields */
entry_username
=
ap_prefs_labeled_entry
(
w
,
hbox
,
_
(
"Username:"
),
"username"
,
NULL
);
entry_url
=
ap_prefs_labeled_entry
(
w
,
hbox
,
_
(
"URL of feed:"
),
"location"
,
NULL
);
value
=
ap_prefs_get_int
(
w
,
"type"
);
if
(
value
==
RSS_XANGA
||
value
==
RSS_LIVEJOURNAL
)
{
gtk_widget_set_sensitive
(
entry_username
,
TRUE
);
gtk_widget_set_sensitive
(
entry_url
,
FALSE
);
}
else
{
gtk_widget_set_sensitive
(
entry_username
,
FALSE
);
gtk_widget_set_sensitive
(
entry_url
,
TRUE
);
}
/* Other options */
label
=
gtk_label_new
(
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
"<b>Other options</b>"
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0
);
gtk_box_pack_start
(
GTK_BOX
(
ret
),
label
,
FALSE
,
FALSE
,
0
);
/* # of chars to display from description */
ap_prefs_labeled_spin_button
(
w
,
ret
,
"Max characters to show in entry (%e)"
,
"entry_limit"
,
1
,
AP_SIZE_MAXIMUM
-
1
,
NULL
);
/* Update rate selection */
hbox
=
gtk_hbox_new
(
FALSE
,
5
);
gtk_box_pack_start
(
GTK_BOX
(
ret
),
hbox
,
FALSE
,
FALSE
,
0
);
label
=
gtk_label_new
(
_
(
"Minutes between checks for updates:"
));
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
label
,
FALSE
,
FALSE
,
0
);
spinner
=
gtk_spin_button_new_with_range
(
1
,
60
,
1
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
spinner
,
FALSE
,
FALSE
,
0
);
gtk_spin_button_set_value
(
GTK_SPIN_BUTTON
(
spinner
),
ap_prefs_get_int
(
w
,
"update_rate"
));
g_signal_connect
(
G_OBJECT
(
spinner
),
"value-changed"
,
G_CALLBACK
(
update_refresh_rate
),
w
);
button
=
gtk_button_new_with_label
(
"Fetch data now!"
);
g_signal_connect
(
G_OBJECT
(
button
),
"clicked"
,
G_CALLBACK
(
rss_data_update
),
w
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
button
,
TRUE
,
TRUE
,
0
);
return
ret
;
}
/* Le end */
struct
component
rss
=
{
N_
(
"RSS / Blogs"
),
N_
(
"Information taken from an RSS feed (Xanga and LiveJournal capable)"
),
"RSS"
,
&
rss_generate
,
&
rss_init
,
&
rss_load
,
&
rss_unload
,
NULL
,
&
rss_menu
};