]>
Dogcows Code - chaz/tint2/blob - src/tint2conf/thumbview.cpp
43b49be3d1c67bf2cd1d8b2137baa19678466535
3 This file is from Nitrogen, an X11 background setter.
4 Copyright (C) 2006 Dave Foster & Javeed Shaikh
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include <glib/gstdio.h>
25 #include <sys/types.h>
29 #include "thumbview.h"
32 //#include "gcs-i18n.h"
35 * Returns the last modified time of a file.
36 * @param file The name of the file to get the modified time for.
38 static time_t get_file_mtime(std::string file
) {
40 if (stat(file
.c_str(), &buf
) == -1) return 0; // error
45 * Returns the value of the "tEXt::Thumb::MTime" key for fd.o style thumbs.
46 * @param pixbuf The pixbuf of the fd.o thumbnail.
48 static time_t get_fdo_thumbnail_mtime(Glib::RefPtr
<Gdk::Pixbuf
> pixbuf
) {
49 std::string mtime_str
= pixbuf
->get_option("tEXt::Thumb::MTime");
50 std::stringstream
stream(mtime_str
);
56 void DelayLoadingStore::get_value_vfunc (const iterator
& iter
, int column
, Glib::ValueBase
& value
) const
58 g_async_queue_ref(aqueue_loadthumbs
);
59 Gtk::ListStore::get_value_vfunc(iter
, column
, value
);
61 Gtk::TreeModel::Row row
= *iter
;
64 Glib::Value
< Glib::RefPtr
<Gdk::Pixbuf
> > base
;
65 Gtk::ListStore::get_value_vfunc(iter
, column
, base
);
67 Glib::RefPtr
<Gdk::Pixbuf
> thumb
= base
.get();
69 if (thumb
== thumbview
->loading_image
&& !row
[thumbview
->record
.LoadingThumb
])
71 TreePair
* tp
= new TreePair();
72 tp
->file
= row
[thumbview
->record
.Filename
];
75 row
[thumbview
->record
.LoadingThumb
] = true;
77 //Util::program_log("Custom model: planning on loading %s\n", tp->file.c_str());
79 g_async_queue_push(aqueue_loadthumbs
, (gpointer
)tp
);
82 g_async_queue_unref(aqueue_loadthumbs
);
86 * Constructor, sets up gtk stuff, inits data and queues
88 Thumbview::Thumbview() : dir("") {
89 set_policy (Gtk::POLICY_NEVER
, Gtk::POLICY_AUTOMATIC
);
90 set_shadow_type (Gtk::SHADOW_IN
);
92 // load map of setbgs (need this for add_file)
95 // make our async queues
96 this->aqueue_loadthumbs
= g_async_queue_new();
97 this->aqueue_createthumbs
= g_async_queue_new();
98 this->aqueue_donethumbs
= g_async_queue_new();
101 store
= DelayLoadingStore::create (record
);
102 store
->set_queue(aqueue_loadthumbs
);
103 store
->set_thumbview(this);
106 view
.set_model (store
);
107 view
.set_headers_visible (FALSE
);
108 view
.set_fixed_height_mode (TRUE
);
109 view
.set_rules_hint (TRUE
);
111 // set cell renderer proprties
112 rend
.property_ellipsize () = Pango::ELLIPSIZE_END
;
113 rend
.set_property ("ellipsize", Pango::ELLIPSIZE_END
);
114 rend
.property_weight () = Pango::WEIGHT_BOLD
;
116 rend_img
.set_fixed_size(105, 82);
118 // make treeviewcolumns
119 this->col_thumb
= new Gtk::TreeViewColumn("thumbnail", this->rend_img
);
120 this->col_desc
= new Gtk::TreeViewColumn("description", this->rend
);
122 col_thumb
->add_attribute (rend_img
, "pixbuf", record
.Thumbnail
);
123 col_thumb
->set_sizing(Gtk::TREE_VIEW_COLUMN_FIXED
);
124 col_thumb
->set_fixed_width(105);
125 col_desc
->add_attribute (rend
, "markup", record
.Description
);
126 col_desc
->set_sort_column (record
.Filename
);
127 col_desc
->set_sort_indicator (true);
128 col_desc
->set_sort_order (Gtk::SORT_ASCENDING
);
129 col_desc
->set_sizing(Gtk::TREE_VIEW_COLUMN_FIXED
);
131 view
.append_column (*col_thumb
);
132 view
.append_column (*col_desc
);
135 view
.set_search_column (record
.Description
);
136 view
.set_search_equal_func (sigc::mem_fun (this, &Thumbview::search_compare
));
138 // load loading image, which not all themes seem to provide
140 this->loading_image
= Gtk::IconTheme::get_default()->load_icon("image-loading", 64, Gtk::ICON_LOOKUP_FORCE_SVG
);
141 } catch (Gtk::IconThemeError e
) {}
144 this->dispatch_thumb
.connect(sigc::mem_fun(this, &Thumbview::handle_dispatch_thumb
));
146 col_desc
->set_expand ();
157 Thumbview::~Thumbview() {
158 g_async_queue_unref(this->aqueue_loadthumbs
);
159 g_async_queue_unref(this->aqueue_createthumbs
);
160 g_async_queue_unref(this->aqueue_donethumbs
);
164 * Adds the given file to the tree view and pushes it onto the thumbnail
167 * @param filename The name of the file to add.
170 void Thumbview::add_file(std::string filename
) {
171 Gtk::Window
*window
= dynamic_cast<Gtk::Window
*>(get_toplevel());
172 Gtk::TreeModel::iterator iter
= this->store
->append ();
173 Gtk::TreeModel::Row row
= *iter
;
174 Glib::RefPtr
<Gdk::Pixbuf
> thumb
= this->loading_image
;
175 row
[record
.Thumbnail
] = thumb
;
176 row
[record
.Filename
] = filename
;
177 row
[record
.Description
] = Glib::ustring(filename
, filename
.rfind ("/")+1);
179 for (std::map
<Glib::ustring
, Glib::ustring
>::iterator i
= map_setbgs
.begin(); i
!=map_setbgs
.end(); i
++)
181 if (filename
== (*i
).second
)
183 row
[record
.CurBGOnDisp
] = (*i
).first
;
184 // row[record.Description] = Util::make_current_set_string(window, filename, (*i).first);
189 row
[record
.Time
] = get_file_mtime(filename
);
191 // Util::program_log("add_file(): Adding file %s\n", filename.c_str());
193 // push it on the thumb queue
194 // TreePair *tp = new TreePair();
195 // tp->file = filename;
198 // queue_thumbs.push(tp);
203 * Opens the internal directory and starts reading files into the async queue.
205 void Thumbview::load_dir(std::string dir
) {
206 if (!dir
.length()) dir
= this->dir
;
208 std::queue
<Glib::ustring
> subdirs
;
209 Glib::Dir
*dirhandle
;
211 // push the initial dir back onto subdirs
215 while ( ! subdirs
.empty() ) {
218 Glib::ustring curdir
= subdirs
.front();
222 dirhandle
= new Glib::Dir(curdir
);
223 // Util::program_log("load_dir(): Opening dir %s\n", curdir.c_str());
225 } catch (Glib::FileError e
) {
226 std::cerr
<< "Could not open dir" << " " << this->dir
<< ": " << e
.what() << "\n";
232 // check if we're already monitoring this dir.
233 if (watches
.find(curdir
) == watches
.end()) {
234 // this dir was successfully opened. monitor it for changes with inotify.
235 // the Watch will be cleaned up automatically if the dir is deleted.
236 Inotify::Watch
* watch
= Inotify::Watch::create(curdir
);
238 // no error occurred.
240 // emitted when a file is deleted in this dir.
241 watch
->signal_deleted
.connect(sigc::mem_fun(this,
242 &Thumbview::file_deleted_callback
));
243 // emitted when a file is modified or created in this dir.
244 watch
->signal_write_closed
.connect(sigc::mem_fun(this,
245 &Thumbview::file_changed_callback
));
246 // two signals that are emitted when a file is renamed in this dir.
247 // the best way to handle this IMO is to remove the file upon receiving
248 // 'moved_from', and then to add the file upon receiving 'moved_to'.
249 watch
->signal_moved_from
.connect(sigc::mem_fun(this,
250 &Thumbview::file_deleted_callback
));
251 watch
->signal_moved_to
.connect(sigc::mem_fun(this,
252 &Thumbview::file_changed_callback
));
253 watch
->signal_created
.connect(sigc::mem_fun(this,
254 &Thumbview::file_created_callback
));
256 watches
[curdir
] = watch
;
262 for (Glib::Dir::iterator i
= dirhandle
->begin(); i
!= dirhandle
->end(); i
++) {
263 Glib::ustring fullstr
= curdir
+ Glib::ustring("/");
265 fullstr
+= /*Glib::filename_to_utf8(*/*i
;//);
266 } catch (Glib::ConvertError
& error
) {
267 std::cerr
<< "Invalid UTF-8 encountered. Skipping file" << " " << *i
<< std::endl
;
271 if ( Glib::file_test(fullstr
, Glib::FILE_TEST_IS_DIR
) )
273 // if ( Config::get_instance()->get_recurse() )
274 // subdirs.push(fullstr);
277 if ( this->is_image(fullstr
) ) {
288 * Tests the file to see if it is an image
289 * TODO: come up with less sux way of doing it than extension
291 * @param file The filename to test
292 * @return If its an image or not
294 bool Thumbview::is_image(std::string file
) {
295 if (file
.find (".jpg") != std::string::npos
||
296 file
.find (".JPG") != std::string::npos
||
297 file
.find (".jpeg") != std::string::npos
||
298 file
.find (".JPEG") != std::string::npos
)
305 * Determines the full path to a cache file. Does all creation of dirs.
306 * TODO: should throw exception if we cannot create the dirs!
308 * @param file The file to convert, calls cache_name on it
309 * @return The full path to
311 Glib::ustring
Thumbview::cache_file(Glib::ustring file
) {
313 Glib::ustring urifile
= Glib::filename_to_uri(file
);
315 // compute md5 of file's uri
317 md5_byte_t digest[16];
319 md5_append(&state, (const md5_byte_t *)urifile.c_str(), strlen(urifile.c_str()));
320 md5_finish(&state, digest);
322 char *buf = new char[3];
323 char *full = new char[33];
326 for (int di = 0; di < 16; ++di) {
327 sprintf(buf, "%02x", digest[di]);
331 Glib::ustring md5file = Glib::ustring(full) + Glib::ustring(".png");
336 Glib::ustring halfref = Glib::build_filename(Glib::get_home_dir(),".thumbnails/");
337 halfref = Glib::build_filename(halfref, "normal/");
339 if ( !Glib::file_test(halfref, Glib::FILE_TEST_EXISTS) )
340 if ( g_mkdir_with_parents(halfref.c_str(), 0700) == -1)
345 return Glib::build_filename(halfref, md5file);
351 * Creates cache images that show up in its queue.
353 void Thumbview::load_cache_images()
355 g_async_queue_ref(this->aqueue_loadthumbs
);
356 g_async_queue_ref(this->aqueue_donethumbs
);
360 // remove first item (blocks until an item occurs)
361 TreePair
*p
= (TreePair
*)g_async_queue_pop(this->aqueue_loadthumbs
);
363 Glib::ustring file
= p
->file
;
364 Glib::ustring cachefile
= this->cache_file(file
);
366 // branch to see if we need to load or create cache file
367 if ( !Glib::file_test(cachefile
, Glib::FILE_TEST_EXISTS
) ) {
368 g_async_queue_push(this->aqueue_createthumbs
,(gpointer
)p
);
371 Glib::RefPtr
<Gdk::Pixbuf
> pb
= Gdk::Pixbuf::create_from_file(this->cache_file(file
));
373 // resize if we need to
374 if (pb
->get_width() > 100 || pb
->get_height() > 80)
376 int pbwidth
= pb
->get_width();
377 int pbheight
= pb
->get_height();
378 float ratio
= (float)pbwidth
/ (float)pbheight
;
380 int newwidth
, newheight
;
382 if (abs(100 - pbwidth
) > abs(80 - pbheight
))
386 newwidth
= newheight
* ratio
;
392 newheight
= newwidth
/ ratio
;
395 pb
= pb
->scale_simple(newwidth
, newheight
, Gdk::INTERP_NEAREST
);
398 if (get_fdo_thumbnail_mtime(pb
) < get_file_mtime(file
)) {
399 // the thumbnail is old. we need to make a new one.
401 g_async_queue_push(this->aqueue_createthumbs
,(gpointer
)p
);
404 TreePair
*sendp
= new TreePair();
406 sendp
->iter
= p
->iter
;
409 g_async_queue_push(this->aqueue_donethumbs
, (gpointer
)sendp
);
411 this->dispatch_thumb
.emit();
418 g_async_queue_unref(this->aqueue_loadthumbs
);
419 g_async_queue_unref(this->aqueue_donethumbs
);
420 throw Glib::Thread::Exit();
424 * Thread function to create thumbnail cache images for those that do not exist.
426 void Thumbview::create_cache_images()
428 Glib::RefPtr
<Gdk::Pixbuf
> thumb
;
430 g_async_queue_ref(this->aqueue_createthumbs
);
431 g_async_queue_ref(this->aqueue_donethumbs
);
436 // remove first item (blocks when no items occur)
437 TreePair
*p
= (TreePair
*)g_async_queue_pop(this->aqueue_createthumbs
);
440 Glib::ustring file
= p
->file
;
441 Glib::ustring cachefile
= this->cache_file(file
);
443 // Util::program_log("create_cache_images(): Caching file %s\n", file.c_str());
447 thumb
= Gdk::Pixbuf::create_from_file(file
);
449 // forget it, move on
454 // eliminate zero heights (due to really tiny images :/)
455 int height
= (int)(100*((float)thumb
->get_height()/(float)thumb
->get_width()));
456 if (!height
) height
= 1;
459 thumb
= thumb
->scale_simple(100, height
, Gdk::INTERP_TILES
);
461 // create required fd.o png tags
462 std::list
<Glib::ustring
> opts
, vals
;
463 opts
.push_back(Glib::ustring("tEXt::Thumb::URI"));
464 vals
.push_back(Glib::filename_to_uri(file
));
466 time_t mtime
= get_file_mtime(file
);
468 char *bufout
= new char[20];
469 sprintf(bufout
, "%d", mtime
);
471 opts
.push_back(Glib::ustring("tEXt::Thumb::MTime"));
472 vals
.push_back(Glib::ustring(bufout
));
476 thumb
->save(cachefile
, "png", opts
, vals
);
478 // send it to the display
479 TreePair
*sendp
= new TreePair();
481 sendp
->iter
= p
->iter
;
482 sendp
->thumb
= thumb
;
483 g_async_queue_push(this->aqueue_donethumbs
, (gpointer
)sendp
);
486 this->dispatch_thumb
.emit();
491 g_async_queue_unref(this->aqueue_createthumbs
);
492 g_async_queue_unref(this->aqueue_donethumbs
);
493 throw Glib::Thread::Exit();
496 void Thumbview::handle_dispatch_thumb() {
497 g_async_queue_ref(this->aqueue_donethumbs
);
499 TreePair
*donep
= (TreePair
*)g_async_queue_pop(this->aqueue_donethumbs
);
500 this->update_thumbnail(donep
->file
, donep
->iter
, donep
->thumb
);
503 g_async_queue_unref(this->aqueue_donethumbs
);
507 * Updates the treeview to show the passed in thumbnail.
510 void Thumbview::update_thumbnail(Glib::ustring file
, Gtk::TreeModel::iterator iter
, Glib::RefPtr
<Gdk::Pixbuf
> pb
)
512 Gtk::TreeModel::Row row
= *iter
;
513 row
[record
.Thumbnail
] = pb
;
515 //row[record.Description] = Glib::ustring(file, file.rfind("/")+1);
517 // emit a changed signal
518 store
->row_changed(store
->get_path(iter
), iter
);
522 * Used by GTK to see whether or not an iterator matches a search string.
524 bool Thumbview::search_compare (const Glib::RefPtr
<Gtk::TreeModel
>& model
, int column
, const Glib::ustring
& key
, const Gtk::TreeModel::iterator
& iter
) {
525 Glib::ustring target
= (*iter
)[record
.Description
];
526 if (target
.find (key
) != Glib::ustring::npos
) {
533 * Sets the sort mode. This tells the view how it should sort backgrounds.
535 void Thumbview::set_sort_mode (Thumbview::SortMode mode
) {
538 store
->set_sort_column (record
.Description
, Gtk::SORT_ASCENDING
);
541 store
->set_sort_column (record
.Description
, Gtk::SORT_DESCENDING
);
544 store
->set_sort_column (record
.Time
, Gtk::SORT_ASCENDING
);
547 store
->set_sort_column (record
.Time
, Gtk::SORT_DESCENDING
);
553 * Loads the map of displays and their set bgs. Used to indicate which
554 * files are currently set as a background.
556 void Thumbview::load_map_setbgs()
560 std::vector
<Glib::ustring
> vecgroups
;
562 // bool ret = Config::get_instance()->get_bg_groups(vecgroups);
566 for (std::vector
<Glib::ustring
>::iterator i
= vecgroups
.begin(); i
!=vecgroups
.end(); i
++)
569 // SetBG::SetMode mode;
572 // ret = Config::get_instance()->get_bg(*i, file, mode, bgcolor);
575 std::cerr
<< "(load_map_setbgs) Could not get background stored info for " << *i
<< "\n";
579 map_setbgs
[*i
] = file
;
586 * Called when a file in a directory being monitored by inotify is deleted.
587 * Removes the file from the tree view.
589 * @param filename The name of the file to remove.
591 void Thumbview::file_deleted_callback(std::string filename
) {
592 if (watches
.find(filename
) != watches
.end()) {
593 watches
.erase(filename
);
597 Gtk::TreeModel::Children children
= store
->children();
598 for (iter
= children
.begin(); iter
!= children
.end(); iter
++) {
599 Glib::ustring this_filename
= (*iter
)[record
.Filename
];
600 if (this_filename
== filename
) {
609 * Called when a file in a directory being monitored by inotify is modified
610 * or a new file is created. Adds the file to the tree view.
612 * @param filename The name of the file modified or created.
614 void Thumbview::file_changed_callback(std::string filename
) {
615 // first remove the old instance of this file.
616 file_deleted_callback(filename
);
617 if ( Glib::file_test(filename
, Glib::FILE_TEST_IS_DIR
) ) {
618 // this is a directory; use load_dir() to set us up the bomb.
620 } else if (is_image(filename
)) {
624 // restart the idle function
625 //Glib::signal_idle().connect(sigc::mem_fun(this, &Thumbview::load_cache_images));
629 * Called when a new file or directory is created in a directory being
630 * monitored. Discards the event for non-directories, because
631 * file_changed_callback will be called for those.
633 * @param filename The name of the file modified or created.
635 void Thumbview::file_created_callback(std::string filename
) {
636 if ( Glib::file_test(filename
, Glib::FILE_TEST_IS_DIR
) ) {
637 file_changed_callback(filename
);
This page took 0.057698 seconds and 3 git commands to generate.