/* * Copyright 2005,2006 Fabrice Colin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include "StringManip.h" #include "TimeConverter.h" #include "Url.h" #include "QueryHistory.h" #include "ResultsExporter.h" #include "ViewHistory.h" #include "config.h" #include "NLS.h" #include "PinotSettings.h" #include "PinotUtils.h" #include "ResultsTree.h" using namespace std; using namespace Glib; using namespace Gdk; using namespace Gtk; ResultsTree::ResultsTree(const ustring &queryName, Menu *pPopupMenu, GroupByMode groupMode, PinotSettings &settings) : TreeView(), m_treeName(queryName), m_pPopupMenu(pPopupMenu), m_pResultsScrolledwindow(NULL), m_settings(settings), m_pExtractScrolledwindow(NULL), m_extractTreeView(NULL), m_showExtract(true), m_groupMode(groupMode) { TreeViewColumn *pColumn = NULL; m_pResultsScrolledwindow = manage(new ScrolledWindow()); m_pExtractScrolledwindow = manage(new ScrolledWindow()); m_extractTreeView = manage(new TreeView()); // This is the actual results tree set_events(Gdk::BUTTON_PRESS_MASK); set_flags(CAN_FOCUS); set_headers_clickable(true); set_headers_visible(true); set_rules_hint(true); set_reorderable(false); set_enable_search(true); get_selection()->set_mode(SELECTION_MULTIPLE); m_pResultsScrolledwindow->set_flags(CAN_FOCUS); m_pResultsScrolledwindow->set_border_width(4); m_pResultsScrolledwindow->set_shadow_type(SHADOW_NONE); m_pResultsScrolledwindow->set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC); m_pResultsScrolledwindow->property_window_placement().set_value(CORNER_TOP_LEFT); m_pResultsScrolledwindow->add(*this); // That's for the extract view m_extractTreeView->set_events(Gdk::BUTTON_PRESS_MASK); m_extractTreeView->set_flags(CAN_FOCUS); m_extractTreeView->set_headers_clickable(false); m_extractTreeView->set_headers_visible(false); m_extractTreeView->set_rules_hint(true); m_extractTreeView->set_reorderable(false); m_extractTreeView->set_enable_search(true); m_extractTreeView->get_selection()->set_mode(SELECTION_SINGLE); m_pExtractScrolledwindow->set_flags(CAN_FOCUS); m_pExtractScrolledwindow->set_border_width(4); m_pExtractScrolledwindow->set_shadow_type(SHADOW_NONE); m_pExtractScrolledwindow->set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC); m_pExtractScrolledwindow->property_window_placement().set_value(CORNER_TOP_LEFT); m_pExtractScrolledwindow->add(*m_extractTreeView); // Associate the columns model to the results tree m_refStore = TreeStore::create(m_resultsColumns); set_model(m_refStore); if (m_groupMode != FLAT) { // The first column is for the status icons pColumn = new TreeViewColumn(""); // Pack an icon renderer for the viewed status CellRendererPixbuf *pIconRenderer = new CellRendererPixbuf(); pColumn->pack_start(*manage(pIconRenderer), false); pColumn->set_cell_data_func(*pIconRenderer, sigc::mem_fun(*this, &ResultsTree::renderViewStatus)); // Pack a second icon renderer for the indexed status pIconRenderer = new CellRendererPixbuf(); pColumn->pack_start(*manage(pIconRenderer), false); pColumn->set_cell_data_func(*pIconRenderer, sigc::mem_fun(*this, &ResultsTree::renderIndexStatus)); // And a third one for the ranking pIconRenderer = new CellRendererPixbuf(); pColumn->pack_start(*manage(pIconRenderer), false); pColumn->set_cell_data_func(*pIconRenderer, sigc::mem_fun(*this, &ResultsTree::renderRanking)); pColumn->set_sizing(TREE_VIEW_COLUMN_AUTOSIZE); append_column(*manage(pColumn)); // This is the score column pColumn = new TreeViewColumn(_("Score")); CellRendererProgress *pProgressRenderer = new CellRendererProgress(); pColumn->pack_start(*manage(pProgressRenderer)); pColumn->add_attribute(pProgressRenderer->property_text(), m_resultsColumns.m_scoreText); pColumn->add_attribute(pProgressRenderer->property_value(), m_resultsColumns.m_score); pColumn->set_sizing(TREE_VIEW_COLUMN_AUTOSIZE); pColumn->set_sort_column(m_resultsColumns.m_score); append_column(*manage(pColumn)); } // This is the title column pColumn = new TreeViewColumn(_("Title")); CellRendererText *pTextRenderer = new CellRendererText(); pColumn->pack_start(*manage(pTextRenderer)); pColumn->set_cell_data_func(*pTextRenderer, sigc::mem_fun(*this, &ResultsTree::renderTitleColumn)); pColumn->add_attribute(pTextRenderer->property_text(), m_resultsColumns.m_text); pColumn->set_resizable(true); pColumn->set_sort_column(m_resultsColumns.m_text); append_column(*manage(pColumn)); // URL pColumn = create_column(_("URL"), m_resultsColumns.m_url, false, true, m_resultsColumns.m_url); if (pColumn != NULL) { pColumn->set_sizing(TREE_VIEW_COLUMN_AUTOSIZE); append_column(*manage(pColumn)); } // The last column is for the timestamp pColumn = create_column(_("Date"), m_resultsColumns.m_timestamp, false, true, m_resultsColumns.m_timestampTime); if (pColumn != NULL) { pColumn->set_sizing(TREE_VIEW_COLUMN_AUTOSIZE); append_column(*manage(pColumn)); } // Connect the signals signal_button_press_event().connect_notify( sigc::mem_fun(*this, &ResultsTree::onButtonPressEvent)); get_selection()->signal_changed().connect( sigc::mem_fun(*this, &ResultsTree::onSelectionChanged)); // Enable interactive search set_search_column(m_resultsColumns.m_text.index()); // Control which rows can be selected get_selection()->set_select_function(sigc::mem_fun(*this, &ResultsTree::onSelectionSelect)); // Listen for style changes signal_style_changed().connect_notify(sigc::mem_fun(*this, &ResultsTree::onStyleChanged)); // Render the icons m_indexedIconPixbuf = render_icon(Stock::INDEX, ICON_SIZE_MENU, "MetaSE-pinot"); m_viewededIconPixbuf = render_icon(Stock::YES, ICON_SIZE_MENU, "MetaSE-pinot"); m_upIconPixbuf = render_icon(Stock::GO_UP, ICON_SIZE_MENU, "MetaSE-pinot"); m_downIconPixbuf = render_icon(Stock::GO_DOWN, ICON_SIZE_MENU, "MetaSE-pinot"); // Associate the columns model to the extract tree m_refExtractStore = ListStore::create(m_extractColumns); m_extractTreeView->set_model(m_refExtractStore); pColumn = new TreeViewColumn(_("Extract")); pTextRenderer = new CellRendererText(); pColumn->pack_start(*manage(pTextRenderer)); pColumn->set_cell_data_func(*pTextRenderer, sigc::mem_fun(*this, &ResultsTree::renderExtractColumn)); pColumn->add_attribute(pTextRenderer->property_text(), m_extractColumns.m_name); pColumn->set_sizing(TREE_VIEW_COLUMN_AUTOSIZE); m_extractTreeView->append_column(*manage(pColumn)); // Show all show(); m_pResultsScrolledwindow->show(); m_extractTreeView->show(); m_pExtractScrolledwindow->show(); } ResultsTree::~ResultsTree() { } void ResultsTree::renderViewStatus(CellRenderer *pRenderer, const TreeModel::iterator &iter) { TreeModel::Row row = *iter; if (pRenderer == NULL) { return; } CellRendererPixbuf *pIconRenderer = dynamic_cast(pRenderer); if (pIconRenderer != NULL) { // Has this result been already viewed ? if ((row[m_resultsColumns.m_viewed] == true) && (m_viewededIconPixbuf)) { pIconRenderer->property_pixbuf() = m_viewededIconPixbuf; } else { pIconRenderer->property_pixbuf().reset_value(); } } } void ResultsTree::renderIndexStatus(CellRenderer *pRenderer, const TreeModel::iterator &iter) { TreeModel::Row row = *iter; if (pRenderer == NULL) { return; } CellRendererPixbuf *pIconRenderer = dynamic_cast(pRenderer); if (pIconRenderer != NULL) { // Is this result indexed ? if ((row[m_resultsColumns.m_indexed] == true) && (m_indexedIconPixbuf)) { pIconRenderer->property_pixbuf() = m_indexedIconPixbuf; } else { pIconRenderer->property_pixbuf().reset_value(); } } } void ResultsTree::renderRanking(CellRenderer *pRenderer, const TreeModel::iterator &iter) { TreeModel::Row row = *iter; if (pRenderer == NULL) { return; } CellRendererPixbuf *pIconRenderer = dynamic_cast(pRenderer); if (pIconRenderer != NULL) { int rankDiff = row[m_resultsColumns.m_rankDiff]; // Has this result's score changed ? if ((rankDiff > 0) && (rankDiff != 666)) { pIconRenderer->property_pixbuf() = m_upIconPixbuf; } else if (rankDiff < 0) { pIconRenderer->property_pixbuf() = m_downIconPixbuf; } else { pIconRenderer->property_pixbuf().reset_value(); } } } void ResultsTree::renderTitleColumn(CellRenderer *pRenderer, const TreeModel::iterator &iter) { TreeModel::Row row = *iter; if (pRenderer == NULL) { return; } CellRendererText *pTextRenderer = dynamic_cast(pRenderer); if (pTextRenderer != NULL) { // Is this result new ? if (row[m_resultsColumns.m_rankDiff] == 666) { Color newColour; newColour.set_red(m_settings.m_newResultsColourRed); newColour.set_green(m_settings.m_newResultsColourGreen); newColour.set_blue(m_settings.m_newResultsColourBlue); // Change the row's background pTextRenderer->property_background_gdk() = newColour; } else { pTextRenderer->property_background_gdk().reset_value(); } ResultsModelColumns::RowType type = row[m_resultsColumns.m_resultType]; if ((type == ResultsModelColumns::ROW_ENGINE) || (type == ResultsModelColumns::ROW_HOST)) { ustring markup(""); markup += row[m_resultsColumns.m_text]; markup += ""; pTextRenderer->property_markup() = markup; } } } void ResultsTree::renderExtractColumn(CellRenderer *pRenderer, const TreeModel::iterator &iter) { TreeModel::Row row = *iter; if (pRenderer == NULL) { return; } CellRendererText *pTextRenderer = dynamic_cast(pRenderer); if (pTextRenderer != NULL) { ustring markup(row[m_extractColumns.m_name]); pTextRenderer->property_markup() = markup; pTextRenderer->property_single_paragraph_mode() = true; // These properties are not available in gtkmm 2.8 #if GTKMM_MAJOR_VERSION==2 && GTKMM_MINOR_VERSION>=10 pTextRenderer->property_wrap_mode() = Pango::WRAP_WORD; pTextRenderer->property_wrap_width() = m_pExtractScrolledwindow->get_width(); #endif } } void ResultsTree::onButtonPressEvent(GdkEventButton *ev) { // Check for popup click if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3) ) { if (m_pPopupMenu != NULL) { m_pPopupMenu->popup(ev->button, ev->time); } } // Check for double clicks else if (ev->type == GDK_2BUTTON_PRESS) { #ifdef DEBUG cout << "ResultsTree::onButtonPressEvent: double click on button " << ev->button << endl; #endif m_signalDoubleClick(); } } void ResultsTree::onSelectionChanged(void) { m_signalSelectionChanged(m_treeName); } bool ResultsTree::onSelectionSelect(const RefPtr& model, const TreeModel::Path& node_path, bool path_currently_selected) { const TreeModel::iterator iter = model->get_iter(node_path); const TreeModel::Row row = *iter; m_indexNames.clear(); if (path_currently_selected == false) { // Clear the extract m_extractTreeView->get_selection()->unselect_all(); m_refExtractStore->clear(); // Is this an actual result ? ResultsModelColumns::RowType type = row[m_resultsColumns.m_resultType]; if (type == ResultsModelColumns::ROW_RESULT) { #ifdef DEBUG cout << "ResultsTree::onSelectionSelect: extract for " << row[m_resultsColumns.m_text] << endl; #endif TreeModel::iterator extractIter = m_refExtractStore->append(); TreeModel::Row extractRow = *extractIter; extractRow[m_extractColumns.m_name] = findResultsExtract(row); } } return true; } void ResultsTree::onStyleChanged(const RefPtr