4 /* vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·: */
7 * Virtual folder plugin for claws-mail
9 * Claws Mail is Copyright (C) 1999-2012 by the Claws Mail Team
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
35 #include "mainwindow.h"
36 #include "foldersel.h"
37 #include "alertpanel.h"
39 #include "vfolder_gtk.h"
41 #include "vfolder_prop.h"
43 #define HEADERS N_("Message _Headers")
44 #define BODY N_("_Message body")
45 #define BOTH N_("_Both")
53 GtkWidget
* message_btn
;
57 static void add_current_config(VFolderItem
* item
, PropsDialog
* props
) {
59 gtk_entry_set_text(GTK_ENTRY(props
->filter
), item
->filter
);
61 gchar
* id
= folder_item_get_identifier(item
->source
);
63 gtk_entry_set_text(GTK_ENTRY(props
->source
), id
);
67 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(props
->frozen
), item
->frozen
);
68 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(props
->deep_copy
), item
->deep_copy
);
69 switch (item
->search
) {
71 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(props
->message_btn
), TRUE
);
73 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(props
->both_btn
), TRUE
);
75 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(props
->label_btn
), TRUE
);
79 static gboolean
is_source_widget(GtkWidget
* widget
) {
80 const gchar
* name
= gtk_widget_get_name(widget
);
82 return (name
&& strcmp("source", name
) == 0);
85 static void foldersel_cb(GtkWidget
*widget
, gpointer data
) {
89 GtkWidget
* entry
= GTK_WIDGET(data
);
91 item
= foldersel_folder_sel(NULL
, FOLDER_SEL_COPY
, NULL
, FALSE
);
92 if (item
&& IS_VFOLDER_FOLDER_ITEM(item
)) {
93 /* Cannot create virtual folder from virtual folder */
94 alertpanel_error(_("%s: Cannot create virtual folder from virtual folder"), item
->name
);
98 if (item
&& (item_id
= folder_item_get_identifier(item
)) != NULL
) {
99 gtk_editable_delete_text(GTK_EDITABLE(entry
), 0, -1);
100 gtk_editable_insert_text(GTK_EDITABLE(entry
),
101 item_id
, strlen(item_id
), &newpos
);
104 debug_print("Source Folder: %s\n", gtk_entry_get_text(GTK_ENTRY(entry
)));
108 static GtkWidget
* vfolder_prop_row(GtkWidget
* widget
,
109 const gchar
* label_markup
,
110 gint width
, gboolean center
) {
111 GtkWidget
* row
= gtk_hbox_new(FALSE
, 5);
112 GtkWidget
* label
= gtk_label_new(NULL
);
114 gtk_widget_set_size_request(label
, width
, -1);
115 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label
), label_markup
);
116 gtk_label_set_mnemonic_widget(GTK_LABEL(label
), widget
);
117 gtk_box_pack_start(GTK_BOX(row
), label
, FALSE
, FALSE
, 5);
118 gtk_box_pack_start(GTK_BOX(row
), widget
, TRUE
, FALSE
, 5);
120 if (is_source_widget(widget
)) {
121 GtkWidget
* btn
= gtk_button_new_from_stock(GTK_STOCK_OPEN
);
122 g_signal_connect(G_OBJECT(btn
), "clicked", G_CALLBACK(foldersel_cb
), widget
);
123 gtk_box_pack_start(GTK_BOX(row
), btn
, FALSE
, FALSE
, 5);
129 static gboolean
vfolder_set_search_type(VFolderItem
* item
, GtkWidget
* list
) {
130 GSList
*btn_list
, *btns
;
131 gboolean active
= FALSE
;
132 GtkToggleButton
* btn
= NULL
;
134 btn_list
= gtk_radio_button_get_group(GTK_RADIO_BUTTON(list
));
135 for (btns
= btn_list
; btns
&& !active
; btns
= g_slist_next(btns
)) {
136 btn
= GTK_TOGGLE_BUTTON(btns
->data
);
137 active
= gtk_toggle_button_get_active(btn
);
140 const gchar
* label
= gtk_button_get_label(GTK_BUTTON(btn
));
142 if (strcmp(BOTH
, label
) == 0) {
143 if (item
->search
!= SEARCH_BOTH
) {
144 item
->search
= SEARCH_BOTH
;
148 else if (strcmp(BODY
, label
) == 0) {
149 if (item
->search
!= SEARCH_BODY
) {
150 item
->search
= SEARCH_BODY
;
155 if (item
->search
!= SEARCH_HEADERS
) {
156 item
->search
= SEARCH_HEADERS
;
166 static void vfolder_copy_msginfo_list(gpointer data, gpointer user_data) {
167 MsgInfo* msg = (MsgInfo *) data;
169 VFolderItem* item = (VFolderItem *) user_data;
171 g_return_if_fail(msg != NULL);
172 g_return_if_fail(item != NULL);
174 new_msg = procmsg_msginfo_copy(msg);
175 item->msginfos = g_slist_prepend(item->msginfos, new_msg);
178 static gboolean
vfolder_search_headers(MsgInfo
* msg
, GPatternSpec
* pattern
) {
179 return ((msg
->cc
&& g_pattern_match_string(pattern
, msg
->cc
)) ||
180 (msg
->from
&& g_pattern_match_string(pattern
, msg
->from
)) ||
181 (msg
->fromname
&& g_pattern_match_string(pattern
, msg
->fromname
)) ||
182 (msg
->inreplyto
&& g_pattern_match_string(pattern
, msg
->inreplyto
)) ||
183 (msg
->subject
&& g_pattern_match_string(pattern
, msg
->subject
)) ||
184 (msg
->to
&& g_pattern_match_string(pattern
, msg
->to
)));
187 static gboolean
vfolder_search_body(MsgInfo
* msg
, GPatternSpec
* pattern
) {
189 gboolean found
= FALSE
;
191 body
= procmsg_get_message_file(msg
);
193 found
= g_pattern_match_string(pattern
, body
);
200 static MsgInfoList
* vfolder_filter_msgs_list(MsgInfoList
* msgs
, VFolderItem
* item
) {
201 MsgInfoList
*list
= NULL
, *tmp
;
202 GPatternSpec
* pattern
;
205 if (!item
|| item
->filter
== NULL
)
208 pattern
= g_pattern_spec_new(item
->filter
);
210 for (tmp
= msgs
; tmp
; tmp
= g_slist_next(tmp
)) {
211 msg
= (MsgInfo
*) tmp
->data
;
212 switch (item
->search
) {
214 if (vfolder_search_headers(msg
, pattern
))
215 list
= g_slist_prepend(list
, msg
);
218 if (vfolder_search_body(msg
, pattern
))
219 list
= g_slist_prepend(list
, msg
);
222 if (vfolder_search_headers(msg
, pattern
)) {
223 list
= g_slist_prepend(list
, msg
);
226 if (vfolder_search_body(msg
, pattern
))
227 list
= g_slist_prepend(list
, msg
);
232 g_pattern_spec_free(pattern
);
237 static gboolean
vfolder_create_msgs_list(VFolderItem
* item
, gboolean copy
) {
238 MsgInfoList
*msgs
= NULL
, *filtered
= NULL
;
240 GSList
* filelist
= NULL
;
242 if (item
->filter
&& item
->msg_filter_func
) {
243 item
->deep_copy
= copy
;
245 msgs
= folder_item_get_msg_list(item
->source
);
246 filtered
= item
->msg_filter_func(msgs
, item
);
248 filelist
= procmsg_get_message_file_list(filtered
);
250 gint n
= folder_item_add_msgs(FOLDER_ITEM(item
), filelist
, FALSE
);
251 FOLDER_ITEM(item
)->last_num
= n
;
252 procmsg_message_file_list_free(filelist
);
254 g_slist_free(filtered
);
261 void vfolder_set_msgs_filter(VFolderItem
* vfolder_item
) {
262 g_return_if_fail(vfolder_item
!= NULL
);
264 vfolder_item
->msg_filter_func
= vfolder_filter_msgs_list
;
267 gboolean
vfolder_create_item_dialog(FolderItem
* folder_item
) {
268 gboolean created
= FALSE
;
269 VFolderItem
* item
= NULL
;
271 g_return_val_if_fail(folder_item
!= NULL
, created
);
272 g_return_val_if_fail(IS_VFOLDER_FOLDER_ITEM(folder_item
), created
);
274 item
= VFOLDER_ITEM(folder_item
);
275 item
->msg_filter_func
= vfolder_filter_msgs_list
;
277 if (vfolder_edit_item_dialog(item
)) {
278 /* save properties */
279 if (FOLDER_ITEM_PROPS_OK
!= vfolder_folder_item_props_write(item
))
288 gboolean
vfolder_edit_item_dialog(VFolderItem
* vfolder_item
) {
290 PropsDialog
* props_dialog
;
298 gboolean frozen
, deep_copy
;
300 gchar
* old_filter
= NULL
;
302 g_return_val_if_fail(vfolder_item
!= NULL
, ok
);
304 MainWindow
*mainwin
= mainwindow_get_mainwindow();
305 props_dialog
= g_new0(PropsDialog
, 1);
306 props_dialog
->filter
= gtk_entry_new();
307 props_dialog
->frozen
= gtk_check_button_new();
308 props_dialog
->deep_copy
= gtk_check_button_new();
309 props_dialog
->source
= gtk_entry_new();
310 props_dialog
->label_btn
=
311 gtk_radio_button_new_with_mnemonic(NULL
, HEADERS
);
312 props_dialog
->message_btn
=
313 gtk_radio_button_new_with_mnemonic_from_widget(
314 GTK_RADIO_BUTTON(props_dialog
->label_btn
), BODY
);
315 props_dialog
->both_btn
=
316 gtk_radio_button_new_with_mnemonic_from_widget(
317 GTK_RADIO_BUTTON(props_dialog
->label_btn
), BOTH
);
318 gtk_widget_set_name(props_dialog
->source
, "source");
319 add_current_config(vfolder_item
, props_dialog
);
321 dialog
= gtk_dialog_new_with_buttons(
322 N_("Edit VFolder Properties"),
323 GTK_WINDOW(mainwin
->window
),
324 GTK_DIALOG_DESTROY_WITH_PARENT
,
325 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
,
326 GTK_STOCK_CANCEL
, GTK_RESPONSE_REJECT
,
328 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_REJECT
);
329 content
= gtk_dialog_get_content_area(GTK_DIALOG(dialog
));
331 GtkWidget
* vbox
= gtk_vbox_new(FALSE
, 5);
333 row
= vfolder_prop_row(props_dialog
->source
, N_("_Source folder"), 110, FALSE
);
334 gtk_box_pack_start(GTK_BOX(vbox
), row
, FALSE
, FALSE
, 5);
336 GtkWidget
* frame1
= gtk_frame_new(_("Message filter"));
337 GtkWidget
* vbox1
= gtk_vbox_new(TRUE
, 2);
338 gtk_container_add(GTK_CONTAINER(frame1
), vbox1
);
340 row
= vfolder_prop_row(props_dialog
->filter
, N_("_Filter"), 110, FALSE
);
341 gtk_box_pack_start(GTK_BOX(vbox1
), row
, FALSE
, FALSE
, 5);
343 box
= gtk_hbox_new(TRUE
, 2);
344 gtk_box_pack_start(GTK_BOX(box
), props_dialog
->label_btn
, TRUE
, TRUE
, 2);
345 gtk_box_pack_start(GTK_BOX(box
), props_dialog
->message_btn
, TRUE
, TRUE
, 2);
346 gtk_box_pack_start(GTK_BOX(box
), props_dialog
->both_btn
, TRUE
, TRUE
, 2);
347 gtk_box_pack_start(GTK_BOX(vbox1
), box
, FALSE
, FALSE
, 5);
349 gtk_box_pack_start(GTK_BOX(vbox
), frame1
, FALSE
, FALSE
, 5);
351 row
= vfolder_prop_row(props_dialog
->frozen
, N_("F_reeze content"), 110, TRUE
);
352 gtk_box_pack_start(GTK_BOX(vbox
), row
, FALSE
, FALSE
, 5);
354 row
= vfolder_prop_row(props_dialog
->deep_copy
, N_("Co_py messages"), 110, TRUE
);
355 gtk_box_pack_start(GTK_BOX(vbox
), row
, FALSE
, FALSE
, 5);
357 name
= g_strconcat(FOLDER_ITEM(vfolder_item
)->name
, N_(": settings"), NULL
);
358 GtkWidget
* frame
= gtk_frame_new(name
);
360 gtk_container_add(GTK_CONTAINER(frame
), vbox
);
361 gtk_widget_show_all(frame
);
363 gtk_container_add(GTK_CONTAINER(content
), frame
);
365 response
= gtk_dialog_run(GTK_DIALOG(dialog
));
366 if (response
== GTK_RESPONSE_ACCEPT
) {
367 frozen
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(props_dialog
->frozen
));
368 deep_copy
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(props_dialog
->deep_copy
));
370 str
= gtk_entry_get_text(GTK_ENTRY(props_dialog
->filter
));
372 old_filter
= g_strdup(vfolder_item
->filter
);
373 if (strlen(str
) == 0) {
374 if (vfolder_item
->filter
) {
375 g_free(vfolder_item
->filter
);
376 vfolder_item
->filter
= NULL
;
381 if (!vfolder_item
->filter
|| strcmp(vfolder_item
->filter
, str
) != 0) {
382 g_free(vfolder_item
->filter
);
383 vfolder_item
->filter
= g_strdup(str
);
388 if (vfolder_set_search_type(vfolder_item
, props_dialog
->label_btn
))
391 str
= gtk_entry_get_text(GTK_ENTRY(props_dialog
->source
));
393 source
= folder_find_item_from_identifier(str
);
394 if (source
&& (source
->stype
!= F_NORMAL
&& source
->stype
!= F_INBOX
)) {
395 alertpanel_error(_("%s: Not suitable for virtual folders\n"
396 "Use only folder type: Normal or Inbox\n"), str
);
397 g_free(vfolder_item
->filter
);
398 vfolder_item
->filter
= g_strdup(old_filter
);
403 if (strlen(str
) == 0) {
404 if (vfolder_item
->source
) {
405 vfolder_item
->source
= NULL
;
406 folder_item_remove_all_msg(FOLDER_ITEM(vfolder_item
));
411 folder_item_remove_all_msg(FOLDER_ITEM(vfolder_item
));
412 gchar
* id
= (vfolder_item
->source
) ?
413 folder_item_get_identifier(vfolder_item
->source
) : NULL
;
414 if (!id
|| strcmp(id
, str
) != 0)
415 vfolder_item
->source
= source
;
416 if (vfolder_item
->source
) {
417 ok
= vfolder_create_msgs_list(vfolder_item
, deep_copy
);
419 g_free(vfolder_item
->filter
);
420 vfolder_item
->filter
= g_strdup(old_filter
);
425 g_free(vfolder_item
->filter
);
426 vfolder_item
->filter
= g_strdup(old_filter
);
433 if (vfolder_item
->frozen
!= frozen
) {
434 vfolder_item
->frozen
= frozen
;
438 if (vfolder_item
->deep_copy
!= deep_copy
) {
439 vfolder_item
->deep_copy
= deep_copy
;
446 gtk_widget_destroy(dialog
);
447 g_free(props_dialog
);