mark PurpleImageClass as private
[pidgin-git.git] / pidgin / plugins / screencap.c
blob8f1f5840a543ebfad99735ca52f10460eb9bb0ba
1 /*
2 * Screen Capture - a plugin that allows taking screenshots and sending them
3 * to your buddies as inline images.
5 * Copyright (C) 2014, Tomasz Wasilczyk <twasilczyk@pidgin.im>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "internal.h"
24 #include <gdk/gdkkeysyms.h>
26 #include "debug.h"
27 #include "glibcompat.h"
28 #include "version.h"
30 #include "gtk3compat.h"
31 #include "gtkconv.h"
32 #include "gtkplugin.h"
33 #include "gtkutils.h"
34 #include "gtkwebviewtoolbar.h"
35 #include "pidginicon.h"
37 #define SCRNCAP_SHOOTING_TIMEOUT 500
38 #define SCRNCAP_DEFAULT_COLOR "#FFFF00000000"
40 enum
42 SCRNCAP_RESPONSE_COLOR
45 static gboolean is_shooting = FALSE;
46 static guint shooting_timeout = 0;
47 static GtkWidget *current_window = NULL;
49 static gint crop_origin_x, crop_origin_y;
50 static gboolean crop_active;
51 static gint crop_x, crop_y, crop_w, crop_h;
53 static gint draw_origin_x, draw_origin_y;
54 static gboolean draw_active;
56 static GdkRGBA brush_color = {1, 0, 0, 1};
57 static gint line_width = 2;
59 /******************************************************************************
60 * libpidgin helper functions
61 ******************************************************************************/
63 static inline void
64 scrncap_conv_set_data(PidginConversation *gtkconv, const gchar *key,
65 gpointer value)
67 g_return_if_fail(gtkconv != NULL);
69 g_object_set_data(G_OBJECT(gtkconv->tab_cont), key, value);
72 static inline gpointer
73 scrncap_conv_get_data(PidginConversation *gtkconv, const gchar *key)
75 g_return_val_if_fail(gtkconv != NULL, NULL);
77 return g_object_get_data(G_OBJECT(gtkconv->tab_cont), key);
80 /******************************************************************************
81 * GdkPixbuf helper functions
82 ******************************************************************************/
84 static GdkPixbuf *
85 scrncap_perform_screenshot(void)
87 GdkWindow *root;
88 gint orig_x, orig_y;
90 root = gdk_get_default_root_window();
91 gdk_window_get_origin(root, &orig_x, &orig_y);
93 return gdk_pixbuf_get_from_window(root, 0, 0,
94 gdk_window_get_width(root), gdk_window_get_height(root));
97 static void
98 scrncap_pixbuf_darken(GdkPixbuf *pixbuf)
100 guchar *pixels;
101 int i, y, width, height, row_width, n_channels, rowstride, pad;
103 pixels = gdk_pixbuf_get_pixels(pixbuf);
104 width = gdk_pixbuf_get_width(pixbuf);
105 height = gdk_pixbuf_get_height(pixbuf);
106 n_channels = gdk_pixbuf_get_n_channels(pixbuf);
107 rowstride = gdk_pixbuf_get_rowstride(pixbuf);
109 row_width = width * n_channels;
110 pad = rowstride - row_width;
111 g_return_if_fail(pad >= 0);
113 for (y = 0; y < height; y++) {
114 for (i = 0; i < row_width; i++, pixels++)
115 *pixels /= 2;
116 pixels += pad;
120 static PurpleImage *
121 scrncap_pixbuf_to_image(GdkPixbuf *pixbuf)
123 PurpleImage *image = NULL;
124 gchar *buffer;
125 gsize count;
126 GError *error = NULL;
128 if (!gdk_pixbuf_save_to_buffer(pixbuf, &buffer, &count, "png",
129 &error, NULL)) {
130 purple_debug_error("screencap", "Failed saving an image: %s",
131 error->message);
132 g_error_free(error);
133 return NULL;
136 image = purple_image_new_take_data((guint8 *)buffer, count);
138 if (purple_image_get_extension(image) == NULL) {
139 purple_debug_error("screencap", "Invalid image format");
140 g_object_unref(image);
141 return NULL;
144 return image;
147 /******************************************************************************
148 * Draw window
149 ******************************************************************************/
151 static gboolean
152 scrncap_drawing_area_btnpress(GtkWidget *draw_area, GdkEventButton *event,
153 gpointer _unused)
155 if (draw_active)
156 return TRUE;
158 draw_origin_x = event->x;
159 draw_origin_y = event->y;
160 draw_active = TRUE;
162 return TRUE;
165 static gboolean
166 scrncap_drawing_area_btnrelease(GtkWidget *draw_area, GdkEvent *event,
167 gpointer _unused)
169 if (!draw_active)
170 return TRUE;
172 draw_active = FALSE;
174 return TRUE;
177 static gboolean
178 scrncap_drawing_area_motion(GtkWidget *draw_area, GdkEventButton *event,
179 gpointer _cr)
181 cairo_t *cr = _cr;
182 int x, y;
183 int redraw_x, redraw_y, redraw_w, redraw_h;
185 x = event->x;
186 y = event->y;
188 if (!draw_active) {
189 draw_origin_x = x;
190 draw_origin_y = y;
191 draw_active = TRUE;
192 return FALSE;
195 cairo_move_to(cr, draw_origin_x, draw_origin_y);
196 cairo_line_to(cr, x, y);
197 cairo_set_line_width(cr, line_width);
198 cairo_stroke(cr);
200 redraw_x = MIN(draw_origin_x, x) - line_width - 1;
201 redraw_y = MIN(draw_origin_y, y) - line_width - 1;
202 redraw_w = MAX(draw_origin_x, x) - redraw_x + line_width + 1;
203 redraw_h = MAX(draw_origin_y, y) - redraw_y + line_width + 1;
205 draw_origin_x = x;
206 draw_origin_y = y;
208 gtk_widget_queue_draw_area(draw_area,
209 redraw_x, redraw_y, redraw_w, redraw_h);
211 return FALSE;
214 static gboolean
215 scrncap_drawing_area_enter(GtkWidget *widget, GdkEvent *event,
216 GdkCursor *draw_cursor)
218 GdkWindow *gdkwindow;
220 gdkwindow = gtk_widget_get_window(GTK_WIDGET(widget));
221 gdk_window_set_cursor(gdkwindow, draw_cursor);
223 return FALSE;
226 static gboolean
227 scrncap_drawing_area_leave(GtkWidget *widget, GdkEvent *event,
228 GdkCursor *draw_cursor)
230 GdkWindow *gdkwindow;
232 gdkwindow = gtk_widget_get_window(GTK_WIDGET(widget));
233 gdk_window_set_cursor(gdkwindow, NULL);
235 return FALSE;
238 static void
239 scrncap_draw_window_close(GtkWidget *window, gpointer _unused)
241 if (current_window != window)
242 return;
244 is_shooting = FALSE;
245 current_window = NULL;
248 static gboolean
249 scrncap_draw_window_paint(GtkWidget *widget, cairo_t *cr, gpointer _surface)
251 cairo_surface_t *surface = _surface;
253 cairo_set_source_surface(cr, surface, 0, 0);
254 cairo_paint(cr);
256 return FALSE;
259 static void
260 scrncap_draw_window_response(GtkDialog *draw_window, gint response_id,
261 gpointer _webview)
263 PidginWebView *webview = PIDGIN_WEBVIEW(_webview);
264 GdkPixbuf *result = NULL;
265 PurpleImage *image;
266 const gchar *fname_prefix;
267 gchar *fname;
268 static guint fname_no = 0;
270 if (response_id == SCRNCAP_RESPONSE_COLOR)
271 return;
273 if (response_id == GTK_RESPONSE_OK) {
274 cairo_surface_t *surface = g_object_get_data(
275 G_OBJECT(draw_window), "surface");
276 result = gdk_pixbuf_get_from_surface(surface, 0, 0,
277 cairo_image_surface_get_width(surface),
278 cairo_image_surface_get_height(surface));
281 gtk_widget_destroy(GTK_WIDGET(draw_window));
283 if (result == NULL)
284 return;
286 image = scrncap_pixbuf_to_image(result);
288 /* translators: this is the file name prefix,
289 * keep it lowercase and pure ASCII.
290 * Please avoid "_" character, use "-" instead. */
291 fname_prefix = _("screenshot-");
292 fname = g_strdup_printf("%s%u", fname_prefix, ++fname_no);
293 purple_image_set_friendly_filename(image, fname);
294 g_free(fname);
296 pidgin_webview_insert_image(webview, image);
297 g_object_unref(image);
300 static void
301 scrncap_draw_color_selected(GtkColorButton *button, cairo_t *cr)
303 gchar *color_str;
305 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(button), &brush_color);
306 gdk_cairo_set_source_rgba(cr, &brush_color);
308 color_str = gdk_rgba_to_string(&brush_color);
309 purple_prefs_set_string("/plugins/gtk/screencap/brush_color", color_str);
310 g_free(color_str);
313 static void
314 scrncap_draw_window(PidginWebView *webview, GdkPixbuf *screen)
316 GtkDialog *draw_window;
317 GtkWidget *drawing_area, *box;
318 GtkWidget *scroll_area;
319 GtkWidget *color_button;
320 int width, height;
321 cairo_t *cr;
322 cairo_surface_t *surface;
323 GdkDisplay *display;
324 GdkCursor *draw_cursor;
326 is_shooting = TRUE;
328 current_window = pidgin_create_dialog(
329 _("Insert screenshot"), 0, "insert-screenshot", TRUE);
330 draw_window = GTK_DIALOG(current_window);
331 gtk_widget_set_size_request(GTK_WIDGET(draw_window), 400, 300);
332 gtk_window_set_position(GTK_WINDOW(draw_window), GTK_WIN_POS_CENTER);
333 g_signal_connect(G_OBJECT(draw_window), "destroy",
334 G_CALLBACK(scrncap_draw_window_close), NULL);
336 display = gtk_widget_get_display(current_window);
337 draw_cursor = gdk_cursor_new_for_display(display, GDK_PENCIL);
338 g_object_set_data_full(G_OBJECT(draw_window), "draw-cursor",
339 draw_cursor, g_object_unref);
341 width = gdk_pixbuf_get_width(screen);
342 height = gdk_pixbuf_get_height(screen);
344 surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
345 cr = cairo_create(surface);
346 g_signal_connect_swapped(G_OBJECT(draw_window), "destroy",
347 G_CALLBACK(cairo_destroy), cr);
348 g_object_set_data_full(G_OBJECT(draw_window), "surface",
349 surface, (GDestroyNotify)cairo_surface_destroy);
351 gdk_cairo_set_source_pixbuf(cr, screen, 0, 0);
352 cairo_rectangle(cr, 0, 0, width, height);
353 cairo_fill(cr);
354 g_object_unref(screen);
356 drawing_area = gtk_drawing_area_new();
357 gtk_widget_set_size_request(drawing_area, width, height);
358 g_signal_connect(G_OBJECT(drawing_area), "draw",
359 G_CALLBACK(scrncap_draw_window_paint), surface);
360 gtk_widget_add_events(drawing_area, GDK_BUTTON_PRESS_MASK |
361 GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK |
362 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
363 g_signal_connect(G_OBJECT(drawing_area), "button-press-event",
364 G_CALLBACK(scrncap_drawing_area_btnpress), NULL);
365 g_signal_connect(G_OBJECT(drawing_area), "button-release-event",
366 G_CALLBACK(scrncap_drawing_area_btnrelease), NULL);
367 g_signal_connect(G_OBJECT(drawing_area), "motion-notify-event",
368 G_CALLBACK(scrncap_drawing_area_motion), cr);
369 g_signal_connect(G_OBJECT(drawing_area), "enter-notify-event",
370 G_CALLBACK(scrncap_drawing_area_enter), draw_cursor);
371 g_signal_connect(G_OBJECT(drawing_area), "leave-notify-event",
372 G_CALLBACK(scrncap_drawing_area_leave), draw_cursor);
374 box = drawing_area;
375 g_object_set(drawing_area,
376 "halign", GTK_ALIGN_CENTER,
377 "valign", GTK_ALIGN_CENTER,
378 NULL);
379 scroll_area = pidgin_make_scrollable(box,
380 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC,
381 GTK_SHADOW_NONE, -1, -1);
382 g_object_set(G_OBJECT(scroll_area), "expand", TRUE, NULL);
383 gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(
384 GTK_DIALOG(draw_window))), scroll_area);
386 color_button = gtk_color_button_new();
387 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(color_button),
388 &brush_color);
389 g_signal_connect(G_OBJECT(color_button), "color-set",
390 G_CALLBACK(scrncap_draw_color_selected), cr);
391 scrncap_draw_color_selected(GTK_COLOR_BUTTON(color_button), cr);
393 gtk_dialog_add_action_widget(draw_window, color_button,
394 SCRNCAP_RESPONSE_COLOR);
395 gtk_dialog_add_button(draw_window, _("_Add"), GTK_RESPONSE_OK);
396 gtk_dialog_add_button(draw_window, _("_Cancel"),
397 GTK_RESPONSE_CANCEL);
398 gtk_dialog_set_default_response(draw_window, GTK_RESPONSE_OK);
399 g_signal_connect(G_OBJECT(draw_window), "response",
400 G_CALLBACK(scrncap_draw_window_response), webview);
402 gtk_widget_show_all(GTK_WIDGET(draw_window));
405 /******************************************************************************
406 * Crop window
407 ******************************************************************************/
409 static void
410 scrncap_crop_window_close(GtkWidget *window, gpointer _unused)
412 if (current_window != window)
413 return;
415 is_shooting = FALSE;
416 current_window = NULL;
419 static gboolean
420 scrncap_crop_window_keypress(GtkWidget *crop_window, GdkEventKey *event,
421 gpointer _webview)
423 PidginWebView *webview = PIDGIN_WEBVIEW(_webview);
424 guint key = event->keyval;
426 if (key == GDK_KEY_Escape) {
427 gtk_widget_destroy(crop_window);
428 return TRUE;
430 if (key == GDK_KEY_Return) {
431 GdkPixbuf *screenshot, *subscreen, *result;
433 screenshot = g_object_get_data(G_OBJECT(crop_window),
434 "screenshot");
435 subscreen = gdk_pixbuf_new_subpixbuf(screenshot,
436 crop_x, crop_y, crop_w, crop_h);
437 result = gdk_pixbuf_copy(subscreen);
438 g_object_unref(subscreen);
440 gtk_widget_destroy(crop_window);
442 scrncap_draw_window(webview, result);
444 return TRUE;
447 return FALSE;
450 static gboolean
451 scrncap_crop_window_focusout(GtkWidget *window, GdkEventFocus *event,
452 gpointer _unused)
454 gtk_widget_destroy(window);
455 return FALSE;
458 static gboolean
459 scrncap_crop_window_btnpress(GtkWidget *window, GdkEventButton *event,
460 gpointer _unused)
462 GtkWidget *hint_box;
463 GtkImage *selection;
464 GtkFixed *cont;
466 g_return_val_if_fail(!crop_active, TRUE);
468 hint_box = g_object_get_data(G_OBJECT(window), "hint-box");
469 if (hint_box) {
470 gtk_widget_destroy(hint_box);
471 g_object_set_data(G_OBJECT(window), "hint-box", NULL);
474 selection = g_object_get_data(G_OBJECT(window), "selection");
475 cont = g_object_get_data(G_OBJECT(window), "cont");
477 gtk_fixed_move(cont, GTK_WIDGET(selection), -10, -10);
478 gtk_image_set_from_pixbuf(selection, NULL);
479 gtk_widget_show(GTK_WIDGET(selection));
481 crop_origin_x = event->x_root;
482 crop_origin_y = event->y_root;
483 crop_active = TRUE;
485 return TRUE;
488 static gboolean
489 scrncap_crop_window_btnrelease(GtkWidget *window, GdkEvent *event,
490 gpointer _unused)
492 crop_active = FALSE;
494 return TRUE;
497 static gboolean
498 scrncap_crop_window_motion(GtkWidget *window, GdkEventButton *event,
499 gpointer _unused)
501 GtkFixed *cont;
502 GtkImage *selection;
503 GdkPixbuf *crop, *screenshot;
505 g_return_val_if_fail(crop_active, FALSE);
507 selection = g_object_get_data(G_OBJECT(window), "selection");
508 cont = g_object_get_data(G_OBJECT(window), "cont");
510 crop_x = MIN(crop_origin_x, event->x_root);
511 crop_y = MIN(crop_origin_y, event->y_root);
512 crop_w = abs(crop_origin_x - event->x_root);
513 crop_h = abs(crop_origin_y - event->y_root);
514 crop_w = MAX(crop_w, 1);
515 crop_h = MAX(crop_h, 1);
517 gtk_fixed_move(cont, GTK_WIDGET(selection), crop_x, crop_y);
519 screenshot = g_object_get_data(G_OBJECT(window), "screenshot");
520 crop = gdk_pixbuf_new_subpixbuf(screenshot,
521 crop_x, crop_y, crop_w, crop_h);
522 gtk_image_set_from_pixbuf(GTK_IMAGE(selection), crop);
523 g_object_unref(crop);
525 return FALSE;
528 static void
529 scrncap_crop_window_realize(GtkWidget *crop_window, gpointer _unused)
531 GdkWindow *gdkwindow;
532 GdkDisplay *display;
533 GdkCursor *cursor;
535 gdkwindow = gtk_widget_get_window(GTK_WIDGET(crop_window));
536 display = gdk_window_get_display(gdkwindow);
538 gdk_window_set_events(gdkwindow, gdk_window_get_events(gdkwindow) |
539 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
540 GDK_BUTTON_MOTION_MASK);
542 cursor = gdk_cursor_new_for_display(display, GDK_CROSSHAIR);
543 gdk_window_set_cursor(gdkwindow, cursor);
544 g_object_unref(cursor);
547 static gboolean
548 scrncap_do_screenshot_cb(gpointer _webview)
550 PidginWebView *webview = PIDGIN_WEBVIEW(_webview);
551 GtkWindow *crop_window;
552 GdkPixbuf *screenshot, *screenshot_d;
553 int width, height;
554 GtkFixed *cont;
555 GtkImage *image, *selection;
556 GtkWidget *hint;
557 gchar *hint_msg;
558 GtkRequisition hint_size;
559 GtkWidget *hint_box;
561 shooting_timeout = 0;
562 crop_active = FALSE;
564 (void)webview;
566 screenshot = scrncap_perform_screenshot();
567 g_return_val_if_fail(screenshot != NULL, G_SOURCE_REMOVE);
568 width = gdk_pixbuf_get_width(screenshot);
569 height = gdk_pixbuf_get_height(screenshot);
571 crop_x = crop_y = 0;
572 crop_w = width;
573 crop_h = height;
575 current_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
576 crop_window = GTK_WINDOW(current_window);
577 gtk_window_set_decorated(crop_window, FALSE);
578 gtk_window_set_resizable(crop_window, FALSE);
579 gtk_widget_set_size_request(GTK_WIDGET(crop_window), width, height);
580 gtk_window_fullscreen(crop_window);
581 gtk_window_set_keep_above(crop_window, TRUE);
583 g_signal_connect(G_OBJECT(crop_window), "realize",
584 G_CALLBACK(scrncap_crop_window_realize), NULL);
585 g_signal_connect(G_OBJECT(crop_window), "destroy",
586 G_CALLBACK(scrncap_crop_window_close), NULL);
587 g_signal_connect(G_OBJECT(crop_window), "key-press-event",
588 G_CALLBACK(scrncap_crop_window_keypress), webview);
589 g_signal_connect(G_OBJECT(crop_window), "focus-out-event",
590 G_CALLBACK(scrncap_crop_window_focusout), NULL);
591 g_signal_connect(G_OBJECT(crop_window), "button-press-event",
592 G_CALLBACK(scrncap_crop_window_btnpress), NULL);
593 g_signal_connect(G_OBJECT(crop_window), "button-release-event",
594 G_CALLBACK(scrncap_crop_window_btnrelease), NULL);
595 g_signal_connect(G_OBJECT(crop_window), "motion-notify-event",
596 G_CALLBACK(scrncap_crop_window_motion), NULL);
597 g_object_set_data_full(G_OBJECT(crop_window), "screenshot",
598 screenshot, g_object_unref);
600 cont = GTK_FIXED(gtk_fixed_new());
601 g_object_set_data(G_OBJECT(crop_window), "cont", cont);
602 gtk_container_add(GTK_CONTAINER(crop_window), GTK_WIDGET(cont));
604 screenshot_d = gdk_pixbuf_copy(screenshot);
605 scrncap_pixbuf_darken(screenshot_d);
606 image = GTK_IMAGE(gtk_image_new_from_pixbuf(screenshot_d));
607 g_object_unref(screenshot_d);
608 gtk_fixed_put(cont, GTK_WIDGET(image), 0, 0);
610 selection = GTK_IMAGE(gtk_image_new_from_pixbuf(NULL));
611 gtk_fixed_put(cont, GTK_WIDGET(selection), -10, -10);
612 g_object_set_data(G_OBJECT(crop_window), "selection", selection);
614 hint = gtk_label_new(NULL);
615 hint_msg = g_strdup_printf("<span size='x-large'>%s</span>",
616 _("Select the region to send and press Enter button to confirm "
617 "or press Escape button to cancel"));
618 gtk_label_set_markup(GTK_LABEL(hint), hint_msg);
619 g_free(hint_msg);
620 gtk_widget_set_margin_start(hint, 10);
621 gtk_widget_set_margin_end(hint, 10);
622 gtk_widget_set_margin_top(hint, 7);
623 gtk_widget_set_margin_bottom(hint, 7);
624 hint_box = gtk_event_box_new();
625 gtk_container_add(GTK_CONTAINER(hint_box), hint);
626 gtk_widget_get_preferred_size(hint, NULL, &hint_size);
627 gtk_fixed_put(cont, hint_box,
628 width / 2 - hint_size.width / 2 - 10,
629 height / 2 - hint_size.height / 2 - 7);
630 g_object_set_data(G_OBJECT(crop_window), "hint-box", hint_box);
632 gtk_widget_show_all(GTK_WIDGET(crop_window));
633 gtk_widget_hide(GTK_WIDGET(selection));
635 return G_SOURCE_REMOVE;
638 static void
639 scrncap_do_screenshot(GtkAction *action, PidginWebView *webview)
641 if (current_window) {
642 gtk_window_present(GTK_WINDOW(current_window));
643 return;
645 if (is_shooting)
646 return;
647 is_shooting = TRUE;
649 shooting_timeout = g_timeout_add(SCRNCAP_SHOOTING_TIMEOUT,
650 scrncap_do_screenshot_cb, webview);
653 /******************************************************************************
654 * PidginConversation setup
655 ******************************************************************************/
657 static void
658 scrncap_convwin_switch(GtkNotebook *notebook, GtkWidget *page, gint page_num,
659 gpointer _win)
661 PidginConvWindow *win = _win;
662 PidginConversation *gtkconv;
663 PidginWebView *webview;
664 gboolean images_supported;
665 GtkAction *action;
667 gtkconv = pidgin_conv_window_get_active_gtkconv(win);
668 if (gtkconv == NULL)
669 return;
671 webview = PIDGIN_WEBVIEW(gtkconv->entry);
672 action = g_object_get_data(G_OBJECT(win->menu->menubar),
673 "insert-screenshot-action");
675 g_return_if_fail(action != NULL);
677 images_supported = pidgin_webview_get_format_functions(webview) &
678 PIDGIN_WEBVIEW_IMAGE;
680 gtk_action_set_sensitive(action, images_supported);
683 static void
684 scrncap_convwin_menu_cb(GtkAction *action, PidginConvWindow *win)
686 PidginConversation *gtkconv;
687 PidginWebView *webview;
689 gtkconv = pidgin_conv_window_get_active_gtkconv(win);
690 webview = PIDGIN_WEBVIEW(gtkconv->entry);
692 scrncap_do_screenshot(action, webview);
695 static void
696 scrncap_convwin_init(PidginConvWindow *win)
698 PidginConvWindowMenu *menu = win->menu;
699 GtkAction *action;
700 GtkWidget *conv_submenu, *conv_insert_image;
701 GtkWidget *scrncap_btn_menu;
702 gint pos = -1, i;
703 GList *children, *it;
705 action = g_object_get_data(G_OBJECT(menu->menubar),
706 "insert-screenshot-action");
707 if (action != NULL)
708 return;
710 action = gtk_action_new("InsertScreenshot", _("Insert Screens_hot..."),
711 NULL, PIDGIN_ICON_CAMERA_PHOTO);
712 gtk_action_set_is_important(action, TRUE);
713 g_object_set_data_full(G_OBJECT(menu->menubar),
714 "insert-screenshot-action", action, g_object_unref);
715 g_signal_connect(G_OBJECT(action), "activate",
716 G_CALLBACK(scrncap_convwin_menu_cb), win);
718 conv_insert_image = gtk_ui_manager_get_widget(menu->ui,
719 "/Conversation/ConversationMenu/InsertImage");
720 g_return_if_fail(conv_insert_image != NULL);
721 conv_submenu = gtk_widget_get_parent(conv_insert_image);
723 pos = -1;
724 children = gtk_container_get_children(GTK_CONTAINER(conv_submenu));
725 for (it = children, i = 0; it; it = g_list_next(it), i++) {
726 if (it->data == conv_insert_image) {
727 pos = i + 1;
728 break;
731 g_list_free(children);
732 g_warn_if_fail(pos >= 0);
734 scrncap_btn_menu = gtk_action_create_menu_item(action);
735 g_object_set_data(G_OBJECT(menu->menubar), "insert-screenshot-btn",
736 scrncap_btn_menu);
737 gtk_menu_shell_insert(GTK_MENU_SHELL(conv_submenu),
738 GTK_WIDGET(scrncap_btn_menu), pos);
739 gtk_widget_show(GTK_WIDGET(scrncap_btn_menu));
741 g_signal_connect_after(G_OBJECT(win->notebook), "switch-page",
742 G_CALLBACK(scrncap_convwin_switch), win);
743 scrncap_convwin_switch(GTK_NOTEBOOK(win->notebook), NULL, 0, win);
746 static void
747 scrncap_convwin_uninit(PidginConvWindow *win)
749 PidginConvWindowMenu *menu = win->menu;
750 GtkWidget *btn;
752 btn = g_object_get_data(G_OBJECT(menu->menubar),
753 "insert-screenshot-btn");
754 if (btn)
755 gtk_widget_destroy(btn);
757 g_object_set_data(G_OBJECT(menu->menubar),
758 "insert-screenshot-btn", NULL);
759 g_object_set_data(G_OBJECT(menu->menubar),
760 "insert-screenshot-action", NULL);
762 g_signal_handlers_disconnect_matched(win->notebook, G_SIGNAL_MATCH_FUNC,
763 0, 0, NULL, scrncap_convwin_switch, NULL);
766 static void
767 scrncap_conversation_update(PidginWebView *webview,
768 PidginWebViewButtons buttons, gpointer _action)
770 GtkAction *action = GTK_ACTION(_action);
772 gtk_action_set_sensitive(action, buttons & PIDGIN_WEBVIEW_IMAGE);
775 static void
776 scrncap_conversation_init(PidginConversation *gtkconv)
778 PidginWebView *webview;
779 PidginWebViewToolbar *toolbar;
780 GtkAction *action;
781 GtkToolItem *scrncap_btn_wide;
782 GtkWidget *scrncap_btn_lean;
783 gint pos = -1, i;
784 GtkToolbar *wide_view, *lean_view;
785 GtkMenu *wide_menu = NULL;
786 GList *wide_children, *it;
788 if (scrncap_conv_get_data(gtkconv, "scrncap-btn-wide") != NULL)
789 return;
791 webview = PIDGIN_WEBVIEW(gtkconv->entry);
792 toolbar = PIDGIN_WEBVIEWTOOLBAR(pidgin_webview_get_toolbar(webview));
793 g_return_if_fail(toolbar != NULL);
794 wide_view = GTK_TOOLBAR(pidgin_webviewtoolbar_get_wide_view(toolbar));
795 g_return_if_fail(wide_view != NULL);
796 lean_view = GTK_TOOLBAR(pidgin_webviewtoolbar_get_lean_view(toolbar));
797 g_return_if_fail(lean_view != NULL);
799 action = gtk_action_new("InsertScreenshot", _("_Screenshot"),
800 _("Insert screenshot"), PIDGIN_ICON_CAMERA_PHOTO);
801 gtk_action_set_is_important(action, TRUE);
802 g_signal_connect(G_OBJECT(action), "activate",
803 G_CALLBACK(scrncap_do_screenshot), webview);
805 scrncap_btn_wide = GTK_TOOL_ITEM(gtk_action_create_tool_item(action));
806 scrncap_conv_set_data(gtkconv, "scrncap-btn-wide", scrncap_btn_wide);
807 for (i = 0; i < gtk_toolbar_get_n_items(wide_view); i++) {
808 GtkToolItem *ref_item = gtk_toolbar_get_nth_item(wide_view, i);
809 GtkAction *action;
811 action = g_object_get_data(G_OBJECT(ref_item), "action");
812 if (action == NULL)
813 continue;
815 if (g_strcmp0(gtk_action_get_name(action), "InsertImage") == 0) {
816 pos = i + 1;
817 break;
820 gtk_toolbar_insert(wide_view, scrncap_btn_wide, pos);
821 gtk_widget_show(GTK_WIDGET(scrncap_btn_wide));
823 for (i = 0; i < gtk_toolbar_get_n_items(lean_view); i++) {
824 GtkToolItem *ref_item = gtk_toolbar_get_nth_item(lean_view, i);
825 const gchar *menu_name;
827 menu_name = g_object_get_data(G_OBJECT(ref_item), "menu-name");
828 if (g_strcmp0(menu_name, "insert") == 0) {
829 wide_menu = g_object_get_data(G_OBJECT(ref_item), "menu");
830 break;
833 g_return_if_fail(wide_menu);
835 pos = -1;
836 wide_children = gtk_container_get_children(GTK_CONTAINER(wide_menu));
837 for (it = wide_children, i = 0; it; it = g_list_next(it), i++) {
838 GtkWidget *child = it->data;
839 GtkAction *action;
841 action = g_object_get_data(G_OBJECT(child), "action");
842 if (action == NULL)
843 continue;
845 if (g_strcmp0(gtk_action_get_name(action), "InsertImage") == 0) {
846 pos = i + 1;
847 break;
850 g_list_free(wide_children);
851 if (pos < 0) {
852 g_warn_if_fail(pos >= 0);
853 pos = 0;
856 g_signal_connect_object(G_OBJECT(webview), "allowed-formats-updated",
857 G_CALLBACK(scrncap_conversation_update), action, 0);
858 scrncap_conversation_update(webview,
859 pidgin_webview_get_format_functions(webview), action);
861 scrncap_btn_lean = gtk_action_create_menu_item(action);
862 scrncap_conv_set_data(gtkconv, "scrncap-btn-lean", scrncap_btn_lean);
863 gtk_menu_shell_insert(GTK_MENU_SHELL(wide_menu),
864 GTK_WIDGET(scrncap_btn_lean), pos);
865 gtk_widget_show(GTK_WIDGET(scrncap_btn_lean));
868 static void
869 scrncap_conversation_uninit(PidginConversation *gtkconv)
871 GtkWidget *scrncap_btn_wide, *scrncap_btn_lean;
873 scrncap_btn_wide = scrncap_conv_get_data(gtkconv, "scrncap-btn-wide");
874 if (scrncap_btn_wide == NULL)
875 return;
877 scrncap_btn_lean = scrncap_conv_get_data(gtkconv, "scrncap-btn-lean");
879 gtk_widget_destroy(scrncap_btn_wide);
880 if (scrncap_btn_lean)
881 gtk_widget_destroy(scrncap_btn_lean);
883 scrncap_conv_set_data(gtkconv, "scrncap-btn-wide", NULL);
884 scrncap_conv_set_data(gtkconv, "scrncap-btn-lean", NULL);
887 /******************************************************************************
888 * Plugin setup
889 ******************************************************************************/
891 static PidginPluginInfo *
892 plugin_query(GError **error)
894 const gchar * const authors[] = {
895 "Tomasz Wasilczyk <twasilczyk@pidgin.im>",
896 NULL
899 return pidgin_plugin_info_new(
900 "id", "gtk-screencap",
901 "name", N_("Screen Capture"),
902 "version", DISPLAY_VERSION,
903 "category", N_("Utility"),
904 "summary", N_("Send screenshots to your buddies."),
905 "description", N_("Adds an option to send a screenshot as an inline "
906 "image. It works only with protocols that supports "
907 "inline images."),
908 "authors", authors,
909 "website", PURPLE_WEBSITE,
910 "abi-version", PURPLE_ABI_VERSION,
911 NULL
915 static gboolean
916 plugin_load(PurplePlugin *plugin, GError **error)
918 GList *it;
919 const gchar *color_str;
921 purple_prefs_add_none("/plugins");
922 purple_prefs_add_none("/plugins/gtk");
923 purple_prefs_add_none("/plugins/gtk/screencap");
924 purple_prefs_add_string("/plugins/gtk/screencap/brush_color",
925 SCRNCAP_DEFAULT_COLOR);
927 color_str = purple_prefs_get_string("/plugins/gtk/screencap/brush_color");
928 if (color_str && color_str[0])
929 gdk_rgba_parse(&brush_color, color_str);
931 purple_signal_connect(pidgin_conversations_get_handle(),
932 "conversation-displayed", plugin,
933 PURPLE_CALLBACK(scrncap_conversation_init), NULL);
934 purple_signal_connect(pidgin_conversations_get_handle(),
935 "conversation-window-created", plugin,
936 PURPLE_CALLBACK(scrncap_convwin_init), NULL);
938 it = purple_conversations_get_all();
939 for (; it; it = g_list_next(it)) {
940 PurpleConversation *conv = it->data;
942 if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
943 continue;
944 scrncap_conversation_init(PIDGIN_CONVERSATION(conv));
947 it = pidgin_conv_windows_get_list();
948 for (; it; it = g_list_next(it)) {
949 PidginConvWindow *win = it->data;
950 scrncap_convwin_init(win);
953 return TRUE;
956 static gboolean
957 plugin_unload(PurplePlugin *plugin, GError **error)
959 GList *it;
961 if (shooting_timeout > 0)
962 g_source_remove(shooting_timeout);
963 if (current_window != NULL)
964 gtk_widget_destroy(GTK_WIDGET(current_window));
966 it = purple_conversations_get_all();
967 for (; it; it = g_list_next(it)) {
968 PurpleConversation *conv = it->data;
970 if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
971 continue;
972 scrncap_conversation_uninit(PIDGIN_CONVERSATION(conv));
975 it = pidgin_conv_windows_get_list();
976 for (; it; it = g_list_next(it)) {
977 PidginConvWindow *win = it->data;
978 scrncap_convwin_uninit(win);
981 return TRUE;
984 PURPLE_PLUGIN_INIT(screencap, plugin_query, plugin_load, plugin_unload);