/* * This file is part of RawTherapee. * * Copyright (c) 2004-2010 Gabor Horvath * * RawTherapee 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 3 of the License, or * (at your option) any later version. * * RawTherapee 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 RawTherapee. If not, see . */ #include "placesbrowser.h" #ifdef _WIN32 #include #include #include #endif #include "guiutils.h" #include "rtimage.h" #include "options.h" #include "toolpanel.h" PlacesBrowser::PlacesBrowser () { set_orientation(Gtk::ORIENTATION_VERTICAL); scrollw = Gtk::manage (new Gtk::ScrolledWindow ()); scrollw->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); pack_start (*scrollw); // Since Gtk3, we can't have image+text buttons natively. We'll comply to the Gtk guidelines and choose one of them (icons here) add = Gtk::manage (new Gtk::Button ()); add->set_tooltip_text(M("MAIN_FRAME_PLACES_ADD")); setExpandAlignProperties(add, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); //add->get_style_context()->set_junction_sides(Gtk::JUNCTION_RIGHT); add->get_style_context()->add_class("Left"); add->set_image (*Gtk::manage (new RTImage ("add-small.png"))); del = Gtk::manage (new Gtk::Button ()); del->set_tooltip_text(M("MAIN_FRAME_PLACES_DEL")); setExpandAlignProperties(del, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); //del->get_style_context()->set_junction_sides(Gtk::JUNCTION_LEFT); del->get_style_context()->add_class("Right"); del->set_image (*Gtk::manage (new RTImage ("remove-small.png"))); Gtk::Grid* buttonBox = Gtk::manage (new Gtk::Grid ()); buttonBox->set_orientation(Gtk::ORIENTATION_HORIZONTAL); buttonBox->attach_next_to(*add, Gtk::POS_LEFT, 1, 1); buttonBox->attach_next_to(*del, *add, Gtk::POS_RIGHT, 1, 1); pack_start (*buttonBox, Gtk::PACK_SHRINK, 2); treeView = Gtk::manage (new Gtk::TreeView ()); treeView->set_can_focus(false); scrollw->add (*treeView); placesModel = Gtk::ListStore::create (placesColumns); treeView->set_model (placesModel); treeView->set_headers_visible (true); Gtk::TreeView::Column *iviewcol = Gtk::manage (new Gtk::TreeView::Column (M("MAIN_FRAME_PLACES"))); Gtk::CellRendererPixbuf *iconCR = Gtk::manage (new Gtk::CellRendererPixbuf()); Gtk::CellRendererText *labelCR = Gtk::manage (new Gtk::CellRendererText()); labelCR->property_ellipsize() = Pango::ELLIPSIZE_MIDDLE; iviewcol->pack_start (*iconCR, false); iviewcol->pack_start (*labelCR, true); iviewcol->add_attribute (*iconCR, "gicon", 0); iviewcol->add_attribute (*labelCR, "text", placesColumns.label); treeView->append_column (*iviewcol); treeView->set_row_separator_func (sigc::mem_fun(*this, &PlacesBrowser::rowSeparatorFunc)); vm = Gio::VolumeMonitor::get(); vm->signal_mount_changed().connect (sigc::mem_fun(*this, &PlacesBrowser::mountChanged)); vm->signal_mount_added().connect (sigc::mem_fun(*this, &PlacesBrowser::mountChanged)); vm->signal_mount_removed().connect (sigc::mem_fun(*this, &PlacesBrowser::mountChanged)); vm->signal_volume_changed().connect (sigc::mem_fun(*this, &PlacesBrowser::volumeChanged)); vm->signal_volume_added().connect (sigc::mem_fun(*this, &PlacesBrowser::volumeChanged)); vm->signal_volume_removed().connect (sigc::mem_fun(*this, &PlacesBrowser::volumeChanged)); vm->signal_drive_connected().connect (sigc::mem_fun(*this, &PlacesBrowser::driveChanged)); vm->signal_drive_disconnected().connect (sigc::mem_fun(*this, &PlacesBrowser::driveChanged)); vm->signal_drive_changed().connect (sigc::mem_fun(*this, &PlacesBrowser::driveChanged)); treeView->get_selection()->signal_changed().connect(sigc::mem_fun(*this, &PlacesBrowser::selectionChanged)); add->signal_clicked().connect(sigc::mem_fun(*this, &PlacesBrowser::addPressed)); del->signal_clicked().connect(sigc::mem_fun(*this, &PlacesBrowser::delPressed)); show_all (); } // For drive letter comparison bool compareMountByRoot (Glib::RefPtr a, Glib::RefPtr b) { return a->get_root()->get_parse_name() < b->get_root()->get_parse_name(); } void PlacesBrowser::refreshPlacesList () { placesModel->clear (); // append favorites for (size_t i = 0; i < options.favoriteDirs.size(); i++) { Glib::RefPtr fav = Gio::File::create_for_path (options.favoriteDirs[i]); if (fav && fav->query_exists()) { try { if (auto info = fav->query_info ()) { Gtk::TreeModel::Row newrow = *(placesModel->append()); newrow[placesColumns.label] = info->get_display_name (); newrow[placesColumns.icon] = info->get_icon (); newrow[placesColumns.root] = fav->get_parse_name (); newrow[placesColumns.type] = 5; newrow[placesColumns.rowSeparator] = false; } } catch(Gio::Error&) {} } } // append home directory Glib::RefPtr hfile = Gio::File::create_for_path (userHomeDir()); // Will send back "My documents" on Windows now, which has no restricted access if (!placesModel->children().empty()) { Gtk::TreeModel::Row newrow = *(placesModel->append()); newrow[placesColumns.rowSeparator] = true; } if (hfile && hfile->query_exists()) { try { if (auto info = hfile->query_info ()) { Gtk::TreeModel::Row newrow = *(placesModel->append()); newrow[placesColumns.label] = info->get_display_name (); newrow[placesColumns.icon] = info->get_icon (); newrow[placesColumns.root] = hfile->get_parse_name (); newrow[placesColumns.type] = 4; newrow[placesColumns.rowSeparator] = false; } } catch (Gio::Error&) {} } // append pictures directory hfile = Gio::File::create_for_path (userPicturesDir()); if (hfile && hfile->query_exists()) { try { if (auto info = hfile->query_info ()) { Gtk::TreeModel::Row newrow = *(placesModel->append()); newrow[placesColumns.label] = info->get_display_name (); newrow[placesColumns.icon] = info->get_icon (); newrow[placesColumns.root] = hfile->get_parse_name (); newrow[placesColumns.type] = 4; newrow[placesColumns.rowSeparator] = false; } } catch (Gio::Error&) {} } if (!placesModel->children().empty()) { Gtk::TreeModel::Row newrow = *(placesModel->append()); newrow[placesColumns.rowSeparator] = true; } // scan all drives std::vector > drives = vm->get_connected_drives (); for (size_t j = 0; j < drives.size (); j++) { std::vector > volumes = drives[j]->get_volumes (); if (volumes.empty()) { Gtk::TreeModel::Row newrow = *(placesModel->append()); newrow[placesColumns.label] = drives[j]->get_name (); newrow[placesColumns.icon] = drives[j]->get_icon (); newrow[placesColumns.root] = ""; newrow[placesColumns.type] = 3; newrow[placesColumns.rowSeparator] = false; } for (size_t i = 0; i < volumes.size (); i++) { Glib::RefPtr mount = volumes[i]->get_mount (); if (mount) { // placesed volumes Gtk::TreeModel::Row newrow = *(placesModel->append()); newrow[placesColumns.label] = mount->get_name (); newrow[placesColumns.icon] = mount->get_icon (); newrow[placesColumns.root] = mount->get_root ()->get_parse_name (); newrow[placesColumns.type] = 1; newrow[placesColumns.rowSeparator] = false; } else { // unplacesed volumes Gtk::TreeModel::Row newrow = *(placesModel->append()); newrow[placesColumns.label] = volumes[i]->get_name (); newrow[placesColumns.icon] = volumes[i]->get_icon (); newrow[placesColumns.root] = ""; newrow[placesColumns.type] = 2; newrow[placesColumns.rowSeparator] = false; } } } // volumes not belonging to drives std::vector > volumes = vm->get_volumes (); for (size_t i = 0; i < volumes.size (); i++) { if (!volumes[i]->get_drive ()) { Glib::RefPtr mount = volumes[i]->get_mount (); if (mount) { // placesed volumes Gtk::TreeModel::Row newrow = *(placesModel->append()); newrow[placesColumns.label] = mount->get_name (); newrow[placesColumns.icon] = mount->get_icon (); newrow[placesColumns.root] = mount->get_root ()->get_parse_name (); newrow[placesColumns.type] = 1; newrow[placesColumns.rowSeparator] = false; } else { // unplacesed volumes Gtk::TreeModel::Row newrow = *(placesModel->append()); newrow[placesColumns.label] = volumes[i]->get_name (); newrow[placesColumns.icon] = volumes[i]->get_icon (); newrow[placesColumns.root] = ""; newrow[placesColumns.type] = 2; newrow[placesColumns.rowSeparator] = false; } } } // places not belonging to volumes // (Drives in Windows) std::vector > mounts = vm->get_mounts (); #ifdef _WIN32 // on Windows, it's usual to sort by drive letter, not by name std::sort (mounts.begin(), mounts.end(), compareMountByRoot); #endif for (size_t i = 0; i < mounts.size (); i++) { if (!mounts[i]->get_volume ()) { Gtk::TreeModel::Row newrow = *(placesModel->append()); newrow[placesColumns.label] = mounts[i]->get_name (); newrow[placesColumns.icon] = mounts[i]->get_icon (); newrow[placesColumns.root] = mounts[i]->get_root ()->get_parse_name (); newrow[placesColumns.type] = 1; newrow[placesColumns.rowSeparator] = false; } } } bool PlacesBrowser::rowSeparatorFunc (const Glib::RefPtr& model, const Gtk::TreeModel::iterator& iter) { return iter->get_value (placesColumns.rowSeparator); } void PlacesBrowser::mountChanged (const Glib::RefPtr& m) { GThreadLock lock; refreshPlacesList (); } void PlacesBrowser::volumeChanged (const Glib::RefPtr& m) { GThreadLock lock; refreshPlacesList (); } void PlacesBrowser::driveChanged (const Glib::RefPtr& m) { GThreadLock lock; refreshPlacesList (); } void PlacesBrowser::selectionChanged () { Glib::RefPtr selection = treeView->get_selection(); Gtk::TreeModel::iterator iter = selection->get_selected(); if (iter) { if (iter->get_value (placesColumns.type) == 2) { std::vector > volumes = vm->get_volumes (); for (size_t i = 0; i < volumes.size(); i++) if (volumes[i]->get_name () == iter->get_value (placesColumns.label)) { volumes[i]->mount (); break; } } else if (iter->get_value (placesColumns.type) == 3) { std::vector > drives = vm->get_connected_drives (); for (size_t i = 0; i < drives.size(); i++) if (drives[i]->get_name () == iter->get_value (placesColumns.label)) { drives[i]->poll_for_media (); break; } } else if (selectDir) { selectDir (iter->get_value (placesColumns.root)); } } } void PlacesBrowser::dirSelected (const Glib::ustring& dirname, const Glib::ustring& openfile) { lastSelectedDir = dirname; } void PlacesBrowser::addPressed () { if (lastSelectedDir.empty()) { return; } // check if the dirname is already in the list. If yes, return. for (size_t i = 0; i < options.favoriteDirs.size(); i++) if (options.favoriteDirs[i] == lastSelectedDir) { return; } // append Glib::RefPtr hfile = Gio::File::create_for_path (lastSelectedDir); if (hfile && hfile->query_exists()) { try { if (auto info = hfile->query_info ()) { options.favoriteDirs.push_back (hfile->get_parse_name ()); refreshPlacesList (); } } catch(Gio::Error&) {} } } void PlacesBrowser::delPressed () { // lookup the selected item in the bookmark Glib::RefPtr selection = treeView->get_selection(); Gtk::TreeModel::iterator iter = selection->get_selected(); if (iter && iter->get_value (placesColumns.type) == 5) { std::vector::iterator i = std::find (options.favoriteDirs.begin(), options.favoriteDirs.end(), iter->get_value (placesColumns.root)); if (i != options.favoriteDirs.end()) { options.favoriteDirs.erase (i); } } refreshPlacesList (); } Glib::ustring PlacesBrowser::userHomeDir () { #ifdef _WIN32 // get_home_dir crashes on some Windows configurations, // so we rather use the safe native functions here. WCHAR pathW[MAX_PATH]; if (SHGetSpecialFolderPathW (NULL, pathW, CSIDL_PERSONAL, false)) { char pathA[MAX_PATH]; if (WideCharToMultiByte (CP_UTF8, 0, pathW, -1, pathA, MAX_PATH, 0, 0)) { return Glib::ustring (pathA); } } return Glib::ustring ("C:\\"); #else return Glib::get_home_dir (); #endif } Glib::ustring PlacesBrowser::userPicturesDir () { #ifdef _WIN32 // get_user_special_dir crashes on some Windows configurations, // so we rather use the safe native functions here. WCHAR pathW[MAX_PATH]; if (SHGetSpecialFolderPathW (NULL, pathW, CSIDL_MYPICTURES, false)) { char pathA[MAX_PATH]; if (WideCharToMultiByte (CP_UTF8, 0, pathW, -1, pathA, MAX_PATH, 0, 0)) { return Glib::ustring (pathA); } } return Glib::ustring ("C:\\"); #else return Glib::get_user_special_dir (G_USER_DIRECTORY_PICTURES); #endif }