/*
* This file is part of RawTherapee.
*
*
* 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 "../rtengine/rt_math.h"
#include "guiutils.h"
#include "options.h"
#include "../rtengine/rt_math.h"
#include "../rtengine/utils.h"
#include "../rtengine/safegtk.h"
#include "rtimage.h"
#include "multilangmgr.h"
#include
using namespace std;
#if TRACE_MYRWMUTEX==1 && !defined NDEBUG
unsigned int MyReaderLock::readerLockCounter = 0;
unsigned int MyWriterLock::writerLockCounter = 0;
#endif
Glib::RefPtr MyExpander::inconsistentPBuf;
Glib::RefPtr MyExpander::enabledPBuf;
Glib::RefPtr MyExpander::disabledPBuf;
Glib::RefPtr MyExpander::openedPBuf;
Glib::RefPtr MyExpander::closedPBuf;
Glib::ustring escapeHtmlChars(const Glib::ustring &src) {
// Sources chars to be escaped
static const Glib::ustring srcChar("&<>");
// Destination strings, in the same order than the source
static std::vector dstChar(3);
dstChar.at(0) = "&";
dstChar.at(1) = "<";
dstChar.at(2) = ">";
// Copying the original string, that will be modified
Glib::ustring dst(src);
// Iterating all chars of the copy of the source string
for (size_t i=0; i list = cont->get_children ();
Glib::ListHandle::iterator i = list.begin ();
for (; i!=list.end() && *i!=w; ++i);
if (i!=list.end()) {
if (increference)
w->reference ();
cont->remove (*w);
return true;
}
else
return false;
}
void thumbInterp (const unsigned char* src, int sw, int sh, unsigned char* dst, int dw, int dh) {
if (options.thumbInterp==0)
rtengine::nearestInterp (src, sw, sh, dst, dw, dh);
else if (options.thumbInterp==1)
rtengine::bilinearInterp (src, sw, sh, dst, dw, dh);
}
Glib::ustring removeExtension (const Glib::ustring& filename) {
Glib::ustring bname = Glib::path_get_basename(filename);
size_t lastdot = bname.find_last_of ('.');
size_t lastwhitespace = bname.find_last_of (" \t\f\v\n\r");
if (lastdot!=bname.npos && (lastwhitespace==bname.npos || lastdot > lastwhitespace))
return filename.substr (0, filename.size()-(bname.size()-lastdot));
else
return filename;
}
Glib::ustring getExtension (const Glib::ustring& filename) {
Glib::ustring bname = Glib::path_get_basename(filename);
size_t lastdot = bname.find_last_of ('.');
size_t lastwhitespace = bname.find_last_of (" \t\f\v\n\r");
if (lastdot!=bname.npos && (lastwhitespace==bname.npos || lastdot > lastwhitespace))
return filename.substr (filename.size()-(bname.size()-lastdot)+1, filename.npos);
else
return "";
}
bool confirmOverwrite (Gtk::Window& parent, const std::string& filename) {
bool safe = true;
if (safe_file_test (filename, Glib::FILE_TEST_EXISTS)) {
Glib::ustring msg_ = Glib::ustring ("\"") + Glib::path_get_basename (filename) + "\": "
+ M("MAIN_MSG_ALREADYEXISTS") + "\n" + M("MAIN_MSG_QOVERWRITE");
Gtk::MessageDialog msgd (parent, msg_, true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true);
safe = (msgd.run () == Gtk::RESPONSE_YES);
}
return safe;
}
void writeFailed (Gtk::Window& parent, const std::string& filename) {
Glib::ustring msg_ = Glib::ustring::compose(M("MAIN_MSG_WRITEFAILED"), filename);
Gtk::MessageDialog msgd (parent, msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
msgd.run ();
}
void drawCrop (Cairo::RefPtr cr, int imx, int imy, int imw, int imh, int startx, int starty, double scale, const rtengine::procparams::CropParams& cparams, bool drawGuide, bool useBgColor, bool fullImageVisible) {
cr->set_line_width (0.);
cr->rectangle (imx, imy, imw, imh);
cr->clip ();
double c1x = (cparams.x-startx)*scale;
double c1y = (cparams.y-starty)*scale;
double c2x = (cparams.x+cparams.w-startx)*scale - (fullImageVisible ? 0.0 : 1.0);
double c2y = (cparams.y+cparams.h-starty)*scale - (fullImageVisible ? 0.0 : 1.0);
// crop overlay color, linked with crop windows background
if (options.bgcolor==0 || !useBgColor)
cr->set_source_rgba (options.cutOverlayBrush[0], options.cutOverlayBrush[1], options.cutOverlayBrush[2], options.cutOverlayBrush[3]);
else if (options.bgcolor==1)
cr->set_source_rgb (0,0,0);
else if (options.bgcolor==2)
cr->set_source_rgb (1,1,1);
cr->rectangle (imx, imy, imw+0.5, round(c1y)+0.5);
cr->rectangle (imx, round(imy+c2y)+0.5, imw+0.5, round(imh-c2y)+0.5);
cr->rectangle (imx, round(imy+c1y)+0.5, round(c1x)+0.5, round(c2y-c1y+1)+0.5);
cr->rectangle (round(imx+c2x)+0.5, round(imy+c1y)+0.5, round(imw-c2x)+0.5, round(c2y-c1y+1)+0.5);
cr->fill ();
// rectangle around the cropped area and guides
if (cparams.guide!="None" && drawGuide) {
double rectx1 = round(c1x) + imx + 0.5;
double recty1 = round(c1y) + imy + 0.5;
double rectx2 = round(c2x) + imx + 0.5;
double recty2 = round(c2y) + imy + 0.5;
if(fullImageVisible) {
rectx2 = min(rectx2, imx+imw-0.5);
recty2 = min(recty2, imy+imh-0.5);
}
cr->set_line_width (1.0);
cr->set_source_rgba (1.0, 1.0, 1.0, 0.618);
cr->move_to (rectx1, recty1);
cr->line_to (rectx2, recty1);
cr->line_to (rectx2, recty2);
cr->line_to (rectx1, recty2);
cr->line_to (rectx1, recty1);
cr->stroke ();
cr->set_source_rgba (0.0, 0.0, 0.0, 0.618);
std::valarray ds (1);
ds[0] = 4;
cr->set_dash (ds, 0);
cr->move_to (rectx1, recty1);
cr->line_to (rectx2, recty1);
cr->line_to (rectx2, recty2);
cr->line_to (rectx1, recty2);
cr->line_to (rectx1, recty1);
cr->stroke ();
ds.resize (0);
cr->set_dash (ds, 0);
if (cparams.guide!="Rule of diagonals" && cparams.guide!="Golden Triangle 1" && cparams.guide!="Golden Triangle 2") {
// draw guide lines
std::vector horiz_ratios;
std::vector vert_ratios;
if (cparams.guide=="Rule of thirds") {
horiz_ratios.push_back (1.0/3.0);
horiz_ratios.push_back (2.0/3.0);
vert_ratios.push_back (1.0/3.0);
vert_ratios.push_back (2.0/3.0);
}
else if (!strncmp(cparams.guide.data(),"Harmonic means",14)) {
horiz_ratios.push_back (1.0-0.618);
horiz_ratios.push_back (0.618);
vert_ratios.push_back (0.618);
vert_ratios.push_back (1.0-0.618);
}
else if (cparams.guide=="Grid") {
// To have even distribution, normalize it a bit
const int longSideNumLines=10;
int w=rectx2-rectx1, h=recty2-recty1;
if (w>longSideNumLines && h>longSideNumLines) {
if (w>h) {
for (int i=1;iset_source_rgba (1.0, 1.0, 1.0, 0.618);
cr->move_to (rectx1, recty1 + round((recty2-recty1) * horiz_ratios[i]));
cr->line_to (rectx2, recty1 + round((recty2-recty1) * horiz_ratios[i]));
cr->stroke ();
cr->set_source_rgba (0.0, 0.0, 0.0, 0.618);
std::valarray ds (1);
ds[0] = 4;
cr->set_dash (ds, 0);
cr->move_to (rectx1, recty1 + round((recty2-recty1) * horiz_ratios[i]));
cr->line_to (rectx2, recty1 + round((recty2-recty1) * horiz_ratios[i]));
cr->stroke ();
ds.resize (0);
cr->set_dash (ds, 0);
}
// Verticals
for (size_t i=0; iset_source_rgba (1.0, 1.0, 1.0, 0.618);
cr->move_to (rectx1 + round((rectx2-rectx1) * vert_ratios[i]), recty1);
cr->line_to (rectx1 + round((rectx2-rectx1) * vert_ratios[i]), recty2);
cr->stroke ();
cr->set_source_rgba (0.0, 0.0, 0.0, 0.618);
std::valarray ds (1);
ds[0] = 4;
cr->set_dash (ds, 0);
cr->move_to (rectx1 + round((rectx2-rectx1) * vert_ratios[i]), recty1);
cr->line_to (rectx1 + round((rectx2-rectx1) * vert_ratios[i]), recty2);
cr->stroke ();
ds.resize (0);
cr->set_dash (ds, 0);
}
}
else if (cparams.guide=="Rule of diagonals") {
double corners_from[4][2];
double corners_to[4][2];
int mindim = min(rectx2-rectx1, recty2-recty1);
corners_from[0][0] = rectx1;
corners_from[0][1] = recty1;
corners_to[0][0] = rectx1 + mindim;
corners_to[0][1] = recty1 + mindim;
corners_from[1][0] = rectx1;
corners_from[1][1] = recty2;
corners_to[1][0] = rectx1 + mindim;
corners_to[1][1] = recty2 - mindim;
corners_from[2][0] = rectx2;
corners_from[2][1] = recty1;
corners_to[2][0] = rectx2 - mindim;
corners_to[2][1] = recty1 + mindim;
corners_from[3][0] = rectx2;
corners_from[3][1] = recty2;
corners_to[3][0] = rectx2 - mindim;
corners_to[3][1] = recty2 - mindim;
for (int i=0; i<4; i++) {
cr->set_source_rgba (1.0, 1.0, 1.0, 0.618);
cr->move_to (corners_from[i][0], corners_from[i][1]);
cr->line_to (corners_to[i][0], corners_to[i][1]);
cr->stroke ();
cr->set_source_rgba (0.0, 0.0, 0.0, 0.618);
std::valarray ds (1);
ds[0] = 4;
cr->set_dash (ds, 0);
cr->move_to (corners_from[i][0], corners_from[i][1]);
cr->line_to (corners_to[i][0], corners_to[i][1]);
cr->stroke ();
ds.resize (0);
cr->set_dash (ds, 0);
}
} else if (cparams.guide=="Golden Triangle 1" || cparams.guide=="Golden Triangle 2") {
// main diagonal
if(cparams.guide=="Golden Triangle 2") {
std::swap(rectx1,rectx2);
}
cr->set_source_rgba (1.0, 1.0, 1.0, 0.618);
cr->move_to (rectx1, recty1);
cr->line_to (rectx2, recty2);
cr->stroke ();
cr->set_source_rgba (0.0, 0.0, 0.0, 0.618);
std::valarray ds (1);
ds[0] = 4;
cr->set_dash (ds, 0);
cr->move_to (rectx1, recty1);
cr->line_to (rectx2, recty2);
cr->stroke ();
ds.resize (0);
cr->set_dash (ds, 0);
double height = recty2 - recty1;
double width = rectx2 - rectx1;
double d = sqrt(height*height + width*width);
double alpha = asin(width/d);
double beta = asin(height/d);
double a = sin(beta) * height;
double b = sin(alpha) * height;
double x = (a*b)/height;
double y = height - (b*(d-a))/width;
cr->set_source_rgba (1.0, 1.0, 1.0, 0.618);
cr->move_to (rectx1, recty2);
cr->line_to (rectx1+x, recty1+y);
cr->stroke ();
cr->set_source_rgba (0.0, 0.0, 0.0, 0.618);
ds.resize (1);
ds[0] = 4;
cr->set_dash (ds, 0);
cr->move_to (rectx1, recty2);
cr->line_to (rectx1+x, recty1+y);
cr->stroke ();
ds.resize (0);
cr->set_dash (ds, 0);
x = width - (a*b)/height;
y = (b*(d-a))/width;
cr->set_source_rgba (1.0, 1.0, 1.0, 0.618);
cr->move_to (rectx2, recty1);
cr->line_to (rectx1+x, recty1+y);
cr->stroke ();
cr->set_source_rgba (0.0, 0.0, 0.0, 0.618);
ds.resize (1);
ds[0] = 4;
cr->set_dash (ds, 0);
cr->move_to (rectx2, recty1);
cr->line_to (rectx1+x, recty1+y);
cr->stroke ();
ds.resize (0);
cr->set_dash (ds, 0);
}
}
cr->reset_clip ();
}
bool ExpanderBox::on_expose_event(GdkEventExpose* event) {
bool retVal = Gtk::EventBox::on_expose_event(event);
if (!options.useSystemTheme) {
Glib::RefPtr window = get_window();
Glib::RefPtr style = get_style ();
Cairo::RefPtr cr = window->create_cairo_context();
int x_, y_, w_, h_, foo;
window->get_geometry(x_, y_, w_, h_, foo);
double x = 0.;
double y = 0.;
double w = double(w_);
double h = double(h_);
cr->set_antialias (Cairo::ANTIALIAS_NONE);
// draw a frame
cr->set_line_width (1.0);
Gdk::Color c = style->get_fg (Gtk::STATE_NORMAL);
cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p());
cr->move_to(x+0.5, y+0.5);
cr->line_to(x+w, y+0.5);
cr->line_to(x+w, y+h);
cr->line_to(x+0.5, y+h);
cr->line_to(x+0.5, y+0.5);
cr->stroke ();
}
return retVal;
}
ExpanderBox::ExpanderBox( Gtk::Container *p):pC(p) {
set_name ("ExpanderBox");
updateStyle();
}
void ExpanderBox::on_style_changed (const Glib::RefPtr& style) {
updateStyle();
}
void ExpanderBox::updateStyle() {
set_border_width(options.slimUI ? 2 : 8); // Outer space around the tool's frame 2:7
}
void ExpanderBox::show_all() {
// ask childs to show themselves, but not us (remain unchanged)
Gtk::Container::show_all_children(true);
}
void ExpanderBox::showBox() {
Gtk::EventBox::show();
}
void ExpanderBox::hideBox() {
Gtk::EventBox::hide();
}
void MyExpander::init() {
inconsistentPBuf = Gdk::Pixbuf::create_from_file(RTImage::findIconAbsolutePath("expanderInconsistent.png"));
enabledPBuf = Gdk::Pixbuf::create_from_file(RTImage::findIconAbsolutePath("expanderEnabled.png"));
disabledPBuf = Gdk::Pixbuf::create_from_file(RTImage::findIconAbsolutePath("expanderDisabled.png"));
openedPBuf = Gdk::Pixbuf::create_from_file(RTImage::findIconAbsolutePath("expanderOpened.png"));
closedPBuf = Gdk::Pixbuf::create_from_file(RTImage::findIconAbsolutePath("expanderClosed.png"));
}
MyExpander::MyExpander(bool useEnabled, Gtk::Widget* titleWidget) :
enabled(false), inconsistent(false), flushEvent(false), expBox(NULL),
child(NULL), headerWidget(NULL), statusImage(NULL),
label(NULL), useEnabled(useEnabled)
{
set_spacing(options.slimUI ? 0 : 2);
set_name("MyExpander");
set_can_focus(false);
headerHBox = Gtk::manage( new Gtk::HBox());
headerHBox->set_can_focus(false);
if (useEnabled) {
statusImage = Gtk::manage(new Gtk::Image(disabledPBuf));
imageEvBox = Gtk::manage(new Gtk::EventBox());
imageEvBox->add(*statusImage);
imageEvBox->set_above_child(true);
imageEvBox->signal_button_release_event().connect( sigc::mem_fun(this, & MyExpander::on_enabled_change) );
imageEvBox->signal_enter_notify_event().connect( sigc::mem_fun(this, & MyExpander::on_enter_leave_enable), false );
imageEvBox->signal_leave_notify_event().connect( sigc::mem_fun(this, & MyExpander::on_enter_leave_enable), false );
headerHBox->pack_start(*imageEvBox, Gtk::PACK_SHRINK, 0);
}
else {
statusImage = Gtk::manage(new Gtk::Image(openedPBuf));
headerHBox->pack_start(*statusImage, Gtk::PACK_SHRINK, 0);
}
statusImage->set_can_focus(false);
if (titleWidget) {
headerHBox->pack_start(*titleWidget, Gtk::PACK_EXPAND_WIDGET, 0);
headerWidget = titleWidget;
}
titleEvBox = Gtk::manage(new Gtk::EventBox());
titleEvBox->set_name("MyExpanderTitle");
titleEvBox->add(*headerHBox);
titleEvBox->set_above_child(false); // this is the key! By making it below the child, they will get the events first.
titleEvBox->set_can_focus(false);
pack_start(*titleEvBox, Gtk::PACK_EXPAND_WIDGET, 0);
updateStyle();
titleEvBox->signal_button_release_event().connect( sigc::mem_fun(this, & MyExpander::on_toggle) );
titleEvBox->signal_enter_notify_event().connect( sigc::mem_fun(this, & MyExpander::on_enter_leave_title), false);
titleEvBox->signal_leave_notify_event().connect( sigc::mem_fun(this, & MyExpander::on_enter_leave_title), false);
}
MyExpander::MyExpander(bool useEnabled, Glib::ustring titleLabel) :
enabled(false), inconsistent(false), flushEvent(false), expBox(NULL),
child(NULL), headerWidget(NULL), statusImage(NULL),
label(NULL), useEnabled(useEnabled)
{
set_spacing(options.slimUI ? 0 : 2);
set_name("MyExpander");
set_can_focus(false);
headerHBox = Gtk::manage( new Gtk::HBox());
headerHBox->set_can_focus(false);
if (useEnabled) {
statusImage = Gtk::manage(new Gtk::Image(disabledPBuf));
imageEvBox = Gtk::manage(new Gtk::EventBox());
imageEvBox->add(*statusImage);
imageEvBox->set_above_child(true);
imageEvBox->signal_button_release_event().connect( sigc::mem_fun(this, & MyExpander::on_enabled_change) );
imageEvBox->signal_enter_notify_event().connect( sigc::mem_fun(this, & MyExpander::on_enter_leave_enable), false );
imageEvBox->signal_leave_notify_event().connect( sigc::mem_fun(this, & MyExpander::on_enter_leave_enable), false );
headerHBox->pack_start(*imageEvBox, Gtk::PACK_SHRINK, 0);
}
else {
statusImage = Gtk::manage(new Gtk::Image(openedPBuf));
headerHBox->pack_start(*statusImage, Gtk::PACK_SHRINK, 0);
}
statusImage->set_can_focus(false);
Glib::ustring str("-");
if (!titleLabel.empty())
str = titleLabel;
label = Gtk::manage(new Gtk::Label());
label->set_alignment(Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER);
label->set_markup(Glib::ustring("") + escapeHtmlChars(titleLabel) + Glib::ustring(""));
headerHBox->pack_start(*label, Gtk::PACK_EXPAND_WIDGET, 0);
titleEvBox = Gtk::manage(new Gtk::EventBox());
titleEvBox->set_name("MyExpanderTitle");
titleEvBox->add(*headerHBox);
titleEvBox->set_above_child(false); // this is the key! By make it below the child, they will get the events first.
titleEvBox->set_can_focus(false);
pack_start(*titleEvBox, Gtk::PACK_EXPAND_WIDGET, 0);
updateStyle();
titleEvBox->signal_button_release_event().connect( sigc::mem_fun(this, & MyExpander::on_toggle));
titleEvBox->signal_enter_notify_event().connect( sigc::mem_fun(this, & MyExpander::on_enter_leave_title), false);
titleEvBox->signal_leave_notify_event().connect( sigc::mem_fun(this, & MyExpander::on_enter_leave_title), false);
}
bool MyExpander::on_enter_leave_title (GdkEventCrossing* event) {
if (is_sensitive()) {
if (event->type == GDK_ENTER_NOTIFY) {
titleEvBox->set_state(Gtk::STATE_PRELIGHT);
queue_draw();
}
else if (event->type == GDK_LEAVE_NOTIFY) {
titleEvBox->set_state(Gtk::STATE_NORMAL);
queue_draw();
}
}
return true;
}
bool MyExpander::on_enter_leave_enable (GdkEventCrossing* event) {
if (is_sensitive()) {
if (event->type == GDK_ENTER_NOTIFY) {
imageEvBox->set_state(Gtk::STATE_PRELIGHT);
queue_draw();
}
else if (event->type == GDK_LEAVE_NOTIFY) {
imageEvBox->set_state(Gtk::STATE_NORMAL);
queue_draw();
}
}
return true;
}
void MyExpander::updateStyle() {
headerHBox->set_spacing(options.slimUI ? 2 : 5);
headerHBox->set_border_width(options.slimUI ? 1 : 2);
set_spacing(0);
set_border_width(options.slimUI ? 0 : 1);
if (expBox) expBox->updateStyle();
}
void MyExpander::setLabel (Glib::ustring newLabel) {
if (label)
label->set_markup(Glib::ustring("") + escapeHtmlChars(newLabel) + Glib::ustring(""));
}
void MyExpander::setLabel (Gtk::Widget *newWidget) {
if (headerWidget) {
removeIfThere(headerHBox, headerWidget, false);
headerHBox->pack_start(*newWidget, Gtk::PACK_EXPAND_WIDGET, 0);
}
}
bool MyExpander::get_inconsistent() {
return inconsistent;
}
void MyExpander::set_inconsistent(bool isInconsistent) {
if (inconsistent != isInconsistent) {
inconsistent = isInconsistent;
if (useEnabled) {
if (isInconsistent)
statusImage->set(inconsistentPBuf);
else {
if (enabled)
statusImage->set(enabledPBuf);
else
statusImage->set(disabledPBuf);
}
}
}
}
bool MyExpander::getUseEnabled() {
return useEnabled;
}
bool MyExpander::getEnabled() {
return enabled;
}
void MyExpander::setEnabled(bool isEnabled) {
if (isEnabled != enabled) {
if (useEnabled) {
if (enabled) {
enabled = false;
if (!inconsistent) {
statusImage->set(disabledPBuf);
message.emit();
}
}
else {
enabled = true;
if (!inconsistent) {
statusImage->set(enabledPBuf);
message.emit();
}
}
}
}
}
void MyExpander::setEnabledTooltipMarkup(Glib::ustring tooltipMarkup) {
if (useEnabled) {
statusImage->set_tooltip_markup(tooltipMarkup);
}
}
void MyExpander::setEnabledTooltipText(Glib::ustring tooltipText) {
if (useEnabled) {
statusImage->set_tooltip_text(tooltipText);
}
}
void MyExpander::set_expanded( bool expanded ) {
if (!expBox)
return;
bool isVisible = expBox->is_visible();
if (isVisible == expanded)
return;
if (!useEnabled) {
if (expanded )
statusImage->set(openedPBuf);
else
statusImage->set(closedPBuf);
}
if (expanded)
expBox->showBox();
else
expBox->hideBox();
}
bool MyExpander::get_expanded() {
return expBox ? expBox->get_visible() : false;
}
void MyExpander::add (Gtk::Container& widget) {
child = &widget;
expBox = Gtk::manage (new ExpanderBox (child));
expBox->add (*child);
pack_start(*expBox, Gtk::PACK_SHRINK, 0);
child->show();
expBox->hideBox();
}
bool MyExpander::on_toggle(GdkEventButton* event) {
if (flushEvent) {
flushEvent = false;
return false;
}
if (!expBox || event->button != 1)
return false;
bool isVisible = expBox->is_visible();
if (!useEnabled) {
if (isVisible)
statusImage->set(closedPBuf);
else
statusImage->set(openedPBuf);
}
if (isVisible)
expBox->hideBox();
else
expBox->showBox();
return false;
}
Gtk::Container* MyExpander::getChild() {
return child;
}
// used to connect a function to the enabled_toggled signal
MyExpander::type_signal_enabled_toggled MyExpander::signal_enabled_toggled() {
return message;
}
// internal use ; when the user clicks on the toggle button, it calls this method that will emit an enabled_change event
bool MyExpander::on_enabled_change(GdkEventButton* event) {
if (event->button == 1) {
if (enabled) {
enabled = false;
statusImage->set(disabledPBuf);
}
else {
enabled = true;
statusImage->set(enabledPBuf);
}
message.emit();
flushEvent = true;
}
return false;
}
/*
*
* Derived class of some widgets to properly handle the scroll wheel ;
* the user has to use the Shift key to be able to change the widget's value,
* otherwise the mouse wheel will scroll the editor's tabs content.
*
*/
MyScrolledWindow::MyScrolledWindow () {
set_size_request(-1,30);
}
bool MyScrolledWindow::on_scroll_event (GdkEventScroll* event) {
if (!options.hideTPVScrollbar) {
Gtk::ScrolledWindow::on_scroll_event (event);
return true;
}
Gtk::Adjustment *adjust = get_vadjustment();
Gtk::VScrollbar *scroll = get_vscrollbar();
if (adjust && scroll) {
double upper = adjust->get_upper();
double lower = adjust->get_lower();
double value = adjust->get_value();
double step = adjust->get_step_increment();
double value2 = 0.;
if (event->direction == GDK_SCROLL_DOWN) {
value2 = value+step;
if (value2 > upper)
value2 = upper;
if (value2 != value) {
scroll->set_value(value2);
}
}
else {
value2 = value-step;
if (value2 < lower)
value2 = lower;
if (value2 != value) {
scroll->set_value(value2);
}
}
}
return true;
}
MyComboBoxText::MyComboBoxText () {
set_size_request(40, -1);
}
bool MyComboBoxText::on_scroll_event (GdkEventScroll* event) {
// If Shift is pressed, the widget is modified
if (event->state & GDK_SHIFT_MASK) {
Gtk::ComboBoxText::on_scroll_event(event);
return true;
}
// ... otherwise the scroll event is sent back to an upper level
return false;
}
MyComboBox::MyComboBox () {
set_size_request(40, -1);
}
bool MyComboBox::on_scroll_event (GdkEventScroll* event) {
// If Shift is pressed, the widget is modified
if (event->state & GDK_SHIFT_MASK) {
Gtk::ComboBox::on_scroll_event(event);
return true;
}
// ... otherwise the scroll event is sent back to an upper level
return false;
}
MySpinButton::MySpinButton () {
Gtk::Border border;
border.bottom = 0;
border.top = 0;
border.left = 3;
border.right = 3;
set_inner_border(border);
set_numeric(true);
set_wrap(false);
set_alignment(Gtk::ALIGN_RIGHT);
}
void MySpinButton::updateSize() {
double vMin, vMax;
int maxAbs;
unsigned int digits, digits2;
unsigned int maxLen;
get_range(vMin, vMax);
digits = get_digits();
maxAbs = (int)(fmax(fabs(vMin), fabs(vMax))+0.000001);
if (maxAbs==0)
digits2 = 1;
else {
digits2 = (int)(log10(double(maxAbs))+0.000001);
digits2++;
}
maxLen = digits+digits2+(vMin<0?1:0)+(digits>0?1:0);
set_max_length(maxLen);
set_width_chars(maxLen);
}
bool MySpinButton::on_key_press_event (GdkEventKey* event) {
double vMin, vMax;
get_range(vMin, vMax);
if ( (event->string[0] >= 'a' && event->string[0] <= 'z')
||(event->string[0] >= 'A' && event->string[0] <= 'Z')
|| event->string[0] == '+' || (event->string[0] == '-' && vMin >= 0)
|| event->string[0] == '=' || event->string[0] == '_'
)
return false;
else {
if(event->string[0] == ',') {
event->keyval = GDK_period;
event->string[0] = '.';
}
return Gtk::Widget::on_key_press_event(event);
}
}
bool MySpinButton::on_scroll_event (GdkEventScroll* event) {
// If Shift is pressed, the widget is modified
if (event->state & GDK_SHIFT_MASK) {
Gtk::SpinButton::on_scroll_event(event);
return true;
}
// ... otherwise the scroll event is sent back to an upper level
return false;
}
bool MyHScale::on_scroll_event (GdkEventScroll* event) {
// If Shift is pressed, the widget is modified
if (event->state & GDK_SHIFT_MASK) {
Gtk::HScale::on_scroll_event(event);
return true;
}
// ... otherwise the scroll event is sent back to an upper level
return false;
}
bool MyHScale::on_key_press_event (GdkEventKey* event) {
if ( event->string[0] == '+' || event->string[0] == '-' )
return false;
else
return Gtk::Widget::on_key_press_event(event);
}
MyFileChooserButton::MyFileChooserButton (const Glib::ustring& title, Gtk::FileChooserAction action) : Gtk::FileChooserButton(title, action) {
set_size_request(20, -1);
}
// For an unknown reason (a bug ?), it doesn't work when action = FILE_CHOOSER_ACTION_SELECT_FOLDER !
bool MyFileChooserButton::on_scroll_event (GdkEventScroll* event) {
// If Shift is pressed, the widget is modified
if (event->state & GDK_SHIFT_MASK) {
Gtk::FileChooserButton::on_scroll_event(event);
return true;
}
// ... otherwise the scroll event is sent back to an upper level
return false;
}
FileChooserLastFolderPersister::FileChooserLastFolderPersister(
Gtk::FileChooser* chooser, Glib::ustring& folderVariable) :
chooser(chooser), folderVariable(folderVariable) {
assert(chooser != NULL);
selectionChangedConnetion = chooser->signal_selection_changed().connect(
sigc::mem_fun(*this,
&FileChooserLastFolderPersister::selectionChanged));
if (!folderVariable.empty()) {
chooser->set_current_folder(folderVariable);
}
}
FileChooserLastFolderPersister::~FileChooserLastFolderPersister() {
}
void FileChooserLastFolderPersister::selectionChanged() {
if (!chooser->get_current_folder().empty()) {
folderVariable = chooser->get_current_folder();
}
}
TextOrIcon::TextOrIcon (Glib::ustring fname, Glib::ustring labelTx, Glib::ustring tooltipTx, TOITypes type) {
imgIcon = 0;
label = 0;
filename = fname;
labelText = labelTx;
tooltipText = tooltipTx;
switchTo(type);
}
TextOrIcon::~TextOrIcon () {
if (imgIcon) delete imgIcon;
if (label) delete label;
}
void TextOrIcon::switchTo(TOITypes type) {
switch (type) {
case (TOI_ICON):
if (!imgIcon) {
removeIfThere(this, label, false);
delete label;
label = 0;
imgIcon = new RTImage (filename);
pack_start(*imgIcon, Gtk::PACK_SHRINK, 0);
set_tooltip_markup ("" + labelText + "\n" + tooltipText);
}
// do nothing if imgIcon exist, which mean that it is currently being displayed
break;
case(TOI_TEXT):
default:
if (!label) {
removeIfThere(this, imgIcon, false);
delete imgIcon;
imgIcon = 0;
label = new Gtk::Label (labelText, Gtk::ALIGN_CENTER);
pack_start(*label, Gtk::PACK_EXPAND_WIDGET, 0);
set_tooltip_markup (tooltipText);
}
// do nothing if label exist, which mean that it is currently being displayed
break;
}
show_all();
}
BackBuffer::BackBuffer() : x(0), y(0), w(0), h(0), offset(0,0), dirty(true) {}
void BackBuffer::setSrcOffset(int x, int y) {
// values will be clamped when used...
offset.x = x;
offset.y = y;
}
// Note: newW & newH must be > 0
bool BackBuffer::setDrawRectangle(Glib::RefPtr window, int newX, int newY, int newW, int newH, bool updateBackBufferSize) {
assert(newW && newH);
bool newSize = w!=newW || h!=newH;
x = newX;
y = newY;
w = newW;
h = newH;
// WARNING: we're assuming that the surface type won't change during all the execution time of RT. I guess it may be wrong when the user change the gfx card display settings!?
if (updateBackBufferSize && newSize && window) {
// allocate a new Surface
surface.clear(); // ... don't know if this is necessary?
surface = Cairo::ImageSurface::create(Cairo::FORMAT_RGB24, w, h);
dirty = true;
}
return dirty;
}
// Note: newW & newH must be > 0
bool BackBuffer::setDrawRectangle(Cairo::Format format, int newX, int newY, int newW, int newH, bool updateBackBufferSize) {
assert(!newW && !newH);
bool newSize = w!=newW || h!=newH;
x = newX;
y = newY;
w = newW;
h = newH;
// WARNING: we're assuming that the surface type won't change during all the execution time of RT. I guess it may be wrong when the user change the gfx card display settings!?
if (updateBackBufferSize && newSize) {
// allocate a new Surface
surface.clear(); // ... don't know if this is necessary?
surface = Cairo::ImageSurface::create(format, w, h);
dirty = true;
}
return dirty;
}
/*
* Copy the backbuffer to a Gdk::Window
*/
void BackBuffer::copySurface(Glib::RefPtr window, GdkRectangle *rectangle) {
if (surface && window) {
// TODO: look out if window can be different on each call, and if not, store a reference to the window
Cairo::RefPtr crSrc = window->create_cairo_context();
Cairo::RefPtr destSurface = crSrc->get_target();
// compute the source offset
int offsetX = rtengine::LIM(offset.x,0, surface->get_width());
int offsetY = rtengine::LIM(offset.y,0, surface->get_height());
// now copy the off-screen Surface to the destination Surface
Cairo::RefPtr crDest = Cairo::Context::create(destSurface);
crDest->set_source(surface, x-offsetX, y-offsetY);
crDest->set_line_width(0.);
if (rectangle)
crDest->rectangle(rectangle->x, rectangle->y, rectangle->width, rectangle->height);
else
crDest->rectangle(x, y, w, h);
crDest->fill();
}
}
/*
* Copy the BackBuffer to another BackBuffer
*/
void BackBuffer::copySurface(BackBuffer *destBackBuffer, GdkRectangle *rectangle) {
if (surface && destBackBuffer) {
// compute the source offset
int offsetX = rtengine::LIM(offset.x,0, surface->get_width());
int offsetY = rtengine::LIM(offset.y,0, surface->get_height());
// now copy the off-screen Surface to the destination Surface
Cairo::RefPtr crDest = Cairo::Context::create(destBackBuffer->getSurface());
crDest->set_source(surface, x-offsetX, y-offsetY);
crDest->set_line_width(0.);
if (rectangle)
crDest->rectangle(rectangle->x, rectangle->y, rectangle->width, rectangle->height);
else
crDest->rectangle(x, y, w, h);
crDest->fill();
}
}
/*
* Copy the BackBuffer to another Cairo::Surface
*/
void BackBuffer::copySurface(Cairo::RefPtr destSurface, GdkRectangle *rectangle) {
if (surface && destSurface) {
// compute the source offset
int offsetX = rtengine::LIM(offset.x,0, surface->get_width());
int offsetY = rtengine::LIM(offset.y,0, surface->get_height());
// now copy the off-screen Surface to the destination Surface
Cairo::RefPtr crDest = Cairo::Context::create(destSurface);
crDest->set_source(surface, x-offsetX, y-offsetY);
crDest->set_line_width(0.);
if (rectangle)
crDest->rectangle(rectangle->x, rectangle->y, rectangle->width, rectangle->height);
else
crDest->rectangle(x, y, w, h);
crDest->fill();
}
}