fe5717d677a639cad25c924ba2216d40f6b38065
[claws.git] / src / gtk / foldersort.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2004-2018 the Claws Mail Team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #include "defs.h"
21
22 #include <glib.h>
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
25
26 #include "foldersort.h"
27 #include "inc.h"
28 #include "utils.h"
29
30 enum {
31         FOLDERSORT_COL_NAME,
32         FOLDERSORT_COL_PTR,
33         N_FOLDERSORT_COLS
34 };
35
36 typedef struct _FolderSortDialog FolderSortDialog;
37
38 struct _FolderSortDialog
39 {
40         GtkWidget *window;
41         GtkWidget *moveup_btn;
42         GtkWidget *movedown_btn;
43         GtkWidget *folderlist;
44         gint rows;
45 };
46
47 static void destroy_dialog(FolderSortDialog *dialog)
48 {
49         inc_unlock();
50         gtk_widget_destroy(dialog->window);
51
52         g_free(dialog);
53 }
54
55 static void ok_clicked(GtkWidget *widget, FolderSortDialog *dialog)
56 {
57         Folder *folder;
58         GtkTreeModel *model;
59         GtkTreeIter iter;
60         int i;
61
62         model = gtk_tree_view_get_model(GTK_TREE_VIEW(dialog->folderlist));
63
64         for (i = 0; i < dialog->rows; i++) {
65                 if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, i))
66                         continue;
67
68                 gtk_tree_model_get(model, &iter, FOLDERSORT_COL_PTR, &folder, -1);
69
70                 folder_set_sort(folder, dialog->rows - i);
71         }
72
73         destroy_dialog(dialog);
74 }
75
76 static void cancel_clicked(GtkWidget *widget, FolderSortDialog *dialog)
77 {
78         destroy_dialog(dialog);
79 }
80
81 static void set_selected(FolderSortDialog *dialog)
82 {
83         GtkTreeSelection *sel;
84         GtkTreeModel *model;
85         GtkTreeIter iter;
86         GtkTreePath *path;
87         gint *indices;
88         gint selected;
89
90         /* Get row number of the selected row */
91         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->folderlist));
92         if (!gtk_tree_selection_get_selected(sel, &model, &iter))
93                 return;
94         path = gtk_tree_model_get_path(model, &iter);
95         indices = gtk_tree_path_get_indices(path);
96         selected = indices[0];
97         gtk_tree_path_free(path);
98
99         if (selected >= 0) {
100                 gtk_widget_set_sensitive(dialog->moveup_btn, selected > 0);
101                 gtk_widget_set_sensitive(dialog->movedown_btn, selected < (dialog->rows - 1));
102         } else {
103                 gtk_widget_set_sensitive(dialog->moveup_btn, FALSE);
104                 gtk_widget_set_sensitive(dialog->movedown_btn, FALSE);
105         }
106 }
107
108 static void moveup_clicked(GtkWidget *widget, FolderSortDialog *dialog)
109 {
110         GtkTreeSelection *sel;
111         GtkTreeModel *model;
112         GtkTreeIter iter, previter;
113 #if !GTK_CHECK_VERSION(3, 0, 0)
114         GtkTreePath *path;
115 #endif
116
117         /* Get currently selected iter */
118         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->folderlist));
119         if (!gtk_tree_selection_get_selected(sel, &model, &iter))
120                 return;
121
122         /* Now get the iter above it, if any */
123 #if GTK_CHECK_VERSION(3, 0, 0)
124         previter = iter;
125         if (!gtk_tree_model_iter_previous(model, &previter)) {
126                 /* No previous iter, are we already on top? */
127                 return;
128         }
129 #else
130         /* GTK+2 does not have gtk_tree_model_iter_previous(), so
131          * we have to get through GtkPath */
132         path = gtk_tree_model_get_path(model, &iter);
133         if (!gtk_tree_path_prev(path)) {
134                 /* No previous path, are we already on top? */
135                 gtk_tree_path_free(path);
136                 return;
137         }
138
139         if (!gtk_tree_model_get_iter(model, &previter, path)) {
140                 /* Eh? */
141                 gtk_tree_path_free(path);
142                 return;
143         }
144
145         gtk_tree_path_free(path);
146 #endif
147
148         gtk_list_store_move_before(GTK_LIST_STORE(model), &iter, &previter);
149
150         set_selected(dialog);
151 }
152
153 static void movedown_clicked(GtkWidget *widget, FolderSortDialog *dialog)
154 {
155         GtkTreeSelection *sel;
156         GtkTreeModel *model;
157         GtkTreeIter iter, nextiter;
158
159         /* Get currently selected iter */
160         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->folderlist));
161         if (!gtk_tree_selection_get_selected(sel, &model, &iter))
162                 return;
163
164         /* Now get the iter above it, if any */
165         nextiter = iter;
166         if (!gtk_tree_model_iter_next(model, &nextiter)) {
167                 /* No next iter, are we already on the bottom? */
168                 return;
169         }
170
171         gtk_list_store_move_after(GTK_LIST_STORE(model), &iter, &nextiter);
172
173         set_selected(dialog);
174 }
175
176 static void folderlist_cursor_changed_cb(GtkTreeView *view, gpointer user_data)
177 {
178         FolderSortDialog *dialog = (FolderSortDialog *)user_data;
179         set_selected(dialog);
180 }
181
182 static void folderlist_row_inserted_cb(GtkTreeModel *model,
183                 GtkTreePath *path, GtkTreeIter *iter,
184                 gpointer user_data)
185 {
186         FolderSortDialog *dialog = (FolderSortDialog *)user_data;
187         GtkTreeSelection *sel;
188
189         /* The only time "row-inserted" signal should be fired is when
190          * a row is reordered using drag&drop. So we want to select
191          * the recently moved row, and call set_selected(), to update
192          * the up/down button sensitivity. */
193         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->folderlist));
194         gtk_tree_selection_select_iter(sel, iter);
195
196         set_selected(dialog);
197 }
198
199 static gint delete_event(GtkWidget *widget, GdkEventAny *event, FolderSortDialog *dialog)
200 {
201         destroy_dialog(dialog);
202         return TRUE;
203 }
204
205 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, FolderSortDialog *dialog)
206 {
207         if (event && event->keyval == GDK_KEY_Escape)
208                 destroy_dialog(dialog);
209         return FALSE;
210 }
211
212 void foldersort_open()
213 {
214         FolderSortDialog *dialog = g_new0(FolderSortDialog, 1);
215         GList *flist;
216
217         GtkWidget *window;
218         GtkWidget *vbox;
219         GtkWidget *vbox1;
220         GtkWidget *label1;
221         GtkWidget *hbox;
222         GtkWidget *hbox2;
223         GtkWidget *ok_btn;
224         GtkWidget *cancel_btn;
225         GtkWidget *confirm_area;
226         GtkWidget *moveup_btn;
227         GtkWidget *movedown_btn;
228         GtkWidget *btn_vbox;
229         GtkWidget *scrolledwindow1;
230         GtkWidget *folderlist;
231         GtkTreeViewColumn *col;
232         GtkListStore *store;
233         GtkCellRenderer *rdr;
234         GtkTreeSelection *selector;
235         GtkTreeIter iter;
236
237         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "foldersort");
238         g_object_set_data(G_OBJECT(window), "window", window);
239         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
240         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
241         gtk_window_set_title(GTK_WINDOW(window), _("Set mailbox order"));
242         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
243         gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
244         g_signal_connect(G_OBJECT(window), "delete_event",
245                          G_CALLBACK(delete_event), dialog);
246         g_signal_connect(G_OBJECT(window), "key_press_event",
247                          G_CALLBACK(key_pressed), dialog);
248
249         vbox = gtk_vbox_new(FALSE, 6);
250         gtk_widget_show(vbox);
251         gtk_container_add(GTK_CONTAINER(window), vbox);
252
253         gtkut_stock_button_set_create(&confirm_area, &cancel_btn, GTK_STOCK_CANCEL,
254                                       &ok_btn, GTK_STOCK_OK,
255                                       NULL, NULL);
256         gtk_widget_show(confirm_area);
257         gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
258         gtk_widget_grab_focus(ok_btn);
259
260         g_signal_connect(G_OBJECT(ok_btn), "clicked",
261                          G_CALLBACK(ok_clicked), dialog);
262         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
263                          G_CALLBACK(cancel_clicked), dialog);
264
265         vbox1 = gtk_vbox_new(FALSE, 8);
266         gtk_widget_show(vbox1);
267         gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0);
268         gtk_container_set_border_width(GTK_CONTAINER(vbox1), 2);
269
270         hbox = gtk_hbox_new(FALSE, 8);
271         gtk_widget_show(hbox);
272         gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0);
273
274         label1 = gtk_label_new(_
275                 ("Move mailboxes up or down to change the sort order "
276                  "in the Folder list."));
277         gtk_widget_show(label1);
278         gtk_widget_set_size_request(GTK_WIDGET(label1), 392, -1);
279         gtk_label_set_line_wrap(GTK_LABEL(label1), TRUE);
280         gtk_box_pack_start(GTK_BOX(hbox), label1, FALSE, FALSE, 0);
281
282         hbox2 = gtk_hbox_new(FALSE, 8);
283         gtk_widget_show(hbox2);
284         gtk_box_pack_start(GTK_BOX(vbox1), hbox2, TRUE, TRUE, 0);
285
286         scrolledwindow1 = gtk_scrolled_window_new(NULL, NULL);
287         gtk_widget_show(scrolledwindow1);
288         gtk_widget_set_size_request(scrolledwindow1, -1, 150);
289         gtk_box_pack_start(GTK_BOX(hbox2), scrolledwindow1,
290                            TRUE, TRUE, 0);
291         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolledwindow1),
292                                        GTK_POLICY_AUTOMATIC,
293                                        GTK_POLICY_AUTOMATIC);
294
295         /* Create the list store */
296         store = gtk_list_store_new(N_FOLDERSORT_COLS,
297                         G_TYPE_STRING, G_TYPE_POINTER, -1);
298
299         /* Create the view widget */
300         folderlist = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
301         g_object_unref(store);
302         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(folderlist), TRUE);
303         gtk_tree_view_set_reorderable(GTK_TREE_VIEW(folderlist), TRUE);
304         selector = gtk_tree_view_get_selection(GTK_TREE_VIEW(folderlist));
305         gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
306
307         /* The only column for the view widget */
308         rdr = gtk_cell_renderer_text_new();
309         col = gtk_tree_view_column_new_with_attributes(_("Mailboxes"),
310                         rdr, "markup", FOLDERSORT_COL_NAME, NULL);
311         gtk_tree_view_column_set_min_width(col, 80);
312         gtk_tree_view_append_column(GTK_TREE_VIEW(folderlist), col);
313
314         gtk_widget_show(folderlist);
315         gtk_container_add(GTK_CONTAINER(scrolledwindow1), folderlist);
316
317         btn_vbox = gtk_vbox_new(FALSE, 8);
318         gtk_widget_show(btn_vbox);
319         gtk_box_pack_start(GTK_BOX(hbox2), btn_vbox, FALSE, FALSE, 0);
320
321         moveup_btn = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
322         gtk_widget_show(moveup_btn);
323         gtk_box_pack_start(GTK_BOX(btn_vbox), moveup_btn, FALSE, FALSE, 0);
324
325         movedown_btn =  gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
326         gtk_widget_show(movedown_btn);
327         gtk_box_pack_start(GTK_BOX(btn_vbox), movedown_btn, FALSE, FALSE, 0);
328
329         dialog->window = window;
330         dialog->moveup_btn = moveup_btn;
331         dialog->movedown_btn = movedown_btn;
332         dialog->folderlist = folderlist;
333
334         gtk_widget_show(window);
335         gtk_widget_set_sensitive(moveup_btn, FALSE);
336         gtk_widget_set_sensitive(movedown_btn, FALSE);
337
338         /* Connect up the signals for the up/down buttons */
339         g_signal_connect(G_OBJECT(moveup_btn), "clicked",
340                          G_CALLBACK(moveup_clicked), dialog);
341         g_signal_connect(G_OBJECT(movedown_btn), "clicked",
342                          G_CALLBACK(movedown_clicked), dialog);
343
344 //      g_signal_connect(G_OBJECT(folderlist), "select-row",
345 //                       G_CALLBACK(row_selected), dialog);
346 //      g_signal_connect(G_OBJECT(folderlist), "unselect-row",
347 //                       G_CALLBACK(row_unselected), dialog);
348 //      g_signal_connect(G_OBJECT(folderlist), "row-move",
349 //                       G_CALLBACK(row_moved), dialog);
350
351         /* Populate the list with mailboxes */
352         dialog->rows = 0;
353         for (flist = folder_get_list(); flist != NULL; flist = g_list_next(flist)) {
354                 Folder *folder = flist->data;
355
356                 gtk_list_store_append(store, &iter);
357                 gtk_list_store_set(store, &iter,
358                                 FOLDERSORT_COL_NAME, folder->name,
359                                 FOLDERSORT_COL_PTR, folder,
360                                 -1);
361                 dialog->rows++;
362         }
363
364         /* Connect up the signals for the folderlist */
365         g_signal_connect(G_OBJECT(folderlist), "cursor-changed",
366                         G_CALLBACK(folderlist_cursor_changed_cb), dialog);
367         g_signal_connect(G_OBJECT(store), "row-inserted",
368                         G_CALLBACK(folderlist_row_inserted_cb), dialog);
369
370         /* Select the first row and adjust the sensitivity of
371          * the up/down buttons */
372         if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
373                 gtk_tree_selection_select_iter(selector, &iter);
374                 set_selected(dialog);
375         }
376
377         inc_lock();
378 }