4
* Copyright (C) 2003 Enrico Zini <enrico@debian.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
#pragma implementation
23
#include "FolderList.h"
27
#include <sigc++/sigc++.h>
28
#include <gtkmm/treemodel.h>
29
#include <gtkmm/treemodelfilter.h>
31
#include "Environment.h"
36
void FolderList::on_filter_changed()
38
filter = filterEdit.get_text();
42
void FolderList::on_document_changed()
47
void FolderList::on_add_tag(Tag tag)
49
Glib::RefPtr<Gtk::TreeSelection> sel = itemList.get_selection();
51
// Build the list of selected items
54
Gtk::TreeSelection::ListHandle_Path lhp = sel->get_selected_rows();
55
for (Gtk::TreeSelection::ListHandle_Path::const_iterator i = lhp.begin(); i != lhp.end(); i++)
57
Gtk::TreeModel::Row row = *itemListModel->get_iter(*i);
58
Glib::ustring s = row[itemListModelColumns.name];
59
selected.push_back(s);
63
TagcollChange<string, Tag> change;
64
for (vector<string>::const_iterator i = selected.begin();
65
i != selected.end(); i++)
67
TagSet ts = doc.collection().getTagsetForFolder(*i);
68
change.insert(make_pair(*i, ts + tag));
72
do_signal_request_tagcoll_change(change);
75
void FolderList::on_remove_tag(Tag tag)
77
Glib::RefPtr<Gtk::TreeSelection> sel = itemList.get_selection();
79
// Build the list of selected items
82
Gtk::TreeSelection::ListHandle_Path lhp = sel->get_selected_rows();
83
for (Gtk::TreeSelection::ListHandle_Path::const_iterator i = lhp.begin(); i != lhp.end(); i++)
85
Gtk::TreeModel::Row row = *itemListModel->get_iter(*i);
86
Glib::ustring s = row[itemListModelColumns.name];
87
selected.push_back(s);
91
TagcollChange<string, Tag> change;
92
for (vector<string>::const_iterator i = selected.begin();
93
i != selected.end(); i++)
95
TagSet ts = doc.collection().getTagsetForFolder(*i);
96
change.insert(make_pair(*i, ts - tag));
100
do_signal_request_tagcoll_change(change);
103
void FolderList::do_signal_request_tagcoll_change(TagcollChange<string, Tag> change)
105
signal_request_tagcoll_change().emit(change);
108
void FolderList::do_signal_request_tagset_merge()
110
signal_request_tagset_merge().emit();
113
void FolderList::do_signal_request_tagset_intersect()
115
signal_request_tagset_intersect().emit();
118
void FolderList::do_signal_request_item_copy()
120
signal_request_item_copy().emit();
123
void FolderList::do_signal_request_item_move()
125
signal_request_item_move().emit();
128
void FolderList::do_signal_select_tagset(TagSet tagset)
130
//debug("SRTA %.*s %.*s\n", PFSTR(item), PFSTR(tag));
131
signal_select_tagset().emit(tagset);
134
void FolderList::do_signal_select_tagset_other_panel(TagSet tagset)
136
//debug("SRTA %.*s %.*s\n", PFSTR(item), PFSTR(tag));
137
signal_select_tagset_other_panel().emit(tagset);
141
bool FolderList::on_event(GdkEvent* e)
143
if (e->type == GDK_BUTTON_PRESS && e->button.button > 1)
145
Gtk::TreeModel::Path path;
146
Gtk::TreeViewColumn* column;
148
if (folderList.get_path_at_pos(
149
(int)e->button.x, (int)e->button.y,
153
// Clicked on an item
154
debug("Cell %d, %d\n", cell_x, cell_y);
156
Gtk::TreeModel::Row row = *folderListModel->get_iter(path);
157
MailFolder folder = row[folderListModelColumns.folder];
158
verbose("Clicked on %.*s\n", PFSTR(folder.path()));
160
folderList.get_selection()->select(row);
163
Glib::ustring tag = row[itemListModelColumns.name];
164
TagSet ts = row[itemListModelColumns.tags];
166
debug("Name: %.*s, %d tags\n", PFSTR(tag), ts.size());
168
Glib::RefPtr<Gtk::TreeSelection> sel = itemList.get_selection();
169
int rows = sel->count_selected_rows();
171
itemPopup.items().clear();
175
itemPopup.items().push_back(Gtk::Menu_Helpers::MenuElem("Select this tag set",
176
sigc::bind< TagSet >(
177
sigc::mem_fun(*this, &FolderList::do_signal_select_tagset), ts)));
178
itemPopup.items().push_back(Gtk::Menu_Helpers::MenuElem("Select this tag set in the other panel",
179
sigc::bind< TagSet >(
180
sigc::mem_fun(*this, &FolderList::do_signal_select_tagset_other_panel), ts)));
184
itemPopup.items().push_back(Gtk::Menu_Helpers::MenuElem("Merge",
185
sigc::mem_fun(*this, &FolderList::do_signal_request_tagset_merge)));
186
itemPopup.items().push_back(Gtk::Menu_Helpers::MenuElem("Intersect",
187
sigc::mem_fun(*this, &FolderList::do_signal_request_tagset_intersect)));
189
itemPopup.items().push_back(Gtk::Menu_Helpers::MenuElem("Copy to other panel",
190
sigc::mem_fun(*this, &FolderList::do_signal_request_item_copy)));
191
itemPopup.items().push_back(Gtk::Menu_Helpers::MenuElem("Move to other panel",
192
sigc::mem_fun(*this, &FolderList::do_signal_request_item_move)));
194
TagMenu<TagcollDocument<string> >* addMenu = new TagMenu<TagcollDocument<string> >();
195
addMenu->set_manage();
196
addMenu->populateUnselected(doc, ts);
198
addMenu->signal_selected().connect(
199
sigc::mem_fun(*this, &FolderList::on_add_tag));
200
itemPopup.items().push_back(Gtk::Menu_Helpers::MenuElem("_Add...", *addMenu));
204
itemPopup.items().push_back(Gtk::Menu_Helpers::SeparatorElem());
205
for (TagSet::const_iterator i = ts.begin();
207
itemPopup.items().push_back(Gtk::Menu_Helpers::MenuElem("Remove " + i->fullname(),
209
sigc::mem_fun(*this, &FolderList::on_remove_tag), *i)));
212
itemPopup.popup(e->button.button, e->button.time);
214
//printf("Menu finished\n");
221
warning("itemList.get_path_at_pos failed\n");
228
void FolderList::on_row_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*)
230
if(const Gtk::TreeIter iter = folderList.get_model()->get_iter(path))
232
Gtk::TreeModel::Row row = *iter;
233
MailFolder folder = row[folderListModelColumns.folder];
234
signal_open_folder().emit(folder);
236
warning("Activated %.*s\n", PFSTR(folder.path()));
237
string cmdline = "x-terminal-emulator -e mutt -f " + folder.path();
239
warning("Running: %.*s\n", PFSTR(cmdline));
240
Environment::get().spawnCommand(cmdline);
241
} catch (SystemException& e) {
242
warning("%s: %.*s\n", e.type(), PFSTR(e.desc()));
248
bool FolderList::filter_row(const Gtk::TreeModel::const_iterator& iter)
250
// Hide empty folders
251
Gtk::TreeModel::Row row = *iter;
253
if (row[folderListModelColumns.msg_unread] > 0)
256
if (viewImportant && row[folderListModelColumns.msg_flagged] > 0)
259
if (viewRead && row[folderListModelColumns.msg_total] > 0)
267
FolderList::FolderList()
268
: viewEmpty(true), viewRead(true)
270
// Add the TreeView, inside a ScrolledWindow
271
scrolledFolderList.add(folderList);
273
// Only show the scrollbars when they are necessary:
274
scrolledFolderList.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
276
pack_start(scrolledFolderList, Gtk::PACK_EXPAND_WIDGET);
278
// Create the Tree model
279
folderListModel = Gtk::ListStore::create(folderListModelColumns);
281
filteredModel = Gtk::TreeModelFilter::create(
283
folderListModel->get_path(folderListModel->children().begin()));
284
filteredModel->set_visible_func(sigc::mem_fun(*this, &FolderList::filter_row));
286
sortedModel = Gtk::TreeModelSort::create(
289
//folderList.set_model(folderListModel);
290
//folderList.set_model(filteredModel);
291
folderList.set_model(sortedModel);
294
// Add the view columns
295
Gtk::TreeViewColumn* column;
298
count = folderList.append_column("Name", folderListModelColumns.name);
299
column = folderList.get_column(count - 1);
300
column->set_sort_column(folderListModelColumns.name);
302
count = folderList.append_column("New", folderListModelColumns.msg_new);
303
column = folderList.get_column(count - 1);
304
column->set_sort_column(folderListModelColumns.msg_new);
306
count = folderList.append_column("Unread", folderListModelColumns.msg_unread);
307
column = folderList.get_column(count - 1);
308
column->set_sort_column(folderListModelColumns.msg_unread);
310
count = folderList.append_column("Total", folderListModelColumns.msg_total);
311
column = folderList.get_column(count - 1);
312
column->set_sort_column(folderListModelColumns.msg_total);
314
count = folderList.append_column("Flagged", folderListModelColumns.msg_flagged);
315
column = folderList.get_column(count - 1);
316
column->set_sort_column(folderListModelColumns.msg_flagged);
319
folderList.get_column(0)->set_resizable(true);
320
folderList.get_column(1)->set_resizable(true);
323
Glib::RefPtr<Gtk::TreeSelection> folderListSelection = folderList.get_selection();
324
folderListSelection->set_mode(Gtk::SELECTION_SINGLE);
326
folderList.add_events(Gdk::BUTTON_PRESS_MASK);
327
folderList.signal_event().connect(sigc::mem_fun(*this, &FolderList::on_event));
328
folderList.signal_row_activated().connect(sigc::mem_fun(*this, &FolderList::on_row_activated));
334
folderList.signal_focus_in_event().connect(sigc::mem_fun(*this, &FolderList::on_focus_in));
335
filterEdit.signal_changed().connect(sigc::mem_fun(*this, &FolderList::on_filter_changed));
336
doc.signal_changed().connect(sigc::mem_fun(*this, &FolderList::on_document_changed));
340
void FolderList::consume(MailFolder& folder)
342
Gtk::TreeModel::Row row = *(folderListModel->append());
343
row[folderListModelColumns.folder] = folder;
344
row[folderListModelColumns.name] = folder.name();
346
//int msg_total, msg_unread, msg_new, msg_flagged;
347
//folder.computeStatistics(&msg_total, &msg_unread, &msg_new, &msg_flagged);
348
if (folder.changed())
350
folder.updateStatistics();
352
row[folderListModelColumns.msg_new] = folder.getMsgNew();
353
row[folderListModelColumns.msg_unread] = folder.getMsgUnread();
354
row[folderListModelColumns.msg_total] = folder.getMsgTotal();
355
row[folderListModelColumns.msg_flagged] = folder.getMsgFlagged();
359
void FolderList::clear()
361
folderListModel->clear();
364
void FolderList::do_update()
366
debug("FolderList::do_update\n");
367
for (Gtk::TreeModel::Children::const_iterator i = folderListModel->children().begin();
368
i != folderListModel->children().end(); i++)
370
Gtk::TreeModel::Row row = *i;
371
MailFolder folder = row[folderListModelColumns.folder];
372
//row[folderListModelColumns.name] = folder.name();
374
folder.updateStatistics();
376
row[folderListModelColumns.msg_new] = folder.getMsgNew();
377
row[folderListModelColumns.msg_unread] = folder.getMsgUnread();
378
row[folderListModelColumns.msg_total] = folder.getMsgTotal();
379
row[folderListModelColumns.msg_flagged] = folder.getMsgFlagged();
383
bool FolderList::do_timer_tick()
389
void FolderList::configureFilter(bool viewEmpty, bool viewRead, bool viewImportant)
391
this->viewEmpty = viewEmpty;
392
this->viewRead = viewRead;
393
this->viewImportant = viewImportant;
394
filteredModel->refilter();
399
void FolderList::on_itemList_drag_data_get(
400
const Glib::RefPtr<Gdk::DragContext>&, Gtk::SelectionData& selection_data, guint, guint)
402
std::map<std::string, TagSet > sel = getSelection();
406
for (std::map< string, TagSet >::const_iterator i = sel.begin();
411
for (TagSet::const_iterator j = i->second.begin();
412
j != i->second.end(); j++)
413
if (j == i->second.begin())
414
str += ": " + j->fullname();
416
str += ", " + j->fullname();
421
selection_data.set(selection_data.get_target(), 8, (const guchar*)str.data(), str.size());
425
void FolderList::on_itemList_drop_drag_data_received(
426
const Glib::RefPtr<Gdk::DragContext>& context, int, int, const Gtk::SelectionData& selection_data, guint, guint time)
428
if ((selection_data.get_length() >= 0) && (selection_data.get_format() == 8))
430
string type = selection_data.get_data_type();
431
warning("Type: %.*s\n", PFSTR(type));
432
Glib::ustring data((const char*)selection_data.get_data(), selection_data.get_length());
433
Tag tag = doc.vocabulary().findTag(data);
434
warning("Received: %.*s [%sfound in vocabulary]\n", PFSTR(data), tag ? "" : "not ");
436
if (type == "TAG" && tag)
438
std::map< std::string, TagSet > sel = getSelection();
440
TagcollChange<string, Tag> change;
442
for (std::map< string, TagSet >::const_iterator i = sel.begin();
444
change.insert(make_pair(i->first, i->second + tag));
446
doc.applyChange(change);
448
else if (type == "TAGCOLL")
453
context->drag_finish(false, false, time);
458
void FolderList::updateList()
460
time_t start = time(NULL);
462
std::map< std::string, TagSet > sel = getSelection();
466
OpSet<string> items = doc.collection().getUntaggedFolders();
468
Gtk::TreeModel::Row row;
469
itemListModel->clear();
470
for (OpSet<string>::const_iterator i = items.begin(); i != items.end(); i++)
472
if (filter.empty() || i->find(filter) != string::npos)
474
row = *(itemListModel->append());
475
row[itemListModelColumns.name] = *i;
476
row[itemListModelColumns.tags] = TagSet();
477
if (sel.find(*i) != sel.end())
478
itemList.get_selection()->select(row);
482
std::map< string, OpSet<Tag> > items = doc.collection().getCompanionFoldersAndTagsets(_tagset);
484
Gtk::TreeModel::Row row;
485
itemListModel->clear();
486
for (std::map< string, OpSet<Tag> >::const_iterator i = items.begin(); i != items.end(); i++)
488
if (filter.empty() || i->first.find(filter) != string::npos)
490
row = *(itemListModel->append());
491
row[itemListModelColumns.name] = i->first;
492
row[itemListModelColumns.tags] = i->second;
493
if (sel.find(i->first) != sel.end())
494
itemList.get_selection()->select(row);
499
time_t end = time(NULL);
501
fprintf(stderr, "FolderList::updateList: %d seconds\n", (end-start));
504
map< string, TagSet > FolderList::getAllFolders()
506
std::map< string, TagSet > res;
508
for (Gtk::TreeModel::Children::const_iterator i = itemListModel->children().begin();
509
i != itemListModel->children().end(); i++)
511
Gtk::TreeModel::Row row = *i;
512
Glib::ustring s = row[itemListModelColumns.name];
513
TagSet ts = row[itemListModelColumns.tags];
514
res.insert(make_pair(s, ts));
515
debug("Found: %.*s\n", PFSTR(s));
521
map< string, TagSet > FolderList::getSelection()
523
Glib::RefPtr<Gtk::TreeSelection> itemSelection = itemList.get_selection();
524
Gtk::TreeSelection::ListHandle_Path lhp = itemSelection->get_selected_rows();
525
std::map< string, TagSet > res;
527
for (Gtk::TreeSelection::ListHandle_Path::const_iterator i = lhp.begin(); i != lhp.end(); i++)
529
Gtk::TreeModel::Row row = *itemListModel->get_iter(*i);
530
Glib::ustring s = row[itemListModelColumns.name];
531
TagSet ts = row[itemListModelColumns.tags];
532
res.insert(make_pair(s, ts));
538
FolderListSelectionDataGatherer data(*this);
539
itemSelection->selected_foreach(sigc::slot(data, &FolderListSelectionDataGatherer::selected_row_callback));
541
//return data.data();
544
int FolderList::getSelectionSize()
546
Glib::RefPtr<Gtk::TreeSelection> itemSelection = itemList.get_selection();
547
return itemSelection->count_selected_rows();
550
void FolderList::do_selection_changed()
552
signal_selection_changed().emit();
555
void FolderList::do_focus_in()
557
signal_focus_in().emit();
560
bool FolderList::on_focus_in(GdkEventFocus*)
562
//debug("FOCUS IN\n");
568
// vim:set ts=4 sw=4: