poll.h: cosmetix
[k8lowj.git] / src / checkfriends-gtk.c
blobab1808342f944d01debe20e056fa873d3a09e1e4
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2004 Evan Martin <martine@danga.com>
3 */
4 #include "gtk-all.h"
5 #include "util-gtk.h"
7 #include "conf.h"
8 #include "jam.h"
9 #include "menu.h"
10 #include "settings.h"
11 #include "spawn.h"
13 #include "throbber.h"
14 #include "checkfriends.h"
16 struct _CFFloat {
17 CFMgr *cfmgr;
18 GtkWidget *box;
19 GtkWidget *throbber;
20 GtkWidget *win;
24 static const gchar *cf_help =
25 N_("The Check Friends feature is enabled by right-clicking on this "
26 "button and selecting \"Check friends view for new entries\". Once "
27 "you do so, you will be notified every time one of your friends "
28 "updates their journal with a new entry.\n\n"
29 "Once this happens, the indicator will turn red. You can then "
30 "read your friends page (as a convenience, double-clicking on "
31 "the indicator will take you there). Click on the indicator once "
32 "you've read the new entries to resume monitoring for newer " "entries.\n\n");
34 static void cf_dock_destroy (CFFloat *cff);
36 static void cf_dock_destroyed_cb (GtkWidget *widget, CFFloat *cff);
37 static void cf_float_destroyed_cb (GtkWidget *widget, CFFloat *cff);
40 static void cf_float_update (CFFloat *cff, CFState state) {
41 const gchar *tip_text = NULL;
42 switch (state) {
43 case CF_DISABLED:
44 throbber_stop(THROBBER(cff->throbber));
45 throbber_reset(THROBBER(cff->throbber));
46 tip_text = _("Check Friends disabled");
47 break;
48 case CF_ON:
49 throbber_stop(THROBBER(cff->throbber));
50 throbber_reset(THROBBER(cff->throbber));
51 tip_text = _("No new entries in your friends view");
52 break;
53 case CF_NEW:
54 throbber_start(THROBBER(cff->throbber));
55 tip_text = _("There are new entries in your friends view");
56 break;
58 gtk_tooltips_set_tip(app.tooltips, cff->box, tip_text, _(cf_help));
62 static void cf_toggle_cb (GtkMenuItem *mi, CFFloat *cff) {
63 CFMgr *cfm = cff->cfmgr;
64 CFState state = cfmgr_get_state(cfm);
65 switch (state) {
66 case CF_DISABLED:
67 cfmgr_set_state(cfm, CF_ON);
68 break;
69 case CF_ON:
70 case CF_NEW:
71 cfmgr_set_state(cfm, CF_DISABLED);
72 break;
77 static void open_friends_list (CFMgr *cfm) {
78 char *url;
79 JamAccountLJ *acc = cfmgr_get_account(cfm);
80 if (!acc) return;
81 url = g_strdup_printf("%s/users/%s/friends", jam_account_lj_get_server(acc)->url, jam_account_lj_get_user(acc)->username);
82 spawn_url(NULL/* XXX parent */, url);
83 g_free(url);
87 void cf_context_menu (CFFloat *cff, GdkEventButton *ev) {
88 CFMgr *cfm = cff->cfmgr;
89 CFState state = cfmgr_get_state(cfm);
90 GtkAccelGroup *accelgroup = gtk_accel_group_new();
91 GtkWidget *cfmenu = gtk_menu_new();
92 GtkWidget *item;
94 /* Check friends on/off */
95 item = gtk_check_menu_item_new_with_mnemonic(_("_Check friends view for new entries"));
96 gtk_menu_shell_append(GTK_MENU_SHELL(cfmenu), item);
97 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), (state != CF_DISABLED));
98 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(cf_toggle_cb), cff);
99 gtk_widget_show(item);
102 /* ---------- */
103 item = gtk_separator_menu_item_new();
104 gtk_menu_shell_append(GTK_MENU_SHELL(cfmenu), item);
105 gtk_widget_show(item);
108 /* Preferences... */
109 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, accelgroup);
110 gtk_menu_shell_append(GTK_MENU_SHELL(cfmenu), item);
111 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(settings_cf_run), cfm);
112 gtk_widget_show(item);
115 /* open friends list in browser */
116 item = gtk_image_menu_item_new_with_mnemonic(_("_Open friends list in browser"));
117 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU));
118 gtk_menu_shell_append(GTK_MENU_SHELL(cfmenu), item);
119 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(open_friends_list), cfm);
120 gtk_widget_show_all(item);
122 gtk_menu_popup(GTK_MENU(cfmenu), NULL, NULL, NULL, NULL, ev->button, ev->time);
126 static gboolean clicked_cb (GtkWidget *w, GdkEventButton *ev, CFFloat *cff) {
127 CFMgr *cfm = cff->cfmgr;
128 CFState state = cfmgr_get_state(cfm);
129 /* right-clicks start context menu (note: this case is terminal) */
130 if (ev->button == 3) {
131 cf_context_menu(cff, ev);
132 return TRUE;
134 /* *all* left-clicks move CF_NEW to CF_ON */
135 if (state == CF_NEW) cfmgr_set_state(cfm, CF_ON);
136 if (ev->button == 2) cfmgr_set_state(cfm, CF_NEW); // XXX testing
137 /* and double-clicks open up the browser, too */
138 if (ev->type == GDK_2BUTTON_PRESS) {
139 // XXX open_friends_list(GTK_WINDOW(cff->parent));
140 return TRUE;
142 /* this help box will only be called once in a double-click,
143 * thankfully, because the above is terminal on double-clicks. */
145 if (state == CF_DISABLED) {
146 jam_message(GTK_WINDOW(cff->parent), JAM_MSG_INFO, TRUE,
147 _("Check Friends"), _(cf_help));
150 return TRUE;
154 static void cf_float_state_changed_cb (CFMgr *cfm, CFState state, CFFloat *cff) {
155 cf_float_update(cff, state);
156 /* autoraise if necessary */
157 if (state == CF_NEW && conf.options.cffloatraise) gtk_window_present(GTK_WINDOW(cff->win));
161 CFFloat *cf_float_new (CFMgr *cfm) {
162 CFFloat *cff = g_new0(CFFloat, 1);
163 cff->cfmgr = cfm;
164 g_signal_connect(G_OBJECT(cfm), "state_changed", G_CALLBACK(cf_float_state_changed_cb), cff);
166 cff->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
167 g_signal_connect(G_OBJECT(cff->win), "destroy", G_CALLBACK(cf_float_destroyed_cb), cff);
168 gtk_window_set_wmclass(GTK_WINDOW(cff->win), "logjam-checkfriends", "logjam-checkfriends");
169 geometry_tie(cff->win, GEOM_CFFLOAT);
171 cff->box = gtk_event_box_new();
172 gtk_container_set_border_width(GTK_CONTAINER(cff->box), 3);
173 g_signal_connect(G_OBJECT(cff->box), "button_press_event", G_CALLBACK(clicked_cb), cff);
175 cff->throbber = throbber_new();
176 gtk_container_add(GTK_CONTAINER(cff->box), cff->throbber);
178 if (!conf.options.cffloat_decorate) {
179 GtkWidget *frame = gtk_frame_new(NULL);
180 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
181 gtk_container_add(GTK_CONTAINER(cff->win), frame);
182 gtk_container_add(GTK_CONTAINER(frame), cff->box);
183 gtk_window_set_decorated(GTK_WINDOW(cff->win), FALSE);
184 } else {
185 gtk_container_add(GTK_CONTAINER(cff->win), cff->box);
187 cf_float_update(cff, cfmgr_get_state(cfm));
188 gtk_widget_show_all(cff->win);
189 return cff;
193 #ifdef USE_DOCK
194 void cf_update_dock (CFMgr *cfm, GtkWindow *parent) {
195 if (!conf.options.docklet && app.docklet) {
196 cf_dock_destroy(app.docklet);
197 } else if (conf.options.docklet && !app.docklet) {
198 app.docklet = cf_float_new(cfm);
201 #endif
204 static void cf_float_destroyed_cb (GtkWidget *widget, CFFloat *cff) {
205 CFMgr *cfm = cff->cfmgr;
206 g_signal_handlers_disconnect_matched(cfm, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, cff);
210 static void cf_float_destroy (CFFloat *cff) {
211 if (GTK_IS_WIDGET(cff->win)) gtk_widget_destroy(cff->win);
212 app.cf_float = NULL;
216 void cf_app_update_float (void) {
217 if (!conf.options.cffloat && app.cf_float) {
218 cf_float_destroy(app.cf_float);
219 app.cf_float = NULL;
220 } else if (conf.options.cffloat && !app.cf_float) {
221 app.cf_float = cf_float_new(app.cfmgr);
226 /* Docklet code based on docklet plugin for Gaim, copyright Robert McQueen */
227 #ifdef USE_DOCK
228 static void cf_dock_destroy (CFFloat *cff) {
229 g_signal_handlers_disconnect_by_func(G_OBJECT(cff->win), G_CALLBACK(cf_dock_destroyed_cb), NULL);
230 gtk_widget_destroy(GTK_WIDGET(cff->win));
231 app.docklet = NULL;
235 static void cf_dock_destroyed_cb(GtkWidget *widget, CFFloat *cff) {
236 app.docklet = NULL;
238 #endif /* USE_DOCK */
241 void cf_float_decorate_refresh (void) {
242 /* suck a bunch of events in. */
243 while (gtk_events_pending()) gtk_main_iteration();
244 if (app.cf_float) {
245 gtk_window_set_decorated(GTK_WINDOW(app.cf_float->win), conf.options.cffloat_decorate);
246 gtk_widget_hide(app.cf_float->win);
247 gtk_widget_show(app.cf_float->win);