/* * 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 . */ #ifndef __GUI_UTILS_ #define __GUI_UTILS_ #include #include "../rtengine/rtengine.h" #include #include Glib::ustring escapeHtmlChars(const Glib::ustring &src); bool removeIfThere (Gtk::Container* cont, Gtk::Widget* w, bool increference = true); void thumbInterp (const unsigned char* src, int sw, int sh, unsigned char* dst, int dw, int dh); Glib::ustring removeExtension (const Glib::ustring& filename); Glib::ustring getExtension (const Glib::ustring& filename); bool confirmOverwrite (Gtk::Window& parent, const std::string& filename); void writeFailed (Gtk::Window& parent, const std::string& filename); 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 = true, bool useBgColor = true, bool fullImageVisible = true); /** * @brief Lock GTK for critical section. * * Will unlock on destruction. To use: * * * { * GThreadLock lock; * // critical code * } * */ class GThreadLock { public: GThreadLock() { gdk_threads_enter(); } ~GThreadLock() { gdk_threads_leave(); } }; /** * @brief Unlock GTK critical section. * * Will relock on destruction. */ class GThreadUnLock { public: GThreadUnLock() { gdk_threads_leave(); } ~GThreadUnLock() { gdk_threads_enter(); } }; class ConnectionBlocker { public: ConnectionBlocker (sigc::connection& connection) : connection (connection) { wasBlocked = connection.block(); } ~ConnectionBlocker () { connection.block(wasBlocked); } private: sigc::connection& connection; bool wasBlocked; }; /** * @brief Glue box to control visibility of the MyExpender's content ; also handle the frame around it */ class ExpanderBox: public Gtk::EventBox { private: Gtk::Container *pC; public: ExpanderBox( Gtk::Container *p); ~ExpanderBox( ) { delete pC; } void updateStyle(); void show() {} void show_all(); void hide() {} void set_visible(bool isVisible = true) {} void showBox(); void hideBox(); void on_style_changed (const Glib::RefPtr& style); bool on_expose_event(GdkEventExpose* event); }; /** * @brief A custom Expander class, that can handle widgets in the title bar * * Custom made expander for responsive widgets in the header. It also handle a "enabled/disabled" property that display * a different arrow depending on this boolean value. * * Warning: once you've instantiated this class with a text label or a widget label, you won't be able to revert to the other solution. */ class MyExpander : public Gtk::VBox { public: typedef sigc::signal type_signal_enabled_toggled; private: type_signal_enabled_toggled message; static Glib::RefPtr inconsistentPBuf; /// "inconsistent" image, displayed when useEnabled is true ; in this case, nothing will tell that an expander is opened/closed static Glib::RefPtr enabledPBuf; /// "enabled" image, displayed when useEnabled is true ; in this case, nothing will tell that an expander is opened/closed static Glib::RefPtr disabledPBuf; /// "disabled" image, displayed when useEnabled is true ; in this case, nothing will tell that an expander is opened/closed static Glib::RefPtr openedPBuf; /// "opened" image, displayed when useEnabled is false static Glib::RefPtr closedPBuf; /// "closed" image, displayed when useEnabled is false bool enabled; /// Enabled feature (default to true) bool inconsistent; /// True if the enabled button is inconsistent Gtk::EventBox *titleEvBox; /// EventBox of the title, to get a connector from it Gtk::HBox *headerHBox; bool flushEvent; /// Flag to control the weird event mechanism of Gtk (please prove me wrong!) ExpanderBox* expBox; /// Frame that includes the child and control its visibility Gtk::EventBox *imageEvBox; /// Enable/Disable or Open/Close arrow event box /// Triggered on opened/closed event bool on_toggle(GdkEventButton* event); /// Triggered on enabled/disabled change -> will emit a toggle event to the connected objects bool on_enabled_change(GdkEventButton* event); /// Used to handle the colored background for the whole Title bool on_enter_leave_title (GdkEventCrossing* event); /// Used to handle the colored background for the Enable button bool on_enter_leave_enable (GdkEventCrossing* event); /// Update the style of this widget, depending in the "slim" option void updateStyle(); void on_style_changed (const Glib::RefPtr& style) { updateStyle(); } protected: Gtk::Container* child; /// Gtk::Contained to display below the expander's title Gtk::Widget* headerWidget; /// Widget to display in the header, next to the arrow image ; can be NULL if the "string" version of the ctor has been used Gtk::Image* statusImage; /// Image to display the opened/closed status (if useEnabled is false) of the enabled/disabled status (if useEnabled is true) Gtk::Label* label; /// Text to display in the header, next to the arrow image ; can be NULL if the "widget" version of the ctor has been used bool useEnabled; /// Set whether to handle an enabled/disabled feature and display the appropriate images public: /** @brief Create a custom expander with a simple header made of a label. * @param useEnabled Set whether to handle an enabled/disabled toggle button and display the appropriate image * @param titleLabel A string to display in the header. Warning: you won't be able to switch to a widget label. */ MyExpander(bool useEnabled, Glib::ustring titleLabel); /** Create a custom expander with a a custom - and responsive - widget * @param useEnabled Set whether to handle an enabled/disabled toggle button and display the appropriate image * @param titleWidget A widget to display in the header. Warning: you won't be able to switch to a string label. */ MyExpander(bool useEnabled, Gtk::Widget* titleWidget); /// Initialize the class by loading the images static void init(); Glib::SignalProxy1< bool, GdkEventButton* > signal_button_release_event() { return titleEvBox->signal_button_release_event(); }; type_signal_enabled_toggled signal_enabled_toggled(); /// Set a new label string. If it has been instantiated with a Gtk::Widget, this method will do nothing void setLabel (Glib::ustring newLabel); /// Set a new label string. If it has been instantiated with a Gtk::Widget, this method will do nothing void setLabel (Gtk::Widget *newWidget); /// Get whether the enabled option is set (to true or false) or unset (i.e. undefined) bool get_inconsistent(); /// Set whether the enabled option is set (to true or false) or unset (i.e. undefined) void set_inconsistent(bool isInconsistent); /// Get whether the enabled button is used or not bool getUseEnabled(); /// Get whether the enabled button is on or off bool getEnabled(); /// If not inconsistent, set the enabled button to true or false and emit the message if the state is different /// If inconsistent, set the internal value to true or false, but do not update the image and do not emit the message void setEnabled(bool isEnabled); /// Adds a Tooltip to the Enabled button, if it exist ; do nothing otherwise void setEnabledTooltipMarkup(Glib::ustring tooltipMarkup); void setEnabledTooltipText(Glib::ustring tooltipText); /// Get the header widget. It'll send back the Gtk::Label* if it has been instantiated with a simple text Gtk::Widget* getLabelWidget() const { return headerWidget ? headerWidget : label; } /// Get the widget shown/hidden by the expander Gtk::Container* getChild(); /// Set the collapsed/expanded state of the expander void set_expanded( bool expanded ); /// Get the collapsed/expanded state of the expander bool get_expanded(); /// Add a Gtk::Container for the content of the expander /// Warning: do not manually Show/Hide the widget, because this parameter is handled by the click on the Expander's title void add (Gtk::Container& widget); }; /** * @brief subclass of Gtk::ScrolledWindow in order to handle the scrollwheel */ class MyScrolledWindow : public Gtk::ScrolledWindow { bool on_scroll_event (GdkEventScroll* event); public: MyScrolledWindow(); }; /** * @brief subclass of Gtk::ComboBox in order to handle the scrollwheel */ class MyComboBox : public Gtk::ComboBox { bool on_scroll_event (GdkEventScroll* event); public: MyComboBox (); }; /** * @brief subclass of Gtk::ComboBoxText in order to handle the scrollwheel */ class MyComboBoxText : public Gtk::ComboBoxText { bool on_scroll_event (GdkEventScroll* event); public: MyComboBoxText (); }; /** * @brief subclass of Gtk::SpinButton in order to handle the scrollwheel */ class MySpinButton : public Gtk::SpinButton { protected: bool on_scroll_event (GdkEventScroll* event); bool on_key_press_event (GdkEventKey* event); public: MySpinButton (); void updateSize(); }; /** * @brief subclass of Gtk::HScale in order to handle the scrollwheel */ class MyHScale : public Gtk::HScale { bool on_scroll_event (GdkEventScroll* event); bool on_key_press_event (GdkEventKey* event); }; /** * @brief subclass of Gtk::FileChooserButton in order to handle the scrollwheel */ class MyFileChooserButton : public Gtk::FileChooserButton { protected: bool on_scroll_event (GdkEventScroll* event); public: MyFileChooserButton (const Glib::ustring& title, Gtk::FileChooserAction action = Gtk::FILE_CHOOSER_ACTION_OPEN); }; /** * @brief A helper method to connect the current folder property of a file chooser to an arbitrary variable. */ void bindCurrentFolder (Gtk::FileChooser& chooser, Glib::ustring& variable); typedef enum RTUpdatePolicy { RTUP_STATIC, RTUP_DYNAMIC } eUpdatePolicy; typedef enum RTOrientation { RTO_Left2Right, RTO_Bottom2Top, RTO_Right2Left, RTO_Top2Bottom } eRTOrientation; enum TOITypes { TOI_TEXT, TOI_ICON }; typedef enum RTNav { NAV_NONE, NAV_NEXT, NAV_PREVIOUS } eRTNav; /** * @brief Handle the switch between text and image to be displayed in the HBox (to be used in a button/toolpanel) */ class TextOrIcon : public Gtk::HBox { protected: Gtk::Image* imgIcon; Gtk::Label* label; Glib::ustring filename; Glib::ustring labelText; Glib::ustring tooltipText; public: TextOrIcon (Glib::ustring filename, Glib::ustring labelTx, Glib::ustring tooltipTx, TOITypes type); ~TextOrIcon (); void switchTo(TOITypes type); }; /** * @brief Define a gradient milestone */ class GradientMilestone { public: double position; double r; double g; double b; double a; GradientMilestone(double _p = 0., double _r = 0., double _g = 0., double _b = 0., double _a = 0.) { position = _p; r = _r; g = _g; b = _b; a = _a; } }; /** * @brief Handle point coordinates */ template class Point { public: T x, y; Point() { x = T(0); y = T(0); } Point(T coordX, T coordY) { x = coordX; y = coordY; } void setCoords(T coordX, T coordY) { x = coordX; y = coordY; } }; /** * @brief Handle backbuffers as automatically as possible */ class BackBuffer { protected: int x, y, w, h; // Rectangle where the colored bar has to be drawn Point offset; // Offset of the source region to draw, relative to the top left corner Cairo::RefPtr surface; bool dirty; // mean that the Surface has to be (re)allocated public: BackBuffer(); // set the destination drawing rectangle; return true if the dimensions are different // Note: newW & newH must be > 0 bool setDrawRectangle(Glib::RefPtr window, int newX, int newY, int newW, int newH, bool updateBackBufferSize = true); bool setDrawRectangle(Cairo::Format format, int newX, int newY, int newW, int newH, bool updateBackBufferSize = true); void setSrcOffset(int x, int y); void copySurface(Glib::RefPtr window, GdkRectangle *rectangle = NULL); void copySurface(BackBuffer *destBackBuffer, GdkRectangle *rectangle = NULL); void copySurface(Cairo::RefPtr destSurface, GdkRectangle *rectangle = NULL); void setDirty(bool isDirty) { dirty = isDirty; if (!dirty && !surface) { dirty = true; } } bool isDirty() { return dirty; } // you have to check if the surface is created thanks to surfaceCreated before starting to draw on it bool surfaceCreated() { return surface; } Cairo::RefPtr getSurface() { return surface; } void setSurface(Cairo::RefPtr surf) { surface = surf; } void deleteSurface() { if (surface) { surface.clear(); } dirty = true; } // will let you get a Cairo::Context for Cairo drawing operations Cairo::RefPtr getContext() { return Cairo::Context::create(surface); } int getWidth() { return surface ? surface->get_width() : 0; } int getHeight() { return surface ? surface->get_height() : 0; } }; inline void setActiveTextOrIndex (Gtk::ComboBoxText& comboBox, const Glib::ustring& text, int index) { comboBox.set_active_text (text); if (comboBox.get_active_row_number () < 0) comboBox.set_active (index); } #endif