play with some tint2conf code git-svn-id: http://tint2.googlecode.com/svn/trunk@146 121b4492-b84c-0410-8b4c-0d4edfb3f3cc
lorthiois@bbsoft.fr lorthiois@bbsoft.fr@121b4492-b84c-0410-8b4c-0d4edfb3f3cc
6 files changed,
1033 insertions(+),
0 deletions(-)
A
src/tint2conf/main.cpp
@@ -0,0 +1,54 @@
+/************************************************************************** +* +* Tint2conf +* +* Copyright (C) 2009 Thierry lorthiois (lorthiois@bbsoft.fr) +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* as published by the Free Software Foundation. +* +* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**************************************************************************/ + +#include <string> +#include <stdio.h> +#include <gtkmm.h> +#include <glib.h> + +#include "mainwin.h" + + +// g++ `pkg-config --libs --cflags gtkmm-2.4 --libs gthread-2.0` -o tint2conf main.cpp mainwin.cpp thumbview.cpp + +int main (int argc, char ** argv) +{ + // set up i18n + //bindtextdomain(PACKAGE, LOCALEDIR); + //bind_textdomain_codeset(PACKAGE, "UTF-8"); + //textdomain(PACKAGE); + + Gtk::Main kit(argc, argv); + Glib::thread_init(); + + MainWin window; + + window.view.set_dir(""); + window.view.load_dir(); + + // rig up idle/thread routines + Glib::Thread::create(sigc::mem_fun(window.view, &Thumbview::load_cache_images), true); + Glib::Thread::create(sigc::mem_fun(window.view, &Thumbview::create_cache_images), true); + + //Shows the window and returns when it is closed. + Gtk::Main::run(window); + + return 0; +} +
A
src/tint2conf/mainwin.cpp
@@ -0,0 +1,140 @@
+ +#include <gtkmm/stock.h> +#include <iostream> +#include "mainwin.h" + + +MainWin::MainWin() +{ + set_title("Tint2 config"); + set_default_size(600, 350); + + add(m_Box); // put a MenuBar at the top of the box and other stuff below it. + + //Create actions for menus and toolbars: + m_refActionGroup = Gtk::ActionGroup::create(); + + //File menu: + m_refActionGroup->add(Gtk::Action::create("FileMenu", "File")); + //Sub-menu. + m_refActionGroup->add(Gtk::Action::create("FileOpen", Gtk::Stock::OPEN, "_Open", "Open config file"), sigc::mem_fun(*this, &MainWin::on_menu_file_new_generic)); + m_refActionGroup->add(Gtk::Action::create("FileSaveAs", Gtk::Stock::SAVE_AS, "_Save As", "Save config as"), sigc::mem_fun(*this, &MainWin::on_menu_file_new_generic)); + m_refActionGroup->add(Gtk::Action::create("FileRefreshAll", Gtk::Stock::REFRESH, "_Refresh all", "Refresh all config file"), sigc::mem_fun(*this, &MainWin::on_menu_file_new_generic)); + m_refActionGroup->add(Gtk::Action::create("FileQuit", Gtk::Stock::QUIT), sigc::mem_fun(*this, &MainWin::on_menu_file_quit)); + + //Edit menu: + m_refActionGroup->add(Gtk::Action::create("EditMenu", "Edit")); + m_refActionGroup->add(Gtk::Action::create("EditProperties", Gtk::Stock::PROPERTIES, "_Properties...", "Show properties"), sigc::mem_fun(*this, &MainWin::on_menu_others)); + m_refActionGroup->add(Gtk::Action::create("EditRename", "_Rename...", "Rename current config"), sigc::mem_fun(*this, &MainWin::on_menu_others)); + m_refActionGroup->add(Gtk::Action::create("EditDelete", Gtk::Stock::DELETE), sigc::mem_fun(*this, &MainWin::on_menu_others)); + m_refActionGroup->add(Gtk::Action::create("EditApply", Gtk::Stock::APPLY, "_Apply", "Apply current config"), sigc::mem_fun(*this, &MainWin::on_menu_others)); + m_refActionGroup->add(Gtk::Action::create("EditRefresh", Gtk::Stock::REFRESH), sigc::mem_fun(*this, &MainWin::on_menu_others)); + + //Help menu: + m_refActionGroup->add( Gtk::Action::create("HelpMenu", "Help") ); + m_refActionGroup->add( Gtk::Action::create("About", Gtk::Stock::ABOUT), sigc::mem_fun(*this, &MainWin::on_menu_about) ); + + m_refUIManager = Gtk::UIManager::create(); + m_refUIManager->insert_action_group(m_refActionGroup); + + add_accel_group(m_refUIManager->get_accel_group()); + + //Layout the actions in a menubar and toolbar: + Glib::ustring ui_info = + "<ui>" + " <menubar name='MenuBar'>" + " <menu action='FileMenu'>" + " <menuitem action='FileOpen'/>" + " <menuitem action='FileSaveAs'/>" + " <separator/>" + " <menuitem action='FileRefreshAll'/>" + " <separator/>" + " <menuitem action='FileQuit'/>" + " </menu>" + " <menu action='EditMenu'>" + " <menuitem action='EditProperties'/>" + " <menuitem action='EditRename'/>" + " <separator/>" + " <menuitem action='EditDelete'/>" + " <separator/>" + " <menuitem action='EditRefresh'/>" + " </menu>" + " <menu action='HelpMenu'>" + " <menuitem action='About'/>" + " </menu>" + " </menubar>" + " <toolbar name='ToolBar'>" + " <toolitem action='FileRefreshAll'/>" + " <separator/>" + " <toolitem action='EditProperties'/>" + " <toolitem action='EditApply'/>" + " </toolbar>" + "</ui>"; + + #ifdef GLIBMM_EXCEPTIONS_ENABLED + try + { + m_refUIManager->add_ui_from_string(ui_info); + } + catch(const Glib::Error& ex) + { + std::cerr << "building menus failed: " << ex.what(); + } + #else + std::auto_ptr<Glib::Error> ex; + m_refUIManager->add_ui_from_string(ui_info, ex); + if(ex.get()) + { + std::cerr << "building menus failed: " << ex->what(); + } + #endif //GLIBMM_EXCEPTIONS_ENABLED + + //Get the menubar and toolbar widgets, and add them to a container widget: + Gtk::Widget* pMenubar = m_refUIManager->get_widget("/MenuBar"); + if(pMenubar) + m_Box.pack_start(*pMenubar, Gtk::PACK_SHRINK); + + Gtk::Widget* pToolbar = m_refUIManager->get_widget("/ToolBar") ; + if(pToolbar) + m_Box.pack_start(*pToolbar, Gtk::PACK_SHRINK); + + + m_Box.add(view); + + show_all_children(); +} + +MainWin::~MainWin() +{ +} + + +void MainWin::on_menu_file_quit() +{ + hide(); +} + + +void MainWin::on_menu_file_new_generic() +{ + std::cout << "A File|New menu item was selected." << std::endl; +} + + +void MainWin::on_menu_others() +{ + std::cout << "A menu item was selected." << std::endl; +} + + +void MainWin::on_menu_about() +{ + Glib::ustring message = "tint2conf " + Glib::ustring(VERSION); + Gtk::MessageDialog dialog(*this, message); + + dialog.set_title("About tint2conf"); + dialog.set_secondary_text("Config tool for tint2.\n\nCopyright (C) 2009 Thierry lorthiois. \nRefer to source code from Nitrogen\nby Dave Foster & Javeed Shaikh."); + + dialog.run(); +} +
A
src/tint2conf/mainwin.h
@@ -0,0 +1,33 @@
+#ifndef TINT2CONF_MAINWIN_H +#define TINT2CONF_MAINWIN_H + +#include <gtkmm.h> +#include "thumbview.h" + +#define VERSION "0.2" + + +class MainWin : public Gtk::Window +{ +public: + MainWin(); + virtual ~MainWin(); + + Thumbview view; +protected: + //Signal handlers: + void on_menu_file_new_generic(); + void on_menu_file_quit(); + void on_menu_others(); + void on_menu_about(); + + //Child widgets: + Gtk::VBox m_Box; + + Glib::RefPtr<Gtk::UIManager> m_refUIManager; + Glib::RefPtr<Gtk::ActionGroup> m_refActionGroup; + Glib::RefPtr<Gtk::RadioAction> m_refChoiceOne, m_refChoiceTwo; +}; + +#endif +
A
src/tint2conf/thumbview.cpp
@@ -0,0 +1,641 @@
+/* + +This file is from Nitrogen, an X11 background setter. +Copyright (C) 2006 Dave Foster & Javeed Shaikh + +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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +//#include "md5.h" +#include <glib/gstdio.h> +#include <png.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <iostream> +#include "thumbview.h" +//#include "Config.h" +//#include "Util.h" +//#include "gcs-i18n.h" + +/** + * Returns the last modified time of a file. + * @param file The name of the file to get the modified time for. + */ +static time_t get_file_mtime(std::string file) { + struct stat buf; + if (stat(file.c_str(), &buf) == -1) return 0; // error + return buf.st_mtime; +} + +/** + * Returns the value of the "tEXt::Thumb::MTime" key for fd.o style thumbs. + * @param pixbuf The pixbuf of the fd.o thumbnail. + */ +static time_t get_fdo_thumbnail_mtime(Glib::RefPtr<Gdk::Pixbuf> pixbuf) { + std::string mtime_str = pixbuf->get_option("tEXt::Thumb::MTime"); + std::stringstream stream(mtime_str); + time_t mtime = 0; + stream >> mtime; + return mtime; +} + +void DelayLoadingStore::get_value_vfunc (const iterator& iter, int column, Glib::ValueBase& value) const +{ + g_async_queue_ref(aqueue_loadthumbs); + Gtk::ListStore::get_value_vfunc(iter, column, value); + + Gtk::TreeModel::Row row = *iter; + if (column == 0) + { + Glib::Value< Glib::RefPtr<Gdk::Pixbuf> > base; + Gtk::ListStore::get_value_vfunc(iter, column, base); + + Glib::RefPtr<Gdk::Pixbuf> thumb = base.get(); + + if (thumb == thumbview->loading_image && !row[thumbview->record.LoadingThumb]) + { + TreePair* tp = new TreePair(); + tp->file = row[thumbview->record.Filename]; + tp->iter = iter; + + row[thumbview->record.LoadingThumb] = true; + + //Util::program_log("Custom model: planning on loading %s\n", tp->file.c_str()); + + g_async_queue_push(aqueue_loadthumbs, (gpointer)tp); + } + } + g_async_queue_unref(aqueue_loadthumbs); +} + +/** + * Constructor, sets up gtk stuff, inits data and queues + */ +Thumbview::Thumbview() : dir("") { + set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); + set_shadow_type (Gtk::SHADOW_IN); + + // load map of setbgs (need this for add_file) + load_map_setbgs(); + + // make our async queues + this->aqueue_loadthumbs = g_async_queue_new(); + this->aqueue_createthumbs = g_async_queue_new(); + this->aqueue_donethumbs = g_async_queue_new(); + + // create store + store = DelayLoadingStore::create (record); + store->set_queue(aqueue_loadthumbs); + store->set_thumbview(this); + + // setup view + view.set_model (store); + view.set_headers_visible (FALSE); + view.set_fixed_height_mode (TRUE); + view.set_rules_hint (TRUE); + + // set cell renderer proprties + rend.property_ellipsize () = Pango::ELLIPSIZE_END; + rend.set_property ("ellipsize", Pango::ELLIPSIZE_END); + rend.property_weight () = Pango::WEIGHT_BOLD; + + rend_img.set_fixed_size(105, 82); + + // make treeviewcolumns + this->col_thumb = new Gtk::TreeViewColumn("thumbnail", this->rend_img); + this->col_desc = new Gtk::TreeViewColumn("description", this->rend); + + col_thumb->add_attribute (rend_img, "pixbuf", record.Thumbnail); + col_thumb->set_sizing(Gtk::TREE_VIEW_COLUMN_FIXED); + col_thumb->set_fixed_width(105); + col_desc->add_attribute (rend, "markup", record.Description); + col_desc->set_sort_column (record.Filename); + col_desc->set_sort_indicator (true); + col_desc->set_sort_order (Gtk::SORT_ASCENDING); + col_desc->set_sizing(Gtk::TREE_VIEW_COLUMN_FIXED); + + view.append_column (*col_thumb); + view.append_column (*col_desc); + + // enable search + view.set_search_column (record.Description); + view.set_search_equal_func (sigc::mem_fun (this, &Thumbview::search_compare)); + + // load loading image, which not all themes seem to provide + try { + this->loading_image = Gtk::IconTheme::get_default()->load_icon("image-loading", 64, Gtk::ICON_LOOKUP_FORCE_SVG); + } catch (Gtk::IconThemeError e) {} + + // init dispatcher + this->dispatch_thumb.connect(sigc::mem_fun(this, &Thumbview::handle_dispatch_thumb)); + + col_desc->set_expand (); + + add (view); + + view.show (); + show (); +} + +/** + * Destructor + */ +Thumbview::~Thumbview() { + g_async_queue_unref(this->aqueue_loadthumbs); + g_async_queue_unref(this->aqueue_createthumbs); + g_async_queue_unref(this->aqueue_donethumbs); +} + +/** + * Adds the given file to the tree view and pushes it onto the thumbnail + * creation queue. + * + * @param filename The name of the file to add. + * + */ +void Thumbview::add_file(std::string filename) { + Gtk::Window *window = dynamic_cast<Gtk::Window*>(get_toplevel()); + Gtk::TreeModel::iterator iter = this->store->append (); + Gtk::TreeModel::Row row = *iter; + Glib::RefPtr<Gdk::Pixbuf> thumb = this->loading_image; + row[record.Thumbnail] = thumb; + row[record.Filename] = filename; + row[record.Description] = Glib::ustring(filename, filename.rfind ("/")+1); + + for (std::map<Glib::ustring, Glib::ustring>::iterator i = map_setbgs.begin(); i!=map_setbgs.end(); i++) + { + if (filename == (*i).second) + { + row[record.CurBGOnDisp] = (*i).first; +// row[record.Description] = Util::make_current_set_string(window, filename, (*i).first); + } + } + + // for modified time + row[record.Time] = get_file_mtime(filename); + +// Util::program_log("add_file(): Adding file %s\n", filename.c_str()); + + // push it on the thumb queue +// TreePair *tp = new TreePair(); +// tp->file = filename; +// tp->iter = iter; + +// queue_thumbs.push(tp); +} + + +/** + * Opens the internal directory and starts reading files into the async queue. + */ +void Thumbview::load_dir(std::string dir) { + if (!dir.length()) dir = this->dir; + + std::queue<Glib::ustring> subdirs; + Glib::Dir *dirhandle; + + // push the initial dir back onto subdirs + subdirs.push(dir); + + // loop it + while ( ! subdirs.empty() ) { + + // pop first + Glib::ustring curdir = subdirs.front(); + subdirs.pop(); + + try { + dirhandle = new Glib::Dir(curdir); +// Util::program_log("load_dir(): Opening dir %s\n", curdir.c_str()); + + } catch (Glib::FileError e) { + std::cerr << "Could not open dir" << " " << this->dir << ": " << e.what() << "\n"; + continue; + } + +#ifdef USE_INOTIFY + + // check if we're already monitoring this dir. + if (watches.find(curdir) == watches.end()) { + // this dir was successfully opened. monitor it for changes with inotify. + // the Watch will be cleaned up automatically if the dir is deleted. + Inotify::Watch * watch = Inotify::Watch::create(curdir); + if (watch) { + // no error occurred. + + // emitted when a file is deleted in this dir. + watch->signal_deleted.connect(sigc::mem_fun(this, + &Thumbview::file_deleted_callback)); + // emitted when a file is modified or created in this dir. + watch->signal_write_closed.connect(sigc::mem_fun(this, + &Thumbview::file_changed_callback)); + // two signals that are emitted when a file is renamed in this dir. + // the best way to handle this IMO is to remove the file upon receiving + // 'moved_from', and then to add the file upon receiving 'moved_to'. + watch->signal_moved_from.connect(sigc::mem_fun(this, + &Thumbview::file_deleted_callback)); + watch->signal_moved_to.connect(sigc::mem_fun(this, + &Thumbview::file_changed_callback)); + watch->signal_created.connect(sigc::mem_fun(this, + &Thumbview::file_created_callback)); + + watches[curdir] = watch; + } + } + +#endif + + for (Glib::Dir::iterator i = dirhandle->begin(); i != dirhandle->end(); i++) { + Glib::ustring fullstr = curdir + Glib::ustring("/"); + try { + fullstr += /*Glib::filename_to_utf8(*/*i;//); + } catch (Glib::ConvertError& error) { + std::cerr << "Invalid UTF-8 encountered. Skipping file" << " " << *i << std::endl; + continue; + } + + if ( Glib::file_test(fullstr, Glib::FILE_TEST_IS_DIR) ) + { +// if ( Config::get_instance()->get_recurse() ) +// subdirs.push(fullstr); + } + else { + if ( this->is_image(fullstr) ) { + add_file(fullstr); + } + } + } + + delete dirhandle; + } +} + +/** + * Tests the file to see if it is an image + * TODO: come up with less sux way of doing it than extension + * + * @param file The filename to test + * @return If its an image or not + */ +bool Thumbview::is_image(std::string file) { + if (file.find (".jpg") != std::string::npos || + file.find (".JPG") != std::string::npos || + file.find (".jpeg") != std::string::npos || + file.find (".JPEG") != std::string::npos ) + return true; + + return false; +} + +/** + * Determines the full path to a cache file. Does all creation of dirs. + * TODO: should throw exception if we cannot create the dirs! + * + * @param file The file to convert, calls cache_name on it + * @return The full path to + */ +Glib::ustring Thumbview::cache_file(Glib::ustring file) { + + Glib::ustring urifile = Glib::filename_to_uri(file); +/* + // compute md5 of file's uri + md5_state_t state; + md5_byte_t digest[16]; + md5_init(&state); + md5_append(&state, (const md5_byte_t *)urifile.c_str(), strlen(urifile.c_str())); + md5_finish(&state, digest); + + char *buf = new char[3]; + char *full = new char[33]; + full[0] = '\0'; + + for (int di = 0; di < 16; ++di) { + sprintf(buf, "%02x", digest[di]); + strcat(full, buf); + } + + Glib::ustring md5file = Glib::ustring(full) + Glib::ustring(".png"); + delete [] buf; + delete [] full; + + // build dir paths + Glib::ustring halfref = Glib::build_filename(Glib::get_home_dir(),".thumbnails/"); + halfref = Glib::build_filename(halfref, "normal/"); + + if ( !Glib::file_test(halfref, Glib::FILE_TEST_EXISTS) ) + if ( g_mkdir_with_parents(halfref.c_str(), 0700) == -1) + // TODO: exception + return "FAIL"; + + // add and return + return Glib::build_filename(halfref, md5file); +*/ + return urifile; +} + +/** + * Creates cache images that show up in its queue. + */ +void Thumbview::load_cache_images() +{ + g_async_queue_ref(this->aqueue_loadthumbs); + g_async_queue_ref(this->aqueue_donethumbs); + + while(1) + { + // remove first item (blocks until an item occurs) + TreePair *p = (TreePair*)g_async_queue_pop(this->aqueue_loadthumbs); + + Glib::ustring file = p->file; + Glib::ustring cachefile = this->cache_file(file); + + // branch to see if we need to load or create cache file + if ( !Glib::file_test(cachefile, Glib::FILE_TEST_EXISTS) ) { + g_async_queue_push(this->aqueue_createthumbs,(gpointer)p); + } else { + // load thumb + Glib::RefPtr<Gdk::Pixbuf> pb = Gdk::Pixbuf::create_from_file(this->cache_file(file)); + + // resize if we need to + if (pb->get_width() > 100 || pb->get_height() > 80) + { + int pbwidth = pb->get_width(); + int pbheight = pb->get_height(); + float ratio = (float)pbwidth / (float)pbheight; + + int newwidth, newheight; + + if (abs(100 - pbwidth) > abs(80 - pbheight)) + { + // cap to vertical + newheight = 80; + newwidth = newheight * ratio; + } + else + { + // cap to horiz + newwidth = 100; + newheight = newwidth / ratio; + } + + pb = pb->scale_simple(newwidth, newheight, Gdk::INTERP_NEAREST); + } + + if (get_fdo_thumbnail_mtime(pb) < get_file_mtime(file)) { + // the thumbnail is old. we need to make a new one. + pb.clear(); + g_async_queue_push(this->aqueue_createthumbs,(gpointer)p); + } else { + // display it + TreePair *sendp = new TreePair(); + sendp->file = file; + sendp->iter = p->iter; + sendp->thumb = pb; + + g_async_queue_push(this->aqueue_donethumbs, (gpointer)sendp); + + this->dispatch_thumb.emit(); + + delete p; + } + } + } + + g_async_queue_unref(this->aqueue_loadthumbs); + g_async_queue_unref(this->aqueue_donethumbs); + throw Glib::Thread::Exit(); +} + +/** + * Thread function to create thumbnail cache images for those that do not exist. + */ +void Thumbview::create_cache_images() +{ + Glib::RefPtr<Gdk::Pixbuf> thumb; + + g_async_queue_ref(this->aqueue_createthumbs); + g_async_queue_ref(this->aqueue_donethumbs); + + // always work! + while (1) { + + // remove first item (blocks when no items occur) + TreePair *p = (TreePair*)g_async_queue_pop(this->aqueue_createthumbs); + + // get filenames + Glib::ustring file = p->file; + Glib::ustring cachefile = this->cache_file(file); + +// Util::program_log("create_cache_images(): Caching file %s\n", file.c_str()); + + // open image + try { + thumb = Gdk::Pixbuf::create_from_file(file); + } catch (...) { + // forget it, move on + delete p; + continue; + } + + // eliminate zero heights (due to really tiny images :/) + int height = (int)(100*((float)thumb->get_height()/(float)thumb->get_width())); + if (!height) height = 1; + + // create thumb + thumb = thumb->scale_simple(100, height, Gdk::INTERP_TILES); + + // create required fd.o png tags + std::list<Glib::ustring> opts, vals; + opts.push_back(Glib::ustring("tEXt::Thumb::URI")); + vals.push_back(Glib::filename_to_uri(file)); + + time_t mtime = get_file_mtime(file); + + char *bufout = new char[20]; + sprintf(bufout, "%d", mtime); + + opts.push_back(Glib::ustring("tEXt::Thumb::MTime")); + vals.push_back(Glib::ustring(bufout)); + + delete [] bufout; + + thumb->save(cachefile, "png", opts, vals); + + // send it to the display + TreePair *sendp = new TreePair(); + sendp->file = file; + sendp->iter = p->iter; + sendp->thumb = thumb; + g_async_queue_push(this->aqueue_donethumbs, (gpointer)sendp); + + // emit dispatcher + this->dispatch_thumb.emit(); + + delete p; + } + + g_async_queue_unref(this->aqueue_createthumbs); + g_async_queue_unref(this->aqueue_donethumbs); + throw Glib::Thread::Exit(); +} + +void Thumbview::handle_dispatch_thumb() { + g_async_queue_ref(this->aqueue_donethumbs); + + TreePair *donep = (TreePair*)g_async_queue_pop(this->aqueue_donethumbs); + this->update_thumbnail(donep->file, donep->iter, donep->thumb); + delete donep; + + g_async_queue_unref(this->aqueue_donethumbs); +} + +/** + * Updates the treeview to show the passed in thumbnail. + * + */ +void Thumbview::update_thumbnail(Glib::ustring file, Gtk::TreeModel::iterator iter, Glib::RefPtr<Gdk::Pixbuf> pb) +{ + Gtk::TreeModel::Row row = *iter; + row[record.Thumbnail] = pb; + // desc + //row[record.Description] = Glib::ustring(file, file.rfind("/")+1); + + // emit a changed signal + store->row_changed(store->get_path(iter), iter); +} + +/** + * Used by GTK to see whether or not an iterator matches a search string. + */ +bool Thumbview::search_compare (const Glib::RefPtr<Gtk::TreeModel>& model, int column, const Glib::ustring& key, const Gtk::TreeModel::iterator& iter) { + Glib::ustring target = (*iter)[record.Description]; + if (target.find (key) != Glib::ustring::npos) { + return false; + } + return true; +} + +/** + * Sets the sort mode. This tells the view how it should sort backgrounds. + */ +void Thumbview::set_sort_mode (Thumbview::SortMode mode) { + switch (mode) { + case SORT_ALPHA: + store->set_sort_column (record.Description, Gtk::SORT_ASCENDING); + break; + case SORT_RALPHA: + store->set_sort_column (record.Description, Gtk::SORT_DESCENDING); + break; + case SORT_TIME: + store->set_sort_column (record.Time, Gtk::SORT_ASCENDING); + break; + case SORT_RTIME: + store->set_sort_column (record.Time, Gtk::SORT_DESCENDING); + break; + } +} + +/** + * Loads the map of displays and their set bgs. Used to indicate which + * files are currently set as a background. + */ +void Thumbview::load_map_setbgs() +{ + map_setbgs.clear(); + + std::vector<Glib::ustring> vecgroups; + bool ret = false; +// bool ret = Config::get_instance()->get_bg_groups(vecgroups); + if (!ret) + return; + + for (std::vector<Glib::ustring>::iterator i = vecgroups.begin(); i!=vecgroups.end(); i++) + { + Glib::ustring file; +// SetBG::SetMode mode; + Gdk::Color bgcolor; + +// ret = Config::get_instance()->get_bg(*i, file, mode, bgcolor); + if (!ret) + { + std::cerr << "(load_map_setbgs) Could not get background stored info for " << *i << "\n"; + return; + } + + map_setbgs[*i] = file; + } +} + +#ifdef USE_INOTIFY + +/** + * Called when a file in a directory being monitored by inotify is deleted. + * Removes the file from the tree view. + * + * @param filename The name of the file to remove. + */ +void Thumbview::file_deleted_callback(std::string filename) { + if (watches.find(filename) != watches.end()) { + watches.erase(filename); + return; + } + Gtk::TreeIter iter; + Gtk::TreeModel::Children children = store->children(); + for (iter = children.begin(); iter != children.end(); iter++) { + Glib::ustring this_filename = (*iter)[record.Filename]; + if (this_filename == filename) { + // remove this iter. + store->erase(iter); + break; + } + } +} + +/** + * Called when a file in a directory being monitored by inotify is modified + * or a new file is created. Adds the file to the tree view. + * + * @param filename The name of the file modified or created. + */ +void Thumbview::file_changed_callback(std::string filename) { + // first remove the old instance of this file. + file_deleted_callback(filename); + if ( Glib::file_test(filename, Glib::FILE_TEST_IS_DIR) ) { + // this is a directory; use load_dir() to set us up the bomb. + load_dir(filename); + } else if (is_image(filename)) { + // this is a file. + add_file(filename); + } + // restart the idle function + //Glib::signal_idle().connect(sigc::mem_fun(this, &Thumbview::load_cache_images)); +} + +/** + * Called when a new file or directory is created in a directory being + * monitored. Discards the event for non-directories, because + * file_changed_callback will be called for those. + * + * @param filename The name of the file modified or created. + */ +void Thumbview::file_created_callback(std::string filename) { + if ( Glib::file_test(filename, Glib::FILE_TEST_IS_DIR) ) { + file_changed_callback(filename); + } +} + +#endif
A
src/tint2conf/thumbview.h
@@ -0,0 +1,162 @@
+/* + +This file is from Nitrogen, an X11 background setter. +Copyright (C) 2006 Dave Foster & Javeed Shaikh + +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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include <queue> +#include <errno.h> +#include <gtkmm.h> + +#ifdef USE_INOTIFY +//#include "Inotify.h" +#endif + +struct TreePair { + Glib::ustring file; + Gtk::TreeModel::iterator iter; + Glib::RefPtr<Gdk::Pixbuf> thumb; +}; + +class Thumbview; + +class DelayLoadingStore : public Gtk::ListStore +{ + public: + static Glib::RefPtr<DelayLoadingStore> create(const Gtk::TreeModelColumnRecord& columns) + { + return Glib::RefPtr<DelayLoadingStore>(new DelayLoadingStore(columns)); + } + + protected: + DelayLoadingStore() : Gtk::ListStore() {} + DelayLoadingStore(const Gtk::TreeModelColumnRecord& columns) : Gtk::ListStore(columns) {} + + protected: + virtual void get_value_vfunc (const iterator& iter, int column, Glib::ValueBase& value) const; + + public: + void set_queue(GAsyncQueue *queue) { aqueue_loadthumbs = queue; } + void set_thumbview(Thumbview *view) { thumbview = view; } + + protected: + GAsyncQueue *aqueue_loadthumbs; + Thumbview *thumbview; + + +}; + +///////////////////////////////////////////////////////////////////////////// + +/** + * Column record for the Thumbview store. + */ +class ThumbviewRecord : public Gtk::TreeModelColumnRecord +{ + public: + ThumbviewRecord() + { + add(Thumbnail); + add(Description); + add(Filename); + add(Time); + add(LoadingThumb); + add(CurBGOnDisp); + } + + Gtk::TreeModelColumn<Glib::ustring> Filename; + Gtk::TreeModelColumn<Glib::ustring> Description; + Gtk::TreeModelColumn< Glib::RefPtr<Gdk::Pixbuf> > Thumbnail; + Gtk::TreeModelColumn<time_t> Time; + Gtk::TreeModelColumn<bool> LoadingThumb; + Gtk::TreeModelColumn<Glib::ustring> CurBGOnDisp; +}; + + +///////////////////////////////////////////////////////////////////////////// + +class Thumbview : public Gtk::ScrolledWindow { + public: + Thumbview (); + ~Thumbview (); + + typedef enum { + SORT_ALPHA, + SORT_RALPHA, + SORT_TIME, + SORT_RTIME + } SortMode; + + void set_dir(std::string indir) { this->dir = indir; } + + Glib::RefPtr<DelayLoadingStore> store; + Gtk::TreeView view; + ThumbviewRecord record; + + // dispatcher + Glib::Dispatcher dispatch_thumb; + + // thread/idle funcs + void load_cache_images(); + void create_cache_images(); + void load_dir(std::string dir = ""); + + void set_sort_mode (SortMode mode); + // search compare function + bool search_compare (const Glib::RefPtr<Gtk::TreeModel>& model, int column, const Glib::ustring& key, const Gtk::TreeModel::iterator& iter); + + // loading image + Glib::RefPtr<Gdk::Pixbuf> loading_image; + + // "cache" of config - maps displays to full filenames + std::map<Glib::ustring, Glib::ustring> map_setbgs; + void load_map_setbgs(); + + protected: + +#ifdef USE_INOTIFY + void file_deleted_callback(std::string filename); + void file_changed_callback(std::string filename); + void file_created_callback(std::string filename); + std::map<std::string, Inotify::Watch*> watches; +#endif + + void add_file(std::string filename); + void handle_dispatch_thumb(); + + Gtk::TreeViewColumn *col_thumb; + Gtk::TreeViewColumn *col_desc; + + Gtk::CellRendererPixbuf rend_img; + Gtk::CellRendererText rend; + + // utility functions + bool is_image(std::string file); + Glib::ustring cache_file(Glib::ustring file); + void update_thumbnail(Glib::ustring file, Gtk::TreeModel::iterator iter, Glib::RefPtr<Gdk::Pixbuf> pb); + + // base dir + // TODO: remove when we get a proper db + std::string dir; + + // load thumbnail queue + GAsyncQueue* aqueue_loadthumbs; + GAsyncQueue* aqueue_createthumbs; + GAsyncQueue* aqueue_donethumbs; + +};