/******************************************************************************* * PROJECT: GNOME Colorscheme * * AUTHOR: Jonathon Jongsma * * Copyright (c) 2005 Jonathon Jongsma * * License: * 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 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 "gcs-bookmarklist.h" #include "palettetreemodel.h" #include "gcs-cellrendererswatch.h" #include "core/log-stream.h" #include "gcs-util.h" #include "gcs-i18n.h" #include "gcs-conf.h" namespace gcs { namespace Widgets { BookmarkList::BookmarkList() : m_file(Conf::get_favorites_file()), m_refUIManager(Gtk::UIManager::create()) { LOG("Populating favorites: " << m_file); std::ifstream file(m_file.c_str()); try { m_palette.parse(file); } catch (const pp::ParseError& e) { g_warning("%s: %s", m_file.c_str(), e.what()); } m_refPaletteModel = PaletteTreeModel::create(m_palette); set_model(m_refPaletteModel); Glib::RefPtr refSel = get_selection(); refSel->set_mode(Gtk::SELECTION_MULTIPLE); // display column headers set_headers_visible(true); // Create a instance of a custom CellRenderer for drawing color // swatches CellRendererSwatch *const pRenderer = Gtk::manage(new CellRendererSwatch); // Create a new Column named "Favorites" Gtk::TreeViewColumn *const pColumn = Gtk::manage(new Gtk::TreeViewColumn(_("Favorites"))); // Pack the custom swatch renderer into the column pColumn->pack_start(*pRenderer, false); // associate the 'color' property of the custom renderer with the // value of m_columns.m_colColor so that the cell renderer can color // each swatch according to the bookmarked item pColumn->add_attribute(pRenderer->property_color(), m_refPaletteModel->get_color_column()); // add a the model text column to the TreeView column -- This packs // both the swatch and the hexstring into the same treeview column pColumn->pack_start(m_refPaletteModel->get_text_column(), false); // append the column to the TreeView append_column(*pColumn); // FIXME: // set up a dummy hidden column with nothing in it and assign it as // the expander column. This is a nasty workaround so that the // first visible column doesn't have a big space at the beginning Gtk::TreeViewColumn dummy_column; dummy_column.set_visible(false); append_column(dummy_column); set_expander_column(dummy_column); set_reorderable(); // save the list when it gets re-ordered so that it stays in that // order the next time the user runs the app signal_drag_end().connect(sigc::hide( sigc::mem_fun(*this, &BookmarkList::on_list_changed))); m_refPaletteModel->signal_rows_reordered().connect(sigc::hide(sigc::hide(sigc::hide( sigc::mem_fun(*this, &BookmarkList::on_list_changed))))); // save the palette when a color gets renamed m_refPaletteModel->signal_row_changed().connect(sigc::hide(sigc::hide( sigc::mem_fun(*this, &BookmarkList::on_list_changed)))); //std::list targets; //targets.push_back(Gtk::TargetEntry("application/x-color")); //drag_dest_set(targets); //signal_drag_data_received().connect(sigc::mem_fun(*this, //&BookmarkList::on_drop_drag_data_received)); // set up the popup menu Glib::RefPtr refActions = Gtk::ActionGroup::create(); // the name of the menu item in the popup in the favorites list refActions->add(Gtk::Action::create("RenameBookmark", Gtk::Stock::EDIT, _("_Rename Color")), sigc::mem_fun(*this, &BookmarkList::on_action_rename)); m_refUIManager->insert_action_group(refActions); try { m_refUIManager->add_ui_from_file(AGAVE_UIDIR "/bookmarkspopup.ui"); m_popupMenu = static_cast( m_refUIManager->get_widget("/BookmarkPopup")); assert(m_popupMenu); } catch(const Glib::Error& ex) { std::cerr << __FILE__ << ": " << ex.what() << std::endl; throw ex; } } void BookmarkList::add(ColorPtr clr) { Gtk::TreeModel::Row row; // check if the color already exists in the palette. bool exists = false; for (PaletteTreeModel::iterator iter = m_refPaletteModel->children().begin(); iter != m_refPaletteModel->children().end(); ++iter) { if (*(*iter).get_value(m_refPaletteModel->get_color_column()) == *clr) { exists = true; row = *iter; } } if (!exists) { row = m_refPaletteModel->append(clr); } // select the newly added row Glib::RefPtr sel = get_selection(); sel->unselect_all(); sel->select(row); // save the bookmarks to disk save_to_disk(); } // returns the color of the currently selected row. Will return NULL // if not exactly one row is selected ColorPtr BookmarkList::get_color(void) { ColorPtr pClr; Gtk::TreeModel::iterator iter = get_selected_iter(); if (iter) { // need to use get_value() here instead of operator[] pClr = iter->get_value(m_refPaletteModel->get_color_column()); } // if number of selected rows is not exactly 1, this will be a NULL // pointer return pClr; } // clear out the bookmarks list void BookmarkList::clear(void) { get_selection()->unselect_all(); m_refPaletteModel->clear(); save_to_disk(); } // check if the list is empty bool BookmarkList::empty(void) { return m_refPaletteModel->children().empty(); } void BookmarkList::remove_selected(void) { Glib::RefPtr sel = get_selection(); std::list paths = sel->get_selected_rows(); sel->unselect_all(); // get a reference to the row one past the end so we can select it // after we're done removing the selected rows. This lets a user // easily remove multiple rows sequentially by simply pressing the // 'remove' button over and over if (paths.size() > 0) { // get an iterator pointing to the last selected item in the // list Gtk::TreeModel::iterator last_iter = m_refPaletteModel->get_iter(*paths.rbegin()); ++last_iter; // advance to the next row in the model if (last_iter) { sel->select(last_iter); } } // convert the paths to a list of row references. A row reference // is always valid as long as the row exists, whereas a treeiter or // path might become invalid when other rows get inserted / deleted. // We need RowReferences here if we want to delete multiple rows. // If we used TreeIters, the later ones would become invalid before // we can delete them. std::list rows; for (std::list::iterator pathiter = paths.begin(); pathiter != paths.end(); pathiter++) { rows.push_back(Gtk::TreeModel::RowReference(get_model(), *pathiter)); } // remove the rows from the treemodel for (std::list::iterator i = rows.begin(); i != rows.end(); i++) { Gtk::TreeModel::iterator treeiter = m_refPaletteModel->get_iter(i->get_path()); m_refPaletteModel->erase(treeiter); } save_to_disk(); } gint BookmarkList::count_selected(void) { return get_selection()->count_selected_rows(); } void BookmarkList::save_to_disk(Glib::ustring filename) { if (filename.empty()) { filename = m_file; } if (!filename.empty()) { Glib::ustring dirname(Glib::path_get_dirname(filename)); LOG("Dirname: " << dirname); try { // FIXME: there should be a better way to do this // will throw Glib::FileError if it doesn't exist Glib::Dir parent(dirname); } catch (Glib::FileError& e) { LOG("Creating directory for bookmarks, etc: " << dirname); // create the directory for holding colorscheme data // must have execute bit for creating files inside the directory g_mkdir(dirname.c_str(), 0755); } std::ofstream fav_file(filename.c_str()); if (fav_file.is_open()) { // make a temporary copy of the palette so that we can // change the name of the palette without affecting the // normal bookmarks pp::Palette temp_palette(m_palette); Glib::Date date; // using set_time_current() doesn't work due to a bug in // gtkmm date.set_time(time(NULL)); std::ostringstream ostream; // make the name of the exported palette file include the // date that the file was exported ostream << "Favorite Colors, exported " << date.get_year() << "/" << date.get_month() << "/" << static_cast(date.get_day()); temp_palette.set_name(ostream.str()); fav_file << temp_palette; } else { std::cerr << "*** Error opening bookmarks for writing" << std::endl; } } } bool BookmarkList::on_button_press_event(GdkEventButton* event) { Gtk::TreeView::on_button_press_event(event); LOG("button pressed: " << event->button); //Then do our custom stuff: if (event->type == GDK_BUTTON_PRESS && event->button == 3) { LOG("Right button pressed"); m_popupMenu->popup(event->button, event->time); } return true; } void BookmarkList::on_action_rename(void) { Dialogs::RenameEntry entry; Glib::ustring name; Gtk::TreeModel::iterator iter = get_selected_iter(); // make sure it's valid if (iter) { // need to use get_value() here instead of operator[] name = iter->get_value(m_refPaletteModel->get_text_column()); } entry.set_name(name); if (entry.run() == Gtk::RESPONSE_OK) { if (iter) { iter->set_value(m_refPaletteModel->get_text_column(), entry.get_name()); } } } Gtk::TreeModel::iterator BookmarkList::get_selected_iter(void) { Gtk::TreeModel::iterator iter; Glib::RefPtr refSel = get_selection(); std::list paths = refSel->get_selected_rows(); if (paths.size() == 1) { // get an iterator pointing to the first (and only) selected row iter = m_refPaletteModel->get_iter(*paths.begin()); } return iter; } /* void BookmarkList::on_drop_drag_data_received(const Glib::RefPtr& context, int x, int y, const Gtk::SelectionData& selection_data, guint info, guint time) { LOG("== Drop received =="); boost::shared_ptr c = get_dropped_color(selection_data); bool drag_success = false; if (c) { // create a gcs::Color from the Gdk::Color ColorPtr pClr = Color::create(*c); // set the application's current color add(pClr); bool drag_success = true; } context->drag_finish(drag_success, false, time); } */ } // namespace Widgets namespace Dialogs { RenameEntry::RenameEntry(void) : pEntry(Gtk::manage(new Gtk::Entry)), // The instructions for the rename dialog pInstructions(Gtk::manage(new Gtk::Label(_("Enter a new name:"), Gtk::ALIGN_LEFT))) { // the title of the color rename dialog set_title(_("Rename Color")); set_border_width(Conf::WINDOW_BORDER); add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK); set_has_separator(false); pInstructions->set_use_markup(true); // make it so that hitting enter in the text entry will 'click' OK pEntry->set_activates_default(); set_default_response(Gtk::RESPONSE_OK); Gtk::VBox* pVbox = get_vbox(); assert(pVbox); pVbox->set_spacing(Conf::UI_SPACING_SMALL); pVbox->set_border_width(Conf::UI_SPACING_SMALL); pVbox->pack_start(*pInstructions); pVbox->pack_start(*pEntry); show_all(); } void RenameEntry::set_name(Glib::ustring name) { pEntry->set_text(name); pEntry->select_region(0, name.size()); } Glib::ustring RenameEntry::get_name(void) { return pEntry->get_text(); } } // namespace Dialogs } // namespace gcs