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
24 #include <gdk/gdkkeysyms.h>
27 #include "glibcompat.h"
30 #include "gtk3compat.h"
32 #include "gtkplugin.h"
34 #include "gtkwebviewtoolbar.h"
35 #include "pidginicon.h"
37 #define SCRNCAP_SHOOTING_TIMEOUT 500
38 #define SCRNCAP_DEFAULT_COLOR "#FFFF00000000"
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 ******************************************************************************/
64 scrncap_conv_set_data(PidginConversation
*gtkconv
, const gchar
*key
,
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 ******************************************************************************/
85 scrncap_perform_screenshot(void)
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
));
98 scrncap_pixbuf_darken(GdkPixbuf
*pixbuf
)
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
++)
121 scrncap_pixbuf_to_image(GdkPixbuf
*pixbuf
)
123 PurpleImage
*image
= NULL
;
126 GError
*error
= NULL
;
128 if (!gdk_pixbuf_save_to_buffer(pixbuf
, &buffer
, &count
, "png",
130 purple_debug_error("screencap", "Failed saving an image: %s",
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
);
147 /******************************************************************************
149 ******************************************************************************/
152 scrncap_drawing_area_btnpress(GtkWidget
*draw_area
, GdkEventButton
*event
,
158 draw_origin_x
= event
->x
;
159 draw_origin_y
= event
->y
;
166 scrncap_drawing_area_btnrelease(GtkWidget
*draw_area
, GdkEvent
*event
,
178 scrncap_drawing_area_motion(GtkWidget
*draw_area
, GdkEventButton
*event
,
183 int redraw_x
, redraw_y
, redraw_w
, redraw_h
;
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
);
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;
208 gtk_widget_queue_draw_area(draw_area
,
209 redraw_x
, redraw_y
, redraw_w
, redraw_h
);
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
);
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
);
239 scrncap_draw_window_close(GtkWidget
*window
, gpointer _unused
)
241 if (current_window
!= window
)
245 current_window
= NULL
;
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);
260 scrncap_draw_window_response(GtkDialog
*draw_window
, gint response_id
,
263 PidginWebView
*webview
= PIDGIN_WEBVIEW(_webview
);
264 GdkPixbuf
*result
= NULL
;
266 const gchar
*fname_prefix
;
268 static guint fname_no
= 0;
270 if (response_id
== SCRNCAP_RESPONSE_COLOR
)
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
));
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
);
296 pidgin_webview_insert_image(webview
, image
);
297 g_object_unref(image
);
301 scrncap_draw_color_selected(GtkColorButton
*button
, cairo_t
*cr
)
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
);
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
;
322 cairo_surface_t
*surface
;
324 GdkCursor
*draw_cursor
;
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
);
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
);
375 g_object_set(drawing_area
,
376 "halign", GTK_ALIGN_CENTER
,
377 "valign", GTK_ALIGN_CENTER
,
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
),
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 /******************************************************************************
407 ******************************************************************************/
410 scrncap_crop_window_close(GtkWidget
*window
, gpointer _unused
)
412 if (current_window
!= window
)
416 current_window
= NULL
;
420 scrncap_crop_window_keypress(GtkWidget
*crop_window
, GdkEventKey
*event
,
423 PidginWebView
*webview
= PIDGIN_WEBVIEW(_webview
);
424 guint key
= event
->keyval
;
426 if (key
== GDK_KEY_Escape
) {
427 gtk_widget_destroy(crop_window
);
430 if (key
== GDK_KEY_Return
) {
431 GdkPixbuf
*screenshot
, *subscreen
, *result
;
433 screenshot
= g_object_get_data(G_OBJECT(crop_window
),
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
);
451 scrncap_crop_window_focusout(GtkWidget
*window
, GdkEventFocus
*event
,
454 gtk_widget_destroy(window
);
459 scrncap_crop_window_btnpress(GtkWidget
*window
, GdkEventButton
*event
,
466 g_return_val_if_fail(!crop_active
, TRUE
);
468 hint_box
= g_object_get_data(G_OBJECT(window
), "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
;
489 scrncap_crop_window_btnrelease(GtkWidget
*window
, GdkEvent
*event
,
498 scrncap_crop_window_motion(GtkWidget
*window
, GdkEventButton
*event
,
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
);
529 scrncap_crop_window_realize(GtkWidget
*crop_window
, gpointer _unused
)
531 GdkWindow
*gdkwindow
;
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
);
548 scrncap_do_screenshot_cb(gpointer _webview
)
550 PidginWebView
*webview
= PIDGIN_WEBVIEW(_webview
);
551 GtkWindow
*crop_window
;
552 GdkPixbuf
*screenshot
, *screenshot_d
;
555 GtkImage
*image
, *selection
;
558 GtkRequisition hint_size
;
561 shooting_timeout
= 0;
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
);
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
);
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
;
639 scrncap_do_screenshot(GtkAction
*action
, PidginWebView
*webview
)
641 if (current_window
) {
642 gtk_window_present(GTK_WINDOW(current_window
));
649 shooting_timeout
= g_timeout_add(SCRNCAP_SHOOTING_TIMEOUT
,
650 scrncap_do_screenshot_cb
, webview
);
653 /******************************************************************************
654 * PidginConversation setup
655 ******************************************************************************/
658 scrncap_convwin_switch(GtkNotebook
*notebook
, GtkWidget
*page
, gint page_num
,
661 PidginConvWindow
*win
= _win
;
662 PidginConversation
*gtkconv
;
663 PidginWebView
*webview
;
664 gboolean images_supported
;
667 gtkconv
= pidgin_conv_window_get_active_gtkconv(win
);
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
);
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
);
696 scrncap_convwin_init(PidginConvWindow
*win
)
698 PidginConvWindowMenu
*menu
= win
->menu
;
700 GtkWidget
*conv_submenu
, *conv_insert_image
;
701 GtkWidget
*scrncap_btn_menu
;
703 GList
*children
, *it
;
705 action
= g_object_get_data(G_OBJECT(menu
->menubar
),
706 "insert-screenshot-action");
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
);
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
) {
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",
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
);
747 scrncap_convwin_uninit(PidginConvWindow
*win
)
749 PidginConvWindowMenu
*menu
= win
->menu
;
752 btn
= g_object_get_data(G_OBJECT(menu
->menubar
),
753 "insert-screenshot-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
);
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
);
776 scrncap_conversation_init(PidginConversation
*gtkconv
)
778 PidginWebView
*webview
;
779 PidginWebViewToolbar
*toolbar
;
781 GtkToolItem
*scrncap_btn_wide
;
782 GtkWidget
*scrncap_btn_lean
;
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
)
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
);
811 action
= g_object_get_data(G_OBJECT(ref_item
), "action");
815 if (g_strcmp0(gtk_action_get_name(action
), "InsertImage") == 0) {
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");
833 g_return_if_fail(wide_menu
);
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
;
841 action
= g_object_get_data(G_OBJECT(child
), "action");
845 if (g_strcmp0(gtk_action_get_name(action
), "InsertImage") == 0) {
850 g_list_free(wide_children
);
852 g_warn_if_fail(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
));
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
)
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 /******************************************************************************
889 ******************************************************************************/
891 static PidginPluginInfo
*
892 plugin_query(GError
**error
)
894 const gchar
* const authors
[] = {
895 "Tomasz Wasilczyk <twasilczyk@pidgin.im>",
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 "
909 "website", PURPLE_WEBSITE
,
910 "abi-version", PURPLE_ABI_VERSION
,
916 plugin_load(PurplePlugin
*plugin
, GError
**error
)
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
))
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
);
957 plugin_unload(PurplePlugin
*plugin
, GError
**error
)
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
))
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
);
984 PURPLE_PLUGIN_INIT(screencap
, plugin_query
, plugin_load
, plugin_unload
);