#include <map>
#include <algorithm>
+#include <iostream>
#include <gtk/gtkpaned.h>
#include <gtk/gtk.h>
#include <gtkmm/label.h>
#include <gtkmm/comboboxtext.h>
#include <gtkmm/tooltip.h>
+#include <gtkmm/menuitem.h>
#include "gtkmm2ext/utils.h"
#include "gtkmm2ext/persistent_tooltip.h"
void
Gtkmm2ext::init (const char* localedir)
{
-#ifdef ENABLE_NLS
+#if ENABLE_NLS
(void) bindtextdomain(PACKAGE, localedir);
(void) bind_textdomain_codeset (PACKAGE, "UTF-8");
#endif
}
/** Set width request to display given text, and height to display anything.
- This is useful for setting many widgets to the same height for consistency. */
+ * This is useful for setting many widgets to the same height for consistency. */
void
Gtkmm2ext::set_size_request_to_display_given_text_width (Gtk::Widget& w,
const gchar* htext,
int width_max = 0;
int height_max = 0;
w.ensure_style ();
- vector<string> copy;
- const vector<string>* to_use;
- vector<string>::const_iterator i;
-
- for (i = strings.begin(); i != strings.end(); ++i) {
- if ((*i).find_first_of ("gy") != string::npos) {
- /* contains a descender */
- break;
- }
- }
-
- if (i == strings.end()) {
- /* make a copy of the strings then add one that has a descender */
- copy = strings;
- copy.push_back ("g");
- to_use = ©
- } else {
- to_use = &strings;
- }
+ vector<string> copy;
+ const vector<string>* to_use;
+ vector<string>::const_iterator i;
+
+ for (i = strings.begin(); i != strings.end(); ++i) {
+ if ((*i).find_first_of ("gy") != string::npos) {
+ /* contains a descender */
+ break;
+ }
+ }
+
+ if (i == strings.end()) {
+ /* make a copy of the strings then add one that has a descender */
+ copy = strings;
+ copy.push_back ("g");
+ to_use = ©
+ } else {
+ to_use = &strings;
+ }
for (vector<string>::const_iterator i = to_use->begin(); i != to_use->end(); ++i) {
get_pixel_size (w.create_pango_layout (*i), width, height);
}
/** This version specifies horizontal padding in text to avoid assumptions
- about font size. Should be used anywhere padding is used to avoid text,
- like combo boxes. */
+ * about font size. Should be used anywhere padding is used to avoid text,
+ * like combo boxes.
+ */
void
Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget& w,
const std::vector<std::string>& strings,
demultiply_alpha (guint8 src,
guint8 alpha)
{
- /* cairo pixel buffer data contains RGB values with the alpha
- values premultiplied.
-
- GdkPixbuf pixel buffer data contains RGB values without the
- alpha value applied.
-
- this removes the alpha component from the cairo version and
- returns the GdkPixbuf version.
- */
+ /* cairo pixel buffer data contains RGB values with the alpha
+ * values premultiplied.
+ *
+ * GdkPixbuf pixel buffer data contains RGB values without the
+ * alpha value applied.
+ *
+ * this removes the alpha component from the cairo version and
+ * returns the GdkPixbuf version.
+ */
return alpha ? ((guint (src) << 8) - src) / alpha : 0;
}
guint8 const* src_pixel = src;
guint8* dst_pixel = dst;
- /* cairo pixel data is endian-dependent ARGB with A in the most significant 8 bits,
- with premultipled alpha values (see preceding function)
-
- GdkPixbuf pixel data is non-endian-dependent RGBA with R in the lowest addressable
- 8 bits, and non-premultiplied alpha values.
-
- convert from the cairo values to the GdkPixbuf ones.
- */
+ /* cairo pixel data is endian-dependent ARGB with A in the most significant 8 bits,
+ * with premultipled alpha values (see preceding function)
+ *
+ * GdkPixbuf pixel data is non-endian-dependent RGBA with R in the lowest addressable
+ * 8 bits, and non-premultiplied alpha values.
+ *
+ * convert from the cairo values to the GdkPixbuf ones.
+ */
for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
+ for (int x = 0; x < width; x++) {
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
- /* Cairo [ B G R A ] is actually [ B G R A ] in memory SOURCE
- 0 1 2 3
- Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
- */
- dst_pixel[0] = demultiply_alpha (src_pixel[2],
- src_pixel[3]); // R [0] <= [ 2 ]
- dst_pixel[1] = demultiply_alpha (src_pixel[1],
- src_pixel[3]); // G [1] <= [ 1 ]
- dst_pixel[2] = demultiply_alpha (src_pixel[0],
- src_pixel[3]); // B [2] <= [ 0 ]
- dst_pixel[3] = src_pixel[3]; // alpha
+ /* Cairo [ B G R A ] is actually [ B G R A ] in memory SOURCE
+ 0 1 2 3
+ Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
+ */
+ dst_pixel[0] = demultiply_alpha (src_pixel[2],
+ src_pixel[3]); // R [0] <= [ 2 ]
+ dst_pixel[1] = demultiply_alpha (src_pixel[1],
+ src_pixel[3]); // G [1] <= [ 1 ]
+ dst_pixel[2] = demultiply_alpha (src_pixel[0],
+ src_pixel[3]); // B [2] <= [ 0 ]
+ dst_pixel[3] = src_pixel[3]; // alpha
#elif G_BYTE_ORDER == G_BIG_ENDIAN
- /* Cairo [ B G R A ] is actually [ A R G B ] in memory SOURCE
- 0 1 2 3
- Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
- */
- dst_pixel[0] = demultiply_alpha (src_pixel[1],
- src_pixel[0]); // R [0] <= [ 1 ]
- dst_pixel[1] = demultiply_alpha (src_pixel[2],
- src_pixel[0]); // G [1] <= [ 2 ]
- dst_pixel[2] = demultiply_alpha (src_pixel[3],
- src_pixel[0]); // B [2] <= [ 3 ]
- dst_pixel[3] = src_pixel[0]; // alpha
+ /* Cairo [ B G R A ] is actually [ A R G B ] in memory SOURCE
+ 0 1 2 3
+ Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
+ */
+ dst_pixel[0] = demultiply_alpha (src_pixel[1],
+ src_pixel[0]); // R [0] <= [ 1 ]
+ dst_pixel[1] = demultiply_alpha (src_pixel[2],
+ src_pixel[0]); // G [1] <= [ 2 ]
+ dst_pixel[2] = demultiply_alpha (src_pixel[3],
+ src_pixel[0]); // B [2] <= [ 3 ]
+ dst_pixel[3] = src_pixel[0]; // alpha
#else
#error ardour does not currently support PDP-endianess
#endif
- dst_pixel += 4;
- src_pixel += 4;
- }
+ dst_pixel += 4;
+ src_pixel += 4;
+ }
}
}
return buf;
}
+void
+_position_menu_anchored (int& x, int& y, bool& push_in,
+ const Gtk::Menu* const menu,
+ Gtk::Widget* const anchor,
+ const std::string& selected)
+{
+ using namespace Gtk;
+ using namespace Gtk::Menu_Helpers;
+
+ /* TODO: lacks support for rotated dropdown buttons */
+
+ if (!anchor->has_screen () || !anchor->get_has_window ()) {
+ return;
+ }
+
+ Gdk::Rectangle monitor;
+ {
+ const int monitor_num = anchor->get_screen ()->get_monitor_at_window (
+ anchor->get_window ());
+ anchor->get_screen ()->get_monitor_geometry (
+ (monitor_num < 0) ? 0 : monitor_num, monitor);
+ }
+
+ const Requisition menu_req = menu->size_request();
+ const Gdk::Rectangle allocation = anchor->get_allocation();
+
+ /* The x and y position are handled separately.
+ *
+ * For the x position if the direction is LTR (or RTL), then we try in order:
+ * a) align the left (right) of the menu with the left (right) of the button
+ * if there's enough room until the right (left) border of the screen;
+ * b) align the right (left) of the menu with the right (left) of the button
+ * if there's enough room until the left (right) border of the screen;
+ * c) align the right (left) border of the menu with the right (left) border
+ * of the screen if there's enough space;
+ * d) align the left (right) border of the menu with the left (right) border
+ * of the screen, with the rightmost (leftmost) part of the menu that
+ * overflows the screen.
+ * XXX We always align left regardless of the direction because if x is
+ * left of the current monitor, the menu popup code after us notices it
+ * and enforces that the menu stays in the monitor that's at the left...*/
+
+ anchor->get_window ()->get_origin (x, y);
+
+ if (anchor->get_direction() == TEXT_DIR_RTL) {
+ if (monitor.get_x() <= x + allocation.get_width() - menu_req.width) {
+ /* a) align menu right and button right */
+ x += allocation.get_width() - menu_req.width;
+ } else if (x + menu_req.width <= monitor.get_x() + monitor.get_width()) {
+ /* b) align menu left and button left: nothing to do*/
+ } else if (menu_req.width > monitor.get_width()) {
+ /* c) align menu left and screen left, guaranteed to fit */
+ x = monitor.get_x();
+ } else {
+ /* d) XXX align left or the menu might change monitors */
+ x = monitor.get_x();
+ }
+ } else { /* LTR */
+ if (x + menu_req.width <= monitor.get_x() + monitor.get_width()) {
+ /* a) align menu left and button left: nothing to do*/
+ } else if (monitor.get_x() <= x + allocation.get_width() - menu_req.width) {
+ /* b) align menu right and button right */
+ x += allocation.get_width() - menu_req.width;
+ } else if (menu_req.width > monitor.get_width()) {
+ /* c) align menu right and screen right, guaranteed to fit */
+ x = monitor.get_x() + monitor.get_width() - menu_req.width;
+ } else {
+ /* d) align left */
+ x = monitor.get_x();
+ }
+ }
+
+ /* For the y position, try in order:
+ * a) if there is a menu item with the same text as the button, align it
+ * with the button, unless that makes the menu overflow the monitor.
+ * b) align the top of the menu with the bottom of the button if there is
+ * enough room below the button;
+ * c) align the bottom of the menu with the top of the button if there is
+ * enough room above the button;
+ * d) align the bottom of the menu with the bottom of the monitor if there
+ * is enough room, but avoid moving the menu to another monitor */
+
+ const MenuList& items = menu->items ();
+ int offset = 0;
+
+ MenuList::const_iterator i = items.begin();
+ for ( ; i != items.end(); ++i) {
+ const Label* label_widget = dynamic_cast<const Label*>(i->get_child());
+ if (label_widget && selected == ((std::string) label_widget->get_label())) {
+ break;
+ }
+ offset += i->size_request().height;
+ }
+ if (i != items.end() &&
+ y - offset >= monitor.get_y() &&
+ y - offset + menu_req.height <= monitor.get_y() + monitor.get_height()) {
+ y -= offset;
+ } else if (y + allocation.get_height() + menu_req.height <= monitor.get_y() + monitor.get_height()) {
+ y += allocation.get_height(); /* a) */
+ } else if ((y - menu_req.height) >= monitor.get_y()) {
+ y -= menu_req.height; /* b) */
+ } else {
+ y = monitor.get_y() + max(0, monitor.get_height() - menu_req.height);
+ }
+
+ push_in = false;
+}
+
+void
+Gtkmm2ext::anchored_menu_popup (Gtk::Menu* const menu,
+ Gtk::Widget* const anchor,
+ const std::string& selected,
+ guint button, guint32 time)
+{
+ menu->popup(
+ sigc::bind (sigc::ptr_fun(&_position_menu_anchored),
+ menu, anchor, selected),
+ button,
+ time);
+}
+
void
Gtkmm2ext::set_popdown_strings (Gtk::ComboBoxText& cr, const vector<string>& strings)
{
int
Gtkmm2ext::physical_screen_height (Glib::RefPtr<Gdk::Window> win)
{
- GdkScreen* scr = gdk_screen_get_default();
-
- if (win) {
- GdkRectangle r;
- gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
- gdk_screen_get_monitor_geometry (scr, monitor, &r);
- return r.height;
- } else {
- return gdk_screen_get_height (scr);
- }
+ GdkScreen* scr = gdk_screen_get_default();
+
+ if (win) {
+ GdkRectangle r;
+ gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
+ gdk_screen_get_monitor_geometry (scr, monitor, &r);
+ return r.height;
+ } else {
+ return gdk_screen_get_height (scr);
+ }
}
int
Gtkmm2ext::physical_screen_width (Glib::RefPtr<Gdk::Window> win)
{
- GdkScreen* scr = gdk_screen_get_default();
-
- if (win) {
- GdkRectangle r;
- gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
- gdk_screen_get_monitor_geometry (scr, monitor, &r);
- return r.width;
- } else {
- return gdk_screen_get_width (scr);
- }
+ GdkScreen* scr = gdk_screen_get_default();
+
+ if (win) {
+ GdkRectangle r;
+ gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
+ gdk_screen_get_monitor_geometry (scr, monitor, &r);
+ return r.width;
+ } else {
+ return gdk_screen_get_width (scr);
+ }
}
void
Gtkmm2ext::container_clear (Gtk::Container& c)
{
- list<Gtk::Widget*> children = c.get_children();
- for (list<Gtk::Widget*>::iterator child = children.begin(); child != children.end(); ++child) {
- c.remove (**child);
- }
+ list<Gtk::Widget*> children = c.get_children();
+ for (list<Gtk::Widget*>::iterator child = children.begin(); child != children.end(); ++child) {
+ (*child)->hide ();
+ c.remove (**child);
+ }
}
void
void
Gtkmm2ext::rounded_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
{
- double degrees = M_PI / 180.0;
+ static const double degrees = M_PI / 180.0;
+ if (r < 1) {
+ cairo_rectangle (cr, x, y, w, h);
+ return;
+ }
cairo_new_sub_path (cr);
cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
void
Gtkmm2ext::rounded_left_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
{
- double degrees = M_PI / 180.0;
+ static const double degrees = M_PI / 180.0;
cairo_new_sub_path (cr);
cairo_line_to (cr, x+w, y); // tr
void
Gtkmm2ext::rounded_right_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
{
- double degrees = M_PI / 180.0;
+ static const double degrees = M_PI / 180.0;
cairo_new_sub_path (cr);
cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
void
Gtkmm2ext::rounded_top_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
{
- double degrees = M_PI / 180.0;
+ static const double degrees = M_PI / 180.0;
cairo_new_sub_path (cr);
cairo_move_to (cr, x+w, y+h);
void
Gtkmm2ext::rounded_bottom_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
{
- double degrees = M_PI / 180.0;
+ static const double degrees = M_PI / 180.0;
cairo_new_sub_path (cr);
cairo_move_to (cr, x, y);
void
Gtkmm2ext::rounded_top_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
{
- double degrees = M_PI / 180.0;
+ static const double degrees = M_PI / 180.0;
cairo_new_sub_path (cr);
cairo_move_to (cr, x+w, y+h);
line = layout->get_line (0);
/* XXX: might need special care to get the ellipsis character, not sure
- how that works
- */
-
+ * how that works
+ */
string s = string (layout->get_text ().substr(line->get_start_index(), line->get_length()));
cerr << "fit to pixels of " << str << " returns " << s << endl;
* @param avail Available horizontal space.
* @return (Text, possibly ellipsized) and (horizontal size of text)
*/
-
std::pair<std::string, double>
Gtkmm2ext::fit_to_pixels (cairo_t* cr, std::string name, double avail)
{
bool
Gtkmm2ext::event_inside_widget_window (Gtk::Widget& widget, GdkEvent* ev)
{
- gdouble evx, evy;
+ gdouble evx, evy;
- if (!gdk_event_get_root_coords (ev, &evx, &evy)) {
- return false;
- }
+ if (!gdk_event_get_root_coords (ev, &evx, &evy)) {
+ return false;
+ }
- gint wx;
- gint wy;
- gint width, height, depth;
- gint x, y;
+ gint wx;
+ gint wy;
+ gint width, height, depth;
+ gint x, y;
- Glib::RefPtr<Gdk::Window> widget_window = widget.get_window();
+ Glib::RefPtr<Gdk::Window> widget_window = widget.get_window();
- widget_window->get_geometry (x, y, width, height, depth);
- widget_window->get_root_origin (wx, wy);
+ widget_window->get_geometry (x, y, width, height, depth);
+ widget_window->get_root_origin (wx, wy);
- if ((evx >= wx && evx < wx + width) &&
- (evy >= wy && evy < wy + height)) {
- return true;
- }
+ if ((evx >= wx && evx < wx + width) &&
+ (evy >= wy && evy < wy + height)) {
+ return true;
+ }
- return false;
+ return false;
}
const char*
}
}
}
- catch (Glib::FileError& e) {
+ catch (Glib::FileError const& e) {
std::cerr << "listing /Volumnes failed: " << e.what() << std::endl;
}
#endif