Logo Search packages:      
Sourcecode: paco version File versions  Download package

maintreeview.cc

//=======================================================================
// maintreeview.cc
//-----------------------------------------------------------------------
// This file is part of the package paco
// Copyright (C) 2004-2009 David Rosal
// For more information visit http://paco.sourceforge.net
//=======================================================================

#include "config.h"
#include "gconfig.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 namespace sigc;
using namespace Gpaco;


MainTreeView::MainTreeView()
:
      TreeView(),
      mPkgSet(),
      mColumns(),
      mpModel(Gtk::ListStore::create(mColumns)),
      mpActionFiles(Gtk::Action::create
            ("Files", Gtk::Stock::DIRECTORY, "View _files")),
      mpActionInfo(Gtk::Action::create
            ("Info", Gtk::Stock::INFO)),
      mpActionRemove(Gtk::Action::create
            ("Remove", Gtk::Stock::DELETE, "_Remove...")),
      mpActionPackage(Gtk::Action::create
            ("Package", Gtk::Stock::EXECUTE, "Create _package...")),
      mpActionUnlog(Gtk::Action::create
            ("Unlog", Gtk::Stock::REMOVE, "Remove from _database..."))
{
      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
      mpActionGroup->add(mpActionFiles, bind<int>
            (mem_fun(*this, &MainTreeView::onPkgWindow), TAB_FILES));
      mpActionGroup->add(mpActionInfo, bind<int>
            (mem_fun(*this, &MainTreeView::onPkgWindow), TAB_INFO));
      mpActionGroup->add(mpActionRemove, bind<int>
            (mem_fun(*this, &MainTreeView::onPkgWindow), TAB_REMOVE));
      mpActionGroup->add(mpActionPackage, bind<int>
            (mem_fun(*this, &MainTreeView::onPkgWindow), TAB_PACKAGE));
      mpActionGroup->add(mpActionUnlog, mem_fun(*this, &MainTreeView::onUnlog));

      mpUIManager->insert_action_group(mpActionGroup);

      mpUIManager->add_ui_from_string(
            "<ui>"
            "     <popup name='PopupMenu'>"
            "           <menuitem action='Files'/>"
            "           <menuitem action='Info'/>"
            "           <menuitem action='Remove'/>"
            "           <menuitem action='Package'/>"
            "           <separator/>"
            "           <menuitem action='Unlog'/>"
            "     </popup>"
            "</ui>");
      
      mpMenu = dynamic_cast<Gtk::Menu*>(mpUIManager->get_widget("/PopupMenu"));
      mpMenu->signal_event().connect(mem_fun(*this, &MainTreeView::onPopupMenu));

      Glib::signal_timeout().connect(mem_fun(*this, &MainTreeView::rewriteRows), 100);
}


// [virtual]
MainTreeView::~MainTreeView()
{ }


//
// Rewrite all the rows. Used only from Preferences::ok() to reflect
// any change in the "show hour in date" option.
//
void MainTreeView::refresh()
{
      mpModel->foreach(mem_fun(*this, &MainTreeView::rowChanged));
}


void MainTreeView::onUpdateDataBase()
{
      g_assert(GConfig::logdirWritable());

      if (mPkgSet.empty())
            return;

      Lock lock;

      gpMainWindow->progressBar().show();
      gpMainWindow->label().set_text("Updating the database");

      // Add newly logged packages

      Glib::Dir dir(GConfig::logdir());
      for (Glib::Dir::iterator d = dir.begin(); d != dir.end(); ++d) {
            g_return_if_fail(gpMainWindow->is_visible());
            if (!mPkgSet.hasPkg(*d)) {
                  try {
                        Pkg* pkg = new Pkg(*d);
                        mPkgSet += pkg;
                        *this += pkg;
                  }
                  catch (...) { }
            }
      }

      // Delete Unlogged Packages

      for (PkgSet::iterator p = mPkgSet.begin(); p != mPkgSet.end(); ++p) {
            g_return_if_fail(gpMainWindow->is_visible());
            if (access((*p)->log().c_str(), F_OK) < 0) {
                  (*p)->deleteWindow();
                  *this -= *p;
                  mPkgSet -= *p;
            }
      }

      // Update the packages in the main window

      float cnt = 0;

      for (PkgSet::iterator p = mPkgSet.begin(); p != mPkgSet.end(); ++p) {
            g_return_if_fail(gpMainWindow->is_visible());
            (*p)->update();
            gpMainWindow->progressBar().set_fraction(++cnt / mPkgSet.size());
            refreshMainLoop();
      }

      gpMainWindow->progressBar().hide();
      GConfig::touchStamp();
}


//---------//
// private //
//---------//


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()
{
      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);
}


//
// Unlog the selected packages
//
void MainTreeView::onUnlog()
{
      g_return_if_fail(GConfig::logdirWritable());
      
      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())) {
                  (*p)->deleteWindow();
                  *this -= *p;
                  mPkgSet -= *p;
            }
            else {
                  errorDialog(NULL, "unlink(" + (*p)->log() + "): " + Glib::strerror(errno));
                  break;
            }
      }
}


//
// Get the curently selected packages
//
vector<Pkg*> MainTreeView::getSelectedPkgs()
{
      vector<Gtk::TreeModel::Path> rows = mpSelection->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;
}


bool MainTreeView::onPopupMenu(GdkEvent*)
{
      g_assert(countSelected() > 0);

      bool vis = mpSelection->count_selected_rows() == 1;

      mpActionFiles->set_visible(vis);
      mpActionInfo->set_visible(vis);
      mpActionRemove->set_visible(vis);
      mpActionRemove->set_sensitive(GConfig::logdirWritable());
      mpActionPackage->set_visible(vis);
      mpActionUnlog->set_sensitive(GConfig::logdirWritable());

      return false;
}


// [virtual]
bool MainTreeView::on_button_press_event(GdkEventButton* e)
{
      Gtk::TreeModel::Path path;
      Gtk::TreeViewColumn* col;
      int x, y;
      bool ret = true;

      if (!get_path_at_pos(e->x, e->y, path, col, x, y))
            unselectAll();

      // Double click on the left mouse button
      else if (e->type == GDK_2BUTTON_PRESS && e->button == 1)
            onPkgWindow(TAB_FILES);
      // Single click on the right mouse button
      else if (e->type == GDK_BUTTON_PRESS && e->button == 3) {
            if (!mpSelection->is_selected(path))
                  ret = Gtk::TreeView::on_button_press_event(e);
            mpMenu->popup(e->button, e->time);
      }
      else
            ret = Gtk::TreeView::on_button_press_event(e);
      
      return ret;
}


// [virtual]
bool MainTreeView::on_key_press_event(GdkEventKey* e)
{
      if (mpSelection->count_selected_rows()) {
            switch (e->keyval) {
                  case GDK_Delete:
                  case GDK_BackSpace:
                        onUnlog();
                        return true;
                  case GDK_Return:
                        onPkgWindow(TAB_FILES);
                        return true;
                  case GDK_Menu:
                        if (countSelected())
                              mpMenu->popup(0, e->time);
                        return true;
            }
      }

      return 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());
      pCellText->property_text() = Paco::toString(Paco::str2num<long>(txt));
}


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 (GConfig::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);
      pCol->add_attribute(*pCell, "value", col);

      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);
}


Generated by  Doxygen 1.6.0   Back to index