📄 maintreeview.cc
字号:
//=======================================================================// MainTreeView.cc//-----------------------------------------------------------------------// This file is part of the package paco// Copyright (C) 2004-2007 David Rosal <david.3r@gmail.com>// For more information visit http://paco.sourceforge.net//=======================================================================#include "config.h"#include "globals.h"#include "Lock.h"#include "Config.h"#include "PkgSet.h"#include "Pkg.h"#include "util.h"#include "MainTreeView.h"#include "MainWindow.h"#include <gtkmm/cellrendererprogress.h>#include <gtkmm/stock.h>#include <gtkmm/menu.h>#include <gtkmm/liststore.h>#include <gtkmm/uimanager.h>#include <gtkmm/actiongroup.h>#include <sstream>using Glib::ustring;using std::string;using std::vector;using std::for_each;using sigc::mem_fun;using sigc::bind;using namespace Gpaco;//--------//// public ////--------//MainTreeView::MainTreeView(): Gtk::TreeView(), mPkgSet(), mColumns(), mpModel(Gtk::ListStore::create(mColumns)), mpMenu(NULL), mpUIManager(Gtk::UIManager::create()), mpActionFiles(Gtk::Action::create("Files", Gtk::Stock::JUSTIFY_FILL, "View _files")), mpActionInfo(Gtk::Action::create("Info", Gtk::Stock::INFO, "_Information")), mpActionRemove(Gtk::Action::create("Remove", Gtk::Stock::DELETE, "_Remove...")), mpActionPackage(Gtk::Action::create("Package", Gtk::Stock::EXECUTE, "Create _package...")), mpActionUpdate(Gtk::Action::create("Update", Gtk::Stock::REFRESH, "_Update...")), mpActionUnlog(Gtk::Action::create("Unlog", Gtk::Stock::REMOVE, "Remove from _database...")){ (get_selection())->set_mode(Gtk::SELECTION_MULTIPLE); show(); // so that we can set the visibility of its children in addColumn() addColumn(mColumns.mIcon, "", &MainTreeView::iconSortFunc); addColumn(mColumns.mName, "Name", &MainTreeView::nameSortFunc); addColumn(mColumns.mSizeInst, "Size", &MainTreeView::sizeInstSortFunc, &MainTreeView::sizeCellFunc, 1.); addColumn(mColumns.mSizeMiss, "Size Miss", &MainTreeView::sizeMissSortFunc, &MainTreeView::sizeCellFunc, 1.); addProgressColumn(mColumns.mSizePercent, "Size %", &MainTreeView::sizePercentSortFunc); addColumn(mColumns.mDate, "Date", &MainTreeView::dateSortFunc, &MainTreeView::dateCellFunc); addColumn(mColumns.mFilesInst, "Files", &MainTreeView::filesInstSortFunc, NULL, 1.); addColumn(mColumns.mFilesMiss, "Files Miss", &MainTreeView::filesMissSortFunc, NULL, 1.); addProgressColumn(mColumns.mFilesPercent, "Files %", &MainTreeView::filesPercentSortFunc); addColumn(mColumns.mSummary, "Summary", &MainTreeView::summarySortFunc); // Populate the tree model for (PkgSet::iterator p = mPkgSet.begin(); p != mPkgSet.end(); ++p) *this += *p; set_model(mpModel); // Build the popup menu Glib::RefPtr<Gtk::ActionGroup> pActionGroup(Gtk::ActionGroup::create()); pActionGroup->add(mpActionFiles, bind<int> (mem_fun(*this, &MainTreeView::onPkgWindow), TAB_FILES)); pActionGroup->add(mpActionInfo, bind<int> (mem_fun(*this, &MainTreeView::onPkgWindow), TAB_INFO)); pActionGroup->add(mpActionRemove, bind<int> (mem_fun(*this, &MainTreeView::onPkgWindow), TAB_REMOVE)); pActionGroup->add(mpActionPackage, bind<int> (mem_fun(*this, &MainTreeView::onPkgWindow), TAB_PACKAGE)); pActionGroup->add(mpActionUpdate, mem_fun(*this, &MainTreeView::onUpdate)); pActionGroup->add(mpActionUnlog, mem_fun(*this, &MainTreeView::onUnlog)); mpUIManager->insert_action_group(pActionGroup); mpUIManager->add_ui_from_string( "<ui>" " <popup name='PopupMenu'>" " <menuitem action='Files'/>" " <menuitem action='Info'/>" " <menuitem action='Remove'/>" " <menuitem action='Package'/>" " <separator/>" " <menuitem action='Update'/>" " <menuitem action='Unlog'/>" " </popup>" "</ui>"); mpMenu = dynamic_cast<Gtk::Menu*>(mpUIManager->get_widget("/PopupMenu")); Glib::signal_timeout().connect(mem_fun (*this, &MainTreeView::rewriteRows), 128);}// [virtual]MainTreeView::~MainTreeView(){ }void MainTreeView::refresh(){ mpModel->foreach(mem_fun(*this, &MainTreeView::rowChanged)); for_each(mPkgSet.begin(), mPkgSet.end(), std::mem_fun(&Pkg::refreshFilesTab));}void MainTreeView::switchRules(){ set_rules_hint(!get_rules_hint()); Config::rules(get_rules_hint()); for_each(mPkgSet.begin(), mPkgSet.end(), std::mem_fun(&Pkg::switchRules));}// Add newly logged packagesvoid MainTreeView::addNewPackages(){ Lock lock; Glib::Dir dir(Config::logdir()); for (Glib::Dir::iterator d = dir.begin(); d != dir.end(); ++d) { if (!mPkgSet.hasPkg(*d)) { try { Pkg* pkg = new Pkg(*d); mPkgSet += pkg; *this += pkg; } catch (...) { } } }}void MainTreeView::onUpdateDataBase(){ g_assert(Config::logdirWritable()); updatePkgs(mPkgSet); addNewPackages();}//---------//// private ////---------//void MainTreeView::markUnloggedPackages(){ for (PkgSet::iterator p = mPkgSet.begin(); p != mPkgSet.end(); ++p) { if (access((*p)->log().c_str(), F_OK) < 0) (*p)->changed(true); }}bool MainTreeView::rewriteRow(iterator const& it){ Pkg* pkg = (*it)[mColumns.mPkg]; g_assert(pkg != NULL); if (!pkg->changed()) return false; else if (!pkg->window() && access(pkg->log().c_str(), F_OK) < 0) { *this -= pkg; mPkgSet -= pkg; return true; } else { writeRow(*pkg, it); pkg->changed(false); } return false;}bool MainTreeView::rewriteRows(){ if (!Lock::locked()) mpModel->foreach_iter(mem_fun(*this, &MainTreeView::rewriteRow)); return true;}void MainTreeView::writeRow(Pkg& pkg, iterator const& it){ (*it)[mColumns.mPkg] = &pkg; if (pkg.icon()) { (*it)[mColumns.mIcon] = pkg.icon()->scale_simple( CELL_HEIGHT, CELL_HEIGHT, Gdk::INTERP_BILINEAR); } (*it)[mColumns.mName] = pkg.name(); (*it)[mColumns.mSizeInst] = pkg.sizeInst(); (*it)[mColumns.mSizeMiss] = pkg.sizeMiss(); (*it)[mColumns.mSizePercent] = pkg.sizePercent(); (*it)[mColumns.mDate] = pkg.date(); (*it)[mColumns.mFilesInst] = pkg.filesInst(); (*it)[mColumns.mFilesMiss] = pkg.filesMiss(); (*it)[mColumns.mFilesPercent] = pkg.filesPercent(); (*it)[mColumns.mSummary] = pkg.summary();}//// Add a new package to the tree view//MainTreeView& MainTreeView::operator+=(Pkg* pkg){ g_assert(pkg != NULL); writeRow(*pkg, mpModel->append()); return *this;}//// Remove the row of a package from the view.//MainTreeView& MainTreeView::operator-=(Pkg* pkg){ g_assert(pkg != NULL); iterator it; if (getIter(*pkg, it)) mpModel->erase(it); return *this;}void MainTreeView::onPkgWindow(int tab){ vector<Pkg*> pkgs = getSelectedPkgs(); if (pkgs.size() == 1) (*(pkgs.begin()))->presentWindow(tab);}void MainTreeView::onUpdate(){ vector<Pkg*> pkgs = getSelectedPkgs(); updatePkgs(pkgs);}void MainTreeView::updatePkgs(vector<Pkg*>& pkgs){ if (pkgs.empty()) return; Lock lock; gpMainWindow->progressBar().show(); // Update the packages in the main window float cnt = 0; for (PkgSet::iterator p = pkgs.begin(); p != pkgs.end(); ++p) { if (!gpMainWindow->is_visible()) return; (*p)->update(); gpMainWindow->progressBar().set_fraction(++cnt / pkgs.size()); refreshMainLoop(); } gpMainWindow->progressBar().hide();}//// Unlog the selected packages//void MainTreeView::onUnlog(){ if (Lock::locked() || !Config::logdirWritable()) return; Lock lock; vector<Pkg*> pkgs = getSelectedPkgs(); g_assert(pkgs.empty() == false); vector<Pkg*>::iterator p = pkgs.begin(); std::ostringstream msg; if (pkgs.size() == 1) msg << "Remove " << (*p)->name() << " from the database ?\n"; else { msg << "Remove the following packages from the database ?\n\n"; guint cnt = 0; for ( ; p != pkgs.end() && cnt < 16; ++p, ++cnt) msg << "\t" << (*p)->name() << "\n"; if (pkgs.size() > cnt) msg << "\t... (" << pkgs.size() - cnt << " more packages)\n"; } if (!questionDialog(NULL, msg.str())) return; for (p = pkgs.begin(); p != pkgs.end(); ++p) { if (access((*p)->log().c_str(), F_OK) || !unlink((*p)->log().c_str())) { if ((*p)->window()) (*p)->deleteWindow(); (*p)->changed(true); } else { errorDialog(NULL, "unlink(" + (*p)->log() + "): " + Glib::strerror(errno)); break; } }}//// Get the curently selected packages//vector<Pkg*> MainTreeView::getSelectedPkgs(){ vector<Gtk::TreeModel::Path> rows = (get_selection())->get_selected_rows(); vector<Gtk::TreeModel::Path>::iterator p; vector<Pkg*> pkgs; for (p = rows.begin(); p != rows.end(); ++p) { Pkg* pkg = (*(mpModel->get_iter(*p)))[mColumns.mPkg]; g_assert(pkg != NULL); pkgs.push_back(pkg); } return pkgs;}//// Try to get the iter of a package. Return true on success.//bool MainTreeView::getIter(Pkg const& pkg, iterator& it){ Gtk::TreeModel::Children child = mpModel->children(); for (it = child.begin(); it != child.end(); ++it) { if (&pkg == (*it)[mColumns.mPkg]) return true; } return false;}void MainTreeView::popupMenu(int rows, GdkEventButton* e){ mpActionFiles->set_sensitive(rows == 1); mpActionInfo->set_sensitive(rows == 1); mpActionRemove->set_sensitive(rows == 1 && Config::logdirWritable()); mpActionPackage->set_sensitive(rows == 1); mpActionUpdate->set_sensitive(!Lock::locked() && Config::logdirWritable()); mpActionUnlog->set_sensitive(!Lock::locked() && Config::logdirWritable()); mpMenu->popup(e->button, e->time);}// [virtual]bool MainTreeView::on_button_press_event(GdkEventButton* e){ bool ret = true; // Double click on the left mouse button if (e->type == GDK_2BUTTON_PRESS && e->button == 1) { ret = Gtk::TreeView::on_button_press_event(e); onPkgWindow(TAB_FILES); } // Single click on the right mouse button else if (e->type == GDK_BUTTON_PRESS && e->button == 3) { int rows = (get_selection())->count_selected_rows(); if (!rows) { ret = Gtk::TreeView::on_button_press_event(e); rows = 1; } popupMenu(rows, e); } else ret = Gtk::TreeView::on_button_press_event(e); return ret;}// [virtual]bool MainTreeView::on_key_press_event(GdkEventKey* e){ bool done = false; int rows = (get_selection())->count_selected_rows(); if (rows) { switch (e->keyval) { case GDK_Delete: onUnlog(); done = true; break; case GDK_Return: onPkgWindow(TAB_FILES); done = true; } } return done ? true : Gtk::TreeView::on_key_press_event(e);}void MainTreeView::sizeCellFunc(Gtk::CellRenderer* pCell, iterator const&){ Gtk::CellRendererText* pCellText = dynamic_cast<Gtk::CellRendererText*>(pCell); ustring txt(pCellText->property_text()); ustring newText(Paco::sizeStr(Config::sizeUnit(), Paco::str2num<long>(txt))); switch (Config::sizeUnit()) { case Paco::KILOBYTE: newText += 'k'; break; case Paco::MEGABYTE: newText += 'M'; } pCellText->property_text() = newText;}void MainTreeView::dateCellFunc(Gtk::CellRenderer* pCell, iterator const& it){ int date = (*it)[mColumns.mDate]; struct tm* t = NULL; time_t __time = (time_t)date; char txt[64] = " "; if (date && (t = localtime(&__time))) { ustring fmt = "%d-%b-%Y"; if (Config::hour()) fmt += " %H:%M"; strftime(txt, sizeof(txt) - 1, fmt.c_str(), t); } (dynamic_cast<Gtk::CellRendererText*>(pCell))->property_text() = txt;}void MainTreeView::addProgressColumn( Gtk::TreeModelColumn<float> const& col, ustring const& title, SortFunc sortFunc) // = NULL{ Gtk::CellRendererProgress* pCell = new Gtk::CellRendererProgress; int id = append_column(title, *pCell) - 1; g_assert(id >= 0); Gtk::TreeViewColumn* pCol = get_column(id); g_assert(pCol != NULL); pCell->set_fixed_size(PROGRESS_CELL_WIDTH, CELL_HEIGHT); pCol->set_sort_column(id); pCol->set_visible(false); pCol->set_resizable(true); #if GLIBMM_PROPERTIES_ENABLED pCol->add_attribute(pCell->property_value(), col);#else pCol->add_attribute(*pCell, "value", col);#endif if (sortFunc) mpModel->set_sort_func(id, mem_fun(*this, sortFunc));}template <typename T>void MainTreeView::addColumn( Gtk::TreeModelColumn<T> const& col, ustring const& title, SortFunc sortFunc, CellFunc cellFunc, gfloat xalign /* = 0. */){ int id = append_column(title, col) - 1; g_assert(id >= 0); Gtk::TreeViewColumn* pCol = get_column(id); g_assert(pCol != NULL); pCol->set_sort_column(id); pCol->set_visible(false); pCol->set_resizable(id != COL_ICON); pCol->set_alignment(xalign); Gtk::CellRenderer* pCell = pCol->get_first_cell_renderer(); pCell->set_fixed_size(-1, CELL_HEIGHT); pCell->property_xalign() = xalign; if (sortFunc) mpModel->set_sort_func(id, mem_fun(*this, sortFunc)); if (cellFunc) pCol->set_cell_data_func(*pCell, mem_fun(*this, cellFunc));}int MainTreeView::summarySortFunc(iterator const& a, iterator const& b){ ustring aStr = (*a)[mColumns.mSummary]; ustring bStr = (*b)[mColumns.mSummary]; return aStr.lowercase() < bStr.lowercase() ? -1 : 1;}int MainTreeView::dateSortFunc(iterator const& a, iterator const& b){ return (*a)[mColumns.mDate] < (*b)[mColumns.mDate] ? -1 : 1;}int MainTreeView::filesInstSortFunc(iterator const& a, iterator const& b){ return (*a)[mColumns.mFilesInst] < (*b)[mColumns.mFilesInst] ? -1 : 1;}int MainTreeView::filesMissSortFunc(iterator const& a, iterator const& b){ return (*a)[mColumns.mFilesMiss] < (*b)[mColumns.mFilesMiss] ? -1 : 1;}int MainTreeView::sizeInstSortFunc(iterator const& a, iterator const& b){ return (*a)[mColumns.mSizeInst] < (*b)[mColumns.mSizeInst] ? -1 : 1;}int MainTreeView::sizeMissSortFunc(iterator const& a, iterator const& b){ return (*a)[mColumns.mSizeMiss] < (*b)[mColumns.mSizeMiss] ? -1 : 1;}int MainTreeView::sizePercentSortFunc(iterator const& a, iterator const& b){ return (*a)[mColumns.mSizePercent] < (*b)[mColumns.mSizePercent] ? -1 : 1;}int MainTreeView::filesPercentSortFunc(iterator const& a, iterator const& b){ return (*a)[mColumns.mFilesPercent] < (*b)[mColumns.mFilesPercent] ? -1 : 1;}int MainTreeView::nameSortFunc(iterator const& a, iterator const& b){ ustring aStr = (*a)[mColumns.mName]; ustring bStr = (*b)[mColumns.mName]; return aStr.lowercase() < bStr.lowercase() ? -1 : 1;}int MainTreeView::iconSortFunc(iterator const& a, iterator const&){ Glib::RefPtr<Gdk::Pixbuf> pixbuf = (*a)[mColumns.mIcon]; return pixbuf ? -1 : 1;}bool MainTreeView::rowChanged( Gtk::TreeModel::Path const& path, iterator const& it){ mpModel->row_changed(path, it); return false;}//----------------------------------//// class MainTreeView::ModelColumns ////----------------------------------//MainTreeView::ModelColumns::ModelColumns(){ add(mPkg), add(mIcon); add(mName); add(mSizeInst); add(mSizeMiss); add(mSizePercent); add(mDate); add(mFilesInst); add(mFilesMiss); add(mFilesPercent); add(mSummary);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -