'smart qotes' now will replace '<', '>' and '&' inside 'pre' and 'code' tags
[k8lowj.git] / src / checkfriends-gtk.c
blob2954483649dc08031224e89f387bb15b58c5b322
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2004 Evan Martin <martine@danga.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
5 */
7 #include "gtk-all.h"
8 #include "util-gtk.h"
10 #include "conf.h"
11 #include "jam.h"
12 #include "spawn.h"
13 #include "menu.h"
14 #include "settings.h"
16 #include "throbber.h"
17 #include "checkfriends.h"
19 struct _CFFloat {
20 CFMgr *cfmgr;
21 GtkWidget *box;
22 GtkWidget *throbber;
23 GtkWidget *win;
26 static const gchar *cf_help = N_(
27 "The Check Friends feature is enabled by right-clicking on this "
28 "button and selecting \"Check friends view for new entries\". Once "
29 "you do so, you will be notified every time one of your friends "
30 "updates their journal with a new entry.\n\n"
32 "Once this happens, the indicator will turn red. You can then "
33 "read your friends page (as a convenience, double-clicking on "
34 "the indicator will take you there). Click on the indicator once "
35 "you've read the new entries to resume monitoring for newer "
36 "entries.\n\n");
38 static void cf_dock_destroy(CFFloat *cff);
40 static void cf_dock_destroyed_cb(GtkWidget *widget, CFFloat *cff);
41 static void cf_float_destroyed_cb(GtkWidget *widget, CFFloat *cff);
43 static void
44 cf_float_update(CFFloat *cff, CFState state) {
45 const gchar *tip_text = NULL;
47 switch (state) {
48 case CF_DISABLED:
49 throbber_stop(THROBBER(cff->throbber));
50 throbber_reset(THROBBER(cff->throbber));
51 tip_text = _("Check Friends disabled");
52 break;
53 case CF_ON:
54 throbber_stop(THROBBER(cff->throbber));
55 throbber_reset(THROBBER(cff->throbber));
56 tip_text = _("No new entries in your friends view");
57 break;
58 case CF_NEW:
59 throbber_start(THROBBER(cff->throbber));
60 tip_text = _("There are new entries in your friends view");
61 break;
64 gtk_tooltips_set_tip(app.tooltips, cff->box, tip_text, _(cf_help));
67 static void
68 cf_toggle_cb(GtkMenuItem *mi, CFFloat* cff) {
69 CFMgr *cfm = cff->cfmgr;
70 CFState state = cfmgr_get_state(cfm);
72 switch (state) {
73 case CF_DISABLED:
74 cfmgr_set_state(cfm, CF_ON);
75 break;
76 case CF_ON:
77 case CF_NEW:
78 cfmgr_set_state(cfm, CF_DISABLED);
79 break;
83 static void
84 open_friends_list(CFMgr *cfm) {
85 char *url;
86 JamAccountLJ *acc;
88 acc = cfmgr_get_account(cfm);
89 if (!acc) return;
91 url = g_strdup_printf("%s/users/%s/friends",
92 jam_account_lj_get_server(acc)->url,
93 jam_account_lj_get_user(acc)->username);
94 spawn_url(NULL /* XXX parent */, url);
95 g_free(url);
98 void
99 cf_context_menu(CFFloat* cff, GdkEventButton *ev) {
100 CFMgr *cfm = cff->cfmgr;
101 CFState state = cfmgr_get_state(cfm);
103 GtkAccelGroup *accelgroup = gtk_accel_group_new();
104 GtkWidget *cfmenu = gtk_menu_new();
105 GtkWidget *item;
107 { /* Check friends on/off */
108 item = gtk_check_menu_item_new_with_mnemonic(
109 _("_Check friends view for new entries"));
110 gtk_menu_shell_append(GTK_MENU_SHELL(cfmenu), item);
111 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),
112 (state != CF_DISABLED));
113 g_signal_connect(G_OBJECT(item), "activate",
114 G_CALLBACK(cf_toggle_cb), cff);
115 gtk_widget_show(item);
118 { /* ---------- */
119 item = gtk_separator_menu_item_new();
120 gtk_menu_shell_append(GTK_MENU_SHELL(cfmenu), item);
121 gtk_widget_show(item);
124 { /* Preferences... */
125 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES,
126 accelgroup);
127 gtk_menu_shell_append(GTK_MENU_SHELL(cfmenu), item);
128 g_signal_connect_swapped(G_OBJECT(item), "activate",
129 G_CALLBACK(settings_cf_run), cfm);
130 gtk_widget_show(item);
133 { /* open friends list in browser */
134 item = gtk_image_menu_item_new_with_mnemonic(
135 _("_Open friends list in browser"));
136 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
137 gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU));
138 gtk_menu_shell_append(GTK_MENU_SHELL(cfmenu), item);
139 g_signal_connect_swapped(G_OBJECT(item), "activate",
140 G_CALLBACK(open_friends_list), cfm);
141 gtk_widget_show_all(item);
144 gtk_menu_popup(GTK_MENU(cfmenu), NULL, NULL, NULL, NULL,
145 ev->button, ev->time);
149 static gboolean
150 clicked_cb(GtkWidget* w, GdkEventButton *ev, CFFloat *cff) {
151 CFMgr *cfm = cff->cfmgr;
152 CFState state = cfmgr_get_state(cfm);
154 /* right-clicks start context menu (note: this case is terminal) */
155 if (ev->button == 3) {
156 cf_context_menu(cff, ev);
157 return TRUE;
160 /* *all* left-clicks move CF_NEW to CF_ON */
161 if (state == CF_NEW)
162 cfmgr_set_state(cfm, CF_ON);
164 if (ev->button == 2) // XXX testing
165 cfmgr_set_state(cfm, CF_NEW);
168 /* and double-clicks open up the browser, too */
169 if (ev->type==GDK_2BUTTON_PRESS) {
170 // XXX open_friends_list(GTK_WINDOW(cff->parent));
172 return TRUE;
175 /* this help box will only be called once in a double-click,
176 * thankfully, because the above is terminal on double-clicks. */
177 /*if (state == CF_DISABLED) {
178 jam_message(GTK_WINDOW(cff->parent), JAM_MSG_INFO, TRUE,
179 _("Check Friends"), _(cf_help));
182 return TRUE;
185 static void
186 cf_float_state_changed_cb(CFMgr *cfm, CFState state, CFFloat *cff) {
187 cf_float_update(cff, state);
189 /* autoraise if necessary */
190 if (state == CF_NEW && conf.options.cffloatraise)
191 gtk_window_present(GTK_WINDOW(cff->win));
194 CFFloat*
195 cf_float_new(CFMgr *cfm) {
196 CFFloat *cff;
198 cff = g_new0(CFFloat, 1);
199 cff->cfmgr = cfm;
201 g_signal_connect(G_OBJECT(cfm), "state_changed",
202 G_CALLBACK(cf_float_state_changed_cb), cff);
204 cff->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
205 g_signal_connect(G_OBJECT(cff->win), "destroy",
206 G_CALLBACK(cf_float_destroyed_cb), cff);
207 gtk_window_set_wmclass(GTK_WINDOW(cff->win),
208 "logjam-checkfriends", "logjam-checkfriends");
209 geometry_tie(cff->win, GEOM_CFFLOAT);
211 cff->box = gtk_event_box_new();
212 gtk_container_set_border_width(GTK_CONTAINER(cff->box), 3);
213 g_signal_connect(G_OBJECT(cff->box), "button_press_event",
214 G_CALLBACK(clicked_cb), cff);
216 cff->throbber = throbber_new();
217 gtk_container_add(GTK_CONTAINER(cff->box), cff->throbber);
219 if (!conf.options.cffloat_decorate) {
220 GtkWidget *frame = gtk_frame_new(NULL);
221 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
222 gtk_container_add(GTK_CONTAINER(cff->win), frame);
223 gtk_container_add(GTK_CONTAINER(frame), cff->box);
224 gtk_window_set_decorated(GTK_WINDOW(cff->win), FALSE);
225 } else {
226 gtk_container_add(GTK_CONTAINER(cff->win), cff->box);
228 cf_float_update(cff, cfmgr_get_state(cfm));
229 gtk_widget_show_all(cff->win);
230 return cff;
233 #ifdef USE_DOCK
234 void
235 cf_update_dock(CFMgr *cfm, GtkWindow* parent) {
236 if (!conf.options.docklet && app.docklet) {
237 cf_dock_destroy(app.docklet);
238 } else if (conf.options.docklet && !app.docklet) {
239 app.docklet = cf_float_new(cfm);
242 #endif
244 static void
245 cf_float_destroyed_cb(GtkWidget *widget, CFFloat *cff) {
246 CFMgr *cfm = cff->cfmgr;
248 g_signal_handlers_disconnect_matched(cfm,
249 G_SIGNAL_MATCH_DATA,
250 0, 0, NULL, NULL,
251 cff);
254 static void
255 cf_float_destroy(CFFloat *cff) {
256 /*CFMgr *cfm = cff->cfmgr;*/
258 if (GTK_IS_WIDGET(cff->win))
259 gtk_widget_destroy(cff->win);
261 app.cf_float = NULL;
264 void
265 cf_app_update_float(void) {
266 if (!conf.options.cffloat && app.cf_float) {
267 cf_float_destroy(app.cf_float);
268 app.cf_float = NULL;
269 } else if (conf.options.cffloat && !app.cf_float) {
270 app.cf_float = cf_float_new(app.cfmgr);
274 /* Docklet code based on docklet plugin for Gaim, copyright Robert McQueen */
275 #ifdef USE_DOCK
276 static void
277 cf_dock_destroy(CFFloat *cff) {
278 /*CFMgr *cfm = cff->cfmgr;*/
280 g_signal_handlers_disconnect_by_func(G_OBJECT(cff->win),
281 G_CALLBACK(cf_dock_destroyed_cb), NULL);
282 gtk_widget_destroy(GTK_WIDGET(cff->win));
284 app.docklet = NULL;
287 static void
288 cf_dock_destroyed_cb(GtkWidget *widget, CFFloat *cff) {
289 app.docklet = NULL;
293 static void
294 cf_dock_setup(CFFloat *ind) {
295 GtkWidget *docklet = app.docklet;
297 g_signal_connect(G_OBJECT(docklet), "destroy",
298 G_CALLBACK(cf_dock_destroyed_cb), ind);
300 gtk_container_add(GTK_CONTAINER(docklet), ind->box);
301 gtk_widget_show_all(GTK_WIDGET(docklet));
303 ind->win = docklet;
306 #endif /* USE_DOCK */
308 void cf_float_decorate_refresh(void) {
309 /* suck a bunch of events in. */
310 while (gtk_events_pending())
311 gtk_main_iteration();
313 if (app.cf_float) {
314 gtk_window_set_decorated(GTK_WINDOW(app.cf_float->win),
315 conf.options.cffloat_decorate);
316 gtk_widget_hide(app.cf_float->win);
317 gtk_widget_show(app.cf_float->win);