rawTherapee/rtgui/popupcommon.cc
Lawrence Lee 22edf5f069
Add radio indicator to external editor selector
Make it easier to see that the selector is for changing the current
editor and not for immediately sending the image to the clicked-on
editor.
2023-01-15 15:10:54 -08:00

284 lines
9.0 KiB
C++

/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* 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 <https://www.gnu.org/licenses/>.
*
* Class created by Jean-Christophe FRISCH, aka 'Hombre'
*/
#include <gtkmm.h>
#include "multilangmgr.h"
#include "popupcommon.h"
#include "rtimage.h"
#include "guiutils.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& fileName, const Glib::ustring& label, Gtk::RadioButtonGroup *radioGroup)
{
RTImage* image = nullptr;
if (!fileName.empty()) {
image = Gtk::manage(new RTImage(fileName));
}
bool success = insertEntryImpl(position, fileName, Glib::RefPtr<const Gio::Icon>(), image, label, radioGroup);
if (!success && image) {
delete image;
}
return success;
}
bool PopUpCommon::insertEntry(int position, const Glib::RefPtr<const Gio::Icon>& gIcon, const Glib::ustring& label, Gtk::RadioButtonGroup *radioGroup)
{
RTImage* 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& fileName, const Glib::RefPtr<const Gio::Icon>& 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);
imageFilenames.insert(imageFilenames.begin() + position, fileName);
images.insert(images.begin() + position, image);
// When there is at least 1 choice, we add the arrow button
if (images.size() == 1) {
changeImage(fileName, 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::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;
// Remove the button image.
buttonImage->hide();
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<Gtk::Widget> menuItem(menu->get_children()[position]);
menu->remove(*menuItem);
imageIcons.erase(imageIcons.begin() + position);
imageFilenames.erase(imageFilenames.begin() + position);
images.erase(images.begin() + position);
}
void PopUpCommon::changeImage(int position)
{
changeImage(imageFilenames.at(position), imageIcons.at(position));
}
void PopUpCommon::changeImage(const Glib::ustring& fileName, const Glib::RefPtr<const Gio::Icon>& gIcon)
{
if (!fileName.empty()) {
buttonImage->changeImage(fileName);
} else {
buttonImage->changeImage(gIcon, static_cast<Gtk::IconSize>(Gtk::ICON_SIZE_BUTTON));
}
}
void PopUpCommon::entrySelected(Gtk::Widget* widget)
{
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<Gtk::RadioMenuItem*>(menu->get_children()[entryNum]);
if (radioMenuItem && menu->get_active() != radioMenuItem) {
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<MyImageMenuItemInterface*>(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();
}