/* * 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 . * * Class created by Jean-Christophe FRISCH, aka 'Hombre' */ #include #include "guiutils.h" #include "multilangmgr.h" #include "popupcommon.h" #include "rtimage.h" #include "threadutils.h" PopUpCommon::PopUpCommon (Gtk::Button* thisButton, const Glib::ustring& label) : buttonImage (nullptr) , menu(new Gtk::Menu()) , selected (-1) // -1 means that the button is invalid { button = thisButton; hasMenu = false; imageContainer = Gtk::manage( new Gtk::Grid()); setExpandAlignProperties(imageContainer, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); button->set_relief (Gtk::RELIEF_NORMAL); button->add(*imageContainer); if (!label.empty()) { Gtk::Label* buttonLabel = Gtk::manage ( new Gtk::Label(label + " ") ); setExpandAlignProperties(buttonLabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); imageContainer->attach(*buttonLabel, 0, 0, 1, 1); } // Create the global container and put the button in it buttonGroup = Gtk::manage( new Gtk::Grid()); setExpandAlignProperties(buttonGroup, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); buttonGroup->attach(*button, 0, 0, 1, 1); buttonGroup->get_style_context()->add_class("image-combo"); // Create the image for the button buttonImage = Gtk::manage(new RTImage()); setExpandAlignProperties(buttonImage, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); imageContainer->attach_next_to(*buttonImage, Gtk::POS_RIGHT, 1, 1); buttonImage->set_no_show_all(); // Create the button for showing the pop-up. arrowButton = Gtk::manage(new Gtk::Button()); Gtk::Image *arrowImage = Gtk::manage(new Gtk::Image()); arrowImage->set_from_icon_name("pan-down-symbolic", Gtk::ICON_SIZE_BUTTON); setExpandAlignProperties(arrowButton, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL); arrowButton->add(*arrowImage); //menuSymbol); arrowImage->show(); buttonGroup->attach_next_to(*arrowButton, *button, Gtk::POS_RIGHT, 1, 1); arrowButton->signal_button_release_event().connect_notify(sigc::mem_fun(*this, &PopUpCommon::showMenu)); arrowButton->get_style_context()->add_class("Right"); arrowButton->get_style_context()->add_class("popupbutton-arrow"); arrowButton->set_no_show_all(); } PopUpCommon::~PopUpCommon () { } bool PopUpCommon::addEntry (const Glib::ustring& fileName, const Glib::ustring& label, Gtk::RadioButtonGroup* radioGroup) { return insertEntry(getEntryCount(), fileName, label, radioGroup); } bool PopUpCommon::insertEntry(int position, const Glib::ustring& iconName, const Glib::ustring& label, Gtk::RadioButtonGroup *radioGroup) { RTImage* image = nullptr; if (!iconName.empty()) { image = Gtk::manage(new RTImage(iconName)); } bool success = insertEntryImpl(position, iconName, Glib::RefPtr(), image, label, radioGroup); if (!success && image) { delete image; } return success; } bool PopUpCommon::insertEntry(int position, const Glib::RefPtr& gIcon, const Glib::ustring& label, Gtk::RadioButtonGroup *radioGroup) { auto image = Gtk::manage(new RTImage(gIcon, Gtk::ICON_SIZE_BUTTON)); bool success = insertEntryImpl(position, "", gIcon, image, label, radioGroup); if (!success) { delete image; } return success; } bool PopUpCommon::insertEntryImpl(int position, const Glib::ustring& iconName, const Glib::RefPtr& gIcon, RTImage* image, const Glib::ustring& label, Gtk::RadioButtonGroup* radioGroup) { if (label.empty() || position < 0 || position > getEntryCount()) return false; // Create the menu item and image Gtk::MenuItem *newItem; if (radioGroup) { newItem = Gtk::manage(new MyRadioImageMenuItem(label, image, *radioGroup)); } else { newItem = Gtk::manage(new MyImageMenuItem(label, image)); } imageIcons.insert(imageIcons.begin() + position, gIcon); imageIconNames.insert(imageIconNames.begin() + position, iconName); images.insert(images.begin() + position, image); // When there is at least 1 choice, we add the arrow button if (images.size() == 1) { changeImage(iconName, gIcon); buttonImage->show(); selected = 0; button->get_style_context()->add_class("Left"); arrowButton->show(); hasMenu = true; } else if (position <= selected) { selected++; } void (PopUpCommon::*entrySelectedFunc)(Gtk::Widget *) = &PopUpCommon::entrySelected; newItem->signal_activate ().connect (sigc::bind (sigc::mem_fun (*this, entrySelectedFunc), newItem)); menu->insert(*newItem, position); return true; } void PopUpCommon::setEmptyImage(const Glib::ustring &fileName) { emptyImageFilename = fileName; if (getEntryCount()) { return; } if (fileName.empty()) { buttonImage->hide(); } else { changeImage(emptyImageFilename, Glib::RefPtr()); buttonImage->show(); } } void PopUpCommon::removeEntry(int position) { if (position < 0 || position >= getEntryCount()) { return; } if (getEntryCount() == 1) { // Last of the entries. // Hide the arrow button. button->get_style_context()->remove_class("Left"); arrowButton->hide(); hasMenu = false; if (emptyImageFilename.empty()) { // Remove the button image. buttonImage->hide(); } else { // Show the empty icon. changeImage(emptyImageFilename, Glib::RefPtr()); } selected = -1; } else if (position < selected) { selected--; } else if (position == selected) { // Select a different entry before removing. int nextSelection = position + (position == getEntryCount() - 1 ? -1 : 1); changeImage(nextSelection); setButtonHint(); } std::unique_ptr menuItem(menu->get_children()[position]); menu->remove(*menuItem); imageIcons.erase(imageIcons.begin() + position); imageIconNames.erase(imageIconNames.begin() + position); images.erase(images.begin() + position); } void PopUpCommon::changeImage(int position) { changeImage(imageIconNames.at(position), imageIcons.at(position)); } void PopUpCommon::changeImage(const Glib::ustring& iconName, const Glib::RefPtr& gIcon) { if (!iconName.empty()) { buttonImage->set_from_icon_name(iconName, Gtk::ICON_SIZE_BUTTON); } else { buttonImage->set_from_gicon(gIcon, Gtk::ICON_SIZE_BUTTON); } } void PopUpCommon::entrySelected(Gtk::Widget* widget) { if (widget != menu->get_active()) { // Not actually selected. return; } if (!entrySelectionMutex.trylock()) { // Already being updated. return; } entrySelectionMutex.unlock(); int i = 0; for (const auto & child : menu->get_children()) { if (widget == child) { break; } i++; } entrySelected(i); } void PopUpCommon::entrySelected(int i) { // Emit a signal if the selected item has changed if (setSelected (posToIndex(i))) messageChanged (posToIndex(selected)); // Emit a signal in all case (i.e. propagate the signal_activate event) messageItemSelected (posToIndex(selected)); } void PopUpCommon::setItemSensitivity (int index, bool isSensitive) { const auto items = menu->get_children (); size_t pos = indexToPos(index); if (pos < items.size ()) { items[pos]->set_sensitive (isSensitive); } } /* * Set the button image with the selected item */ bool PopUpCommon::setSelected (int entryNum) { entryNum = indexToPos(entryNum); if (entryNum < 0 || entryNum > ((int)images.size() - 1) || (int)entryNum == selected) { return false; } else { // Maybe we could do something better than loading the image file each time the selection is changed !? changeImage(entryNum); selected = entryNum; setButtonHint(); auto radioMenuItem = dynamic_cast(menu->get_children()[entryNum]); if (radioMenuItem && !radioMenuItem->get_active()) { MyMutex::MyLock updateLock(entrySelectionMutex); radioMenuItem->set_active(); } return true; } } void PopUpCommon::show() { menu->reposition(); setButtonHint(); menu->show_all(); buttonGroup->show_all(); } void PopUpCommon::setButtonHint() { Glib::ustring hint; if (!buttonHint.empty()) { hint = buttonHint; if (selected > -1) { hint += " "; } } if (selected > -1) { auto widget = menu->get_children ()[selected]; auto item = dynamic_cast(widget); if (item) { hint += escapeHtmlChars(item->getLabel()->get_text()); } } button->set_tooltip_markup(hint); } void PopUpCommon::showMenu(GdkEventButton* event) { if (event->button == 1) { menu->popup(event->button, event->time); } } void PopUpCommon::set_tooltip_text (const Glib::ustring &text) { buttonHint = text; setButtonHint(); }