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

gcs-bookmarklist.cc

/*******************************************************************************
 *  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 <iostream>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <glib/gstdio.h>

#include <glibmm/date.h>
#include <gtkmm/stock.h>
#include <gtkmm/actiongroup.h>
#include <gtkmm/treerowreference.h>

#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(Glib::ustring fname) :
            m_file(fname),
            m_refUIManager(Gtk::UIManager::create())
        {
            LOG("Populating favorites: " << fname);
            std::ifstream file(fname.c_str());
            try
            {
                m_palette.parse(file);
            }
            catch (const pp::ParseError& e)
            {
                g_warning("%s: %s", fname.c_str(), e.what());
            }
            m_refPaletteModel = PaletteTreeModel::create(m_palette);
            set_model(m_refPaletteModel);
            Glib::RefPtr<Gtk::TreeSelection> 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<Gtk::TargetEntry> 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<Gtk::ActionGroup> 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<Gtk::Menu*>(
                        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<Gtk::TreeView::Selection> 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<Gtk::TreeSelection> sel = get_selection();
            std::list<Gtk::TreeModel::Path> 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<Gtk::TreeModel::RowReference> rows;
            for (std::list<Gtk::TreeModel::Path>::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<Gtk::TreeModel::RowReference>::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<int>(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<Gtk::TreeSelection> refSel = get_selection();
            std::list<Gtk::TreeModel::Path> 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<Gdk::DragContext>& context, int x, int y,
                const Gtk::SelectionData& selection_data, guint info,
                guint time)
        {
            LOG("== Drop received ==");
            boost::shared_ptr<Gdk::Color> 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

Generated by  Doxygen 1.6.0   Back to index