1 /*
2  * Mouse gestures plugin for Purple
3  *
4  * Copyright (C) 2003 Christian Hammond.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02111-1301, USA.
20  */
21 #include "internal.h"
22 #include "pidgin.h"
23 
24 #include "debug.h"
25 #include "prefs.h"
26 #include "signals.h"
27 #include "version.h"
28 
29 #include "gtkconv.h"
30 #include "gtkplugin.h"
31 #include "gtkutils.h"
32 
33 #include "gstroke.h"
34 
35 #define GESTURES_PLUGIN_ID "gtk-x11-gestures"
36 
37 static void
stroke_close(GtkWidget * widget,void * data)38 stroke_close(GtkWidget *widget, void *data)
39 {
40 	PurpleConversation *conv;
41 	PidginConversation *gtkconv;
42 
43 	conv = (PurpleConversation *)data;
44 
45 	/* Double-check */
46 	if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
47 		return;
48 
49 	gtkconv = PIDGIN_CONVERSATION(conv);
50 
51 	gstroke_cleanup(gtkconv->imhtml);
52 	purple_conversation_destroy(conv);
53 }
54 
55 static void
switch_page(PidginWindow * win,GtkDirectionType dir)56 switch_page(PidginWindow *win, GtkDirectionType dir)
57 {
58 	int count, current;
59 
60 	count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(win->notebook));
61 	current = gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook));
62 
63 	if (dir == GTK_DIR_LEFT)
64 	{
65 		gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), current - 1);
66 	}
67 	else if (dir == GTK_DIR_RIGHT)
68 	{
69 		if (current == count - 1)
70 			gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), 0);
71 		else
72 			gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), current + 1);
73 	}
74 }
75 
76 static void
stroke_prev_tab(GtkWidget * widget,void * data)77 stroke_prev_tab(GtkWidget *widget, void *data)
78 {
79 	PurpleConversation *conv;
80 	PidginConversation *gtkconv;
81 	PidginWindow *win;
82 
83 	conv  = (PurpleConversation *)data;
84 	gtkconv = PIDGIN_CONVERSATION(conv);
85 	win   = gtkconv->win;
86 
87 	switch_page(win, GTK_DIR_LEFT);
88 }
89 
90 static void
stroke_next_tab(GtkWidget * widget,void * data)91 stroke_next_tab(GtkWidget *widget, void *data)
92 {
93 	PurpleConversation *conv;
94 	PidginWindow *win;
95 
96 	conv  = (PurpleConversation *)data;
97 	win   = PIDGIN_CONVERSATION(conv)->win;
98 
99 	switch_page(win, GTK_DIR_RIGHT);
100 }
101 
102 static void
stroke_new_win(GtkWidget * widget,void * data)103 stroke_new_win(GtkWidget *widget, void *data)
104 {
105 	PidginWindow *new_win, *old_win;
106 	PurpleConversation *conv;
107 
108 	conv    = (PurpleConversation *)data;
109 	old_win = PIDGIN_CONVERSATION(conv)->win;
110 
111 	if (pidgin_conv_window_get_gtkconv_count(old_win) <= 1)
112 		return;
113 
114 	new_win = pidgin_conv_window_new();
115 
116 	pidgin_conv_window_remove_gtkconv(old_win, PIDGIN_CONVERSATION(conv));
117 	pidgin_conv_window_add_gtkconv(new_win, PIDGIN_CONVERSATION(conv));
118 
119 	pidgin_conv_window_show(new_win);
120 }
121 
122 static void
attach_signals(PurpleConversation * conv)123 attach_signals(PurpleConversation *conv)
124 {
125 	PidginConversation *gtkconv;
126 
127 	gtkconv = PIDGIN_CONVERSATION(conv);
128 
129 	gstroke_enable(gtkconv->imhtml);
130 	gstroke_signal_connect(gtkconv->imhtml, "14789",  stroke_close,    conv);
131 	gstroke_signal_connect(gtkconv->imhtml, "1456",   stroke_close,    conv);
132 	gstroke_signal_connect(gtkconv->imhtml, "1489",   stroke_close,    conv);
133 	gstroke_signal_connect(gtkconv->imhtml, "74123",  stroke_next_tab, conv);
134 	gstroke_signal_connect(gtkconv->imhtml, "7456",   stroke_next_tab, conv);
135 	gstroke_signal_connect(gtkconv->imhtml, "96321",  stroke_prev_tab, conv);
136 	gstroke_signal_connect(gtkconv->imhtml, "9654",   stroke_prev_tab, conv);
137 	gstroke_signal_connect(gtkconv->imhtml, "25852",  stroke_new_win,  conv);
138 }
139 
140 static void
new_conv_cb(PurpleConversation * conv)141 new_conv_cb(PurpleConversation *conv)
142 {
143 	if (PIDGIN_IS_PIDGIN_CONVERSATION(conv))
144 		attach_signals(conv);
145 }
146 
147 #if 0
148 #if GTK_CHECK_VERSION(2,4,0)
149 static void
150 mouse_button_menu_cb(GtkComboBox *opt, gpointer data)
151 {
152 	int button = gtk_combo_box_get_active(opt);
153 
154 	gstroke_set_mouse_button(button + 2);
155 }
156 #else
157 static void
158 mouse_button_menu_cb(GtkMenuItem *item, gpointer data)
159 {
160 	int button = (int)data;
161 
162 	gstroke_set_mouse_button(button + 2);
163 }
164 #endif
165 #endif
166 
167 static void
toggle_draw_cb(GtkToggleButton * toggle,gpointer data)168 toggle_draw_cb(GtkToggleButton *toggle, gpointer data)
169 {
170 	purple_prefs_set_bool("/plugins/gtk/X11/gestures/visual",
171 		gtk_toggle_button_get_active(toggle));
172 }
173 
174 static void
visual_pref_cb(const char * name,PurplePrefType type,gconstpointer value,gpointer data)175 visual_pref_cb(const char *name, PurplePrefType type, gconstpointer value,
176 			   gpointer data)
177 {
178 	gstroke_set_draw_strokes((gboolean) GPOINTER_TO_INT(value) );
179 }
180 
181 static gboolean
plugin_load(PurplePlugin * plugin)182 plugin_load(PurplePlugin *plugin)
183 {
184 	PurpleConversation *conv;
185 	GList *l;
186 
187 	for (l = purple_get_conversations(); l != NULL; l = l->next) {
188 		conv = (PurpleConversation *)l->data;
189 
190 		if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
191 			continue;
192 
193 		attach_signals(conv);
194 	}
195 
196 	purple_signal_connect(purple_conversations_get_handle(),
197 						"conversation-created",
198 						plugin, PURPLE_CALLBACK(new_conv_cb), NULL);
199 
200 	return TRUE;
201 }
202 
203 static gboolean
plugin_unload(PurplePlugin * plugin)204 plugin_unload(PurplePlugin *plugin)
205 {
206 	PurpleConversation *conv;
207 	PidginConversation *gtkconv;
208 	GList *l;
209 
210 	for (l = purple_get_conversations(); l != NULL; l = l->next) {
211 		conv = (PurpleConversation *)l->data;
212 
213 		if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
214 			continue;
215 
216 		gtkconv = PIDGIN_CONVERSATION(conv);
217 
218 		gstroke_cleanup(gtkconv->imhtml);
219 		gstroke_disable(gtkconv->imhtml);
220 	}
221 
222 	return TRUE;
223 }
224 
225 static GtkWidget *
get_config_frame(PurplePlugin * plugin)226 get_config_frame(PurplePlugin *plugin)
227 {
228 	GtkWidget *ret;
229 	GtkWidget *vbox;
230 	GtkWidget *toggle;
231 #if 0
232 	GtkWidget *opt;
233 #if GTK_CHECK_VERSION(2,4,0)
234 	GtkWidget *menu, *item;
235 #endif
236 #endif
237 
238 	/* Outside container */
239 	ret = gtk_vbox_new(FALSE, 18);
240 	gtk_container_set_border_width(GTK_CONTAINER(ret), 12);
241 
242 	/* Configuration frame */
243 	vbox = pidgin_make_frame(ret, _("Mouse Gestures Configuration"));
244 
245 #if 0
246 #if GTK_CHECK_VERSION(2,4,0)
247 	/* Mouse button drop-down menu */
248 	opt = gtk_combo_box_new_text();
249 
250 	gtk_combo_box_append_text(_("Middle mouse button"));
251 	gtk_combo_box_append_text(_("Right mouse button"));
252 	g_signal_connect(G_OBJECT(opt), "changed",
253 	                 G_CALLBACK(mouse_button_menu_cb), NULL);
254 
255 	gtk_box_pack_start(GTK_BOX(vbox), opt, FALSE, FALSE, 0);
256 	gtk_combo_box_set_active(GTK_COMBO_BOX(opt),
257 							gstroke_get_mouse_button() - 2);
258 #else
259 	/* Mouse button drop-down menu */
260 	menu = gtk_menu_new();
261 	opt = gtk_option_menu_new();
262 
263 	item = gtk_menu_item_new_with_label(_("Middle mouse button"));
264 	g_signal_connect(G_OBJECT(item), "activate",
265 					 G_CALLBACK(mouse_button_menu_cb), opt);
266 	gtk_menu_append(menu, item);
267 
268 	item = gtk_menu_item_new_with_label(_("Right mouse button"));
269 	g_signal_connect(G_OBJECT(item), "activate",
270 					 G_CALLBACK(mouse_button_menu_cb), opt);
271 	gtk_menu_append(menu, item);
272 
273 	gtk_box_pack_start(GTK_BOX(vbox), opt, FALSE, FALSE, 0);
274 	gtk_option_menu_set_menu(GTK_OPTION_MENU(opt), menu);
275 	gtk_option_menu_set_history(GTK_OPTION_MENU(opt),
276 								gstroke_get_mouse_button() - 2);
277 #endif
278 #endif
279 
280 	/* "Visual gesture display" checkbox */
281 	toggle = gtk_check_button_new_with_mnemonic(_("_Visual gesture display"));
282 	gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
283 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
284 			purple_prefs_get_bool("/plugins/gtk/X11/gestures/visual"));
285 	g_signal_connect(G_OBJECT(toggle), "toggled",
286 					 G_CALLBACK(toggle_draw_cb), NULL);
287 
288 	gtk_widget_show_all(ret);
289 
290 	return ret;
291 }
292 
293 static PidginPluginUiInfo ui_info =
294 {
295 	get_config_frame,
296 	0, /* page_num (Reserved) */
297 
298 	/* padding */
299 	NULL,
300 	NULL,
301 	NULL,
302 	NULL
303 };
304 
305 static PurplePluginInfo info =
306 {
307 	PURPLE_PLUGIN_MAGIC,
308 	PURPLE_MAJOR_VERSION,
309 	PURPLE_MINOR_VERSION,
310 	PURPLE_PLUGIN_STANDARD,                             /**< type           */
311 	PIDGIN_PLUGIN_TYPE,                             /**< ui_requirement */
312 	0,                                                /**< flags          */
313 	NULL,                                             /**< dependencies   */
314 	PURPLE_PRIORITY_DEFAULT,                            /**< priority       */
315 
316 	GESTURES_PLUGIN_ID,                               /**< id             */
317 	N_("Mouse Gestures"),                             /**< name           */
318 	DISPLAY_VERSION,                                  /**< version        */
319 	                                                  /**  summary        */
320 	N_("Provides support for mouse gestures"),
321 	                                                  /**  description    */
322 	N_("Allows support for mouse gestures in conversation windows. "
323 	   "Drag the middle mouse button to perform certain actions:\n"
324 	   " • Drag down and then to the right to close a conversation.\n"
325 	   " • Drag up and then to the left to switch to the previous "
326 	   "conversation.\n"
327 	   " • Drag up and then to the right to switch to the next "
328 	   "conversation."),
329 	"Christian Hammond <chipx86@gnupdate.org>",       /**< author         */
330 	PURPLE_WEBSITE,                                     /**< homepage       */
331 
332 	plugin_load,                                      /**< load           */
333 	plugin_unload,                                    /**< unload         */
334 	NULL,                                             /**< destroy        */
335 
336 	&ui_info,                                         /**< ui_info        */
337 	NULL,                                             /**< extra_info     */
338 	NULL,
339 	NULL,
340 
341 	/* padding */
342 	NULL,
343 	NULL,
344 	NULL,
345 	NULL
346 };
347 
348 static void
init_plugin(PurplePlugin * plugin)349 init_plugin(PurplePlugin *plugin)
350 {
351 	purple_prefs_add_none("/plugins/gtk");
352 	purple_prefs_add_none("/plugins/gtk/X11");
353 	purple_prefs_add_none("/plugins/gtk/X11/gestures");
354 	purple_prefs_add_bool("/plugins/gtk/X11/gestures/visual", FALSE);
355 
356 	purple_prefs_connect_callback(plugin, "/plugins/gtk/X11/gestures/visual",
357 								visual_pref_cb, NULL);
358 }
359 
360 PURPLE_INIT_PLUGIN(gestures, init_plugin, info)
361