2 Copyright (C) 2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include <libart_lgpl/art_misc.h>
25 #include <gtkmm/window.h>
26 #include <gtkmm/combo.h>
27 #include <gtkmm/label.h>
28 #include <gtkmm/paned.h>
29 #include <gtk/gtkpaned.h>
31 #include <gtkmm2ext/utils.h>
32 #include <ardour/ardour.h>
34 #include "ardour_ui.h"
38 #include "rgb_macros.h"
39 #include "canvas_impl.h"
48 pixel_width (const ustring& str, Pango::FontDescription& font)
51 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
53 layout->set_font_description (font);
54 layout->set_text (str);
57 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
62 fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
65 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
66 ustring::size_type shorter_by = 0;
69 layout->set_font_description (font);
74 ustring::iterator last = ustr.end();
75 --last; /* now points at final entry */
79 while (!ustr.empty()) {
81 layout->set_text (txt);
84 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
86 if (width < pixel_width) {
94 if (with_ellipses && shorter_by > 3) {
106 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
112 /* xpm2rgb copied from nixieclock, which bore the legend:
114 nixieclock - a nixie desktop timepiece
115 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
117 and was released under the GPL.
121 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
123 static long vals[256], val;
124 uint32_t t, x, y, colors, cpp;
126 unsigned char *savergb, *rgb;
130 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
131 error << string_compose (_("bad XPM header %1"), xpm[0])
136 savergb = rgb = (unsigned char*) malloc (h * w * 3);
138 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
139 for (t = 0; t < colors; ++t) {
140 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
144 // COLORMAP -> RGB CONVERSION
145 // Get low 3 bytes from vals[]
149 for (y = h-1; y > 0; --y) {
151 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
152 val = vals[(int)*p++];
153 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
154 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
155 *(rgb+0) = val & 0xff; // 0:R
163 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
165 static long vals[256], val;
166 uint32_t t, x, y, colors, cpp;
168 unsigned char *savergb, *rgb;
173 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
174 error << string_compose (_("bad XPM header %1"), xpm[0])
179 savergb = rgb = (unsigned char*) malloc (h * w * 4);
181 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
183 if (strstr (xpm[1], "None")) {
184 sscanf (xpm[1], "%c", &transparent);
191 for (; t < colors; ++t) {
192 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
196 // COLORMAP -> RGB CONVERSION
197 // Get low 3 bytes from vals[]
201 for (y = h-1; y > 0; --y) {
205 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
207 if (transparent && (*p++ == transparent)) {
215 *(rgb+3) = alpha; // 3: alpha
216 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
217 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
218 *(rgb+0) = val & 0xff; // 0:R
225 ArdourCanvas::Points*
226 get_canvas_points (string who, uint32_t npoints)
228 // cerr << who << ": wants " << npoints << " canvas points" << endl;
229 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
230 if (npoints > (uint32_t) gdk_screen_width() + 4) {
234 return new ArdourCanvas::Points (npoints);
237 Pango::FontDescription*
238 get_font_for_style (string widgetname)
240 Gtk::Window window (WINDOW_TOPLEVEL);
242 Glib::RefPtr<Gtk::Style> style;
245 foobar.set_name (widgetname);
246 foobar.ensure_style();
248 style = foobar.get_style ();
250 Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
252 PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
256 /* layout inherited its font description from a PangoContext */
258 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
259 pfd = pango_context_get_font_description (ctxt);
260 return new Pango::FontDescription (pfd, true); /* make a copy */
263 return new Pango::FontDescription (pfd, true); /* make a copy */
267 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
269 /* In GTK+2, styles aren't set up correctly if the widget is not
270 attached to a toplevel window that has a screen pointer.
273 static Gtk::Window* window = 0;
276 window = new Window (WINDOW_TOPLEVEL);
283 foo.set_name (style);
286 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
290 r = waverc->fg[state].red / 257;
291 g = waverc->fg[state].green / 257;
292 b = waverc->fg[state].blue / 257;
294 /* what a hack ... "a" is for "active" */
295 if (state == Gtk::STATE_NORMAL && rgba) {
296 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
298 } else if (attr == "bg") {
300 r = waverc->bg[state].red / 257;
301 g = waverc->bg[state].green / 257;
302 b = waverc->bg[state].blue / 257;
303 } else if (attr == "base") {
304 r = waverc->base[state].red / 257;
305 g = waverc->base[state].green / 257;
306 b = waverc->base[state].blue / 257;
307 } else if (attr == "text") {
308 r = waverc->text[state].red / 257;
309 g = waverc->text[state].green / 257;
310 b = waverc->text[state].blue / 257;
313 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
318 if (state == Gtk::STATE_NORMAL && rgba) {
319 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
321 return (uint32_t) RGB_TO_UINT(r,g,b);
326 canvas_item_visible (ArdourCanvas::Item* item)
328 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
332 set_color (Gdk::Color& c, int rgb)
334 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
338 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
340 GtkWindow* win = window.gobj();
341 GtkWidget* focus = gtk_window_get_focus (win);
342 bool special_handling_of_unmodified_accelerators = false;
344 #undef DEBUG_ACCELERATOR_HANDLING
345 #ifdef DEBUG_ACCELERATOR_HANDLING
346 bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
350 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
351 special_handling_of_unmodified_accelerators = true;
355 #ifdef DEBUG_ACCELERATOR_HANDLING
357 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
358 << special_handling_of_unmodified_accelerators
363 /* This exists to allow us to override the way GTK handles
364 key events. The normal sequence is:
366 a) event is delivered to a GtkWindow
367 b) accelerators/mnemonics are activated
368 c) if (b) didn't handle the event, propagate to
369 the focus widget and/or focus chain
371 The problem with this is that if the accelerators include
372 keys without modifiers, such as the space bar or the
373 letter "e", then pressing the key while typing into
374 a text entry widget results in the accelerator being
375 activated, instead of the desired letter appearing
378 There is no good way of fixing this, but this
379 represents a compromise. The idea is that
380 key events involving modifiers (not Shift)
381 get routed into the activation pathway first, then
382 get propagated to the focus widget if necessary.
384 If the key event doesn't involve modifiers,
385 we deliver to the focus widget first, thus allowing
386 it to get "normal text" without interference
389 Of course, this can also be problematic: if there
390 is a widget with focus, then it will swallow
391 all "normal text" accelerators.
395 if (!special_handling_of_unmodified_accelerators) {
397 /* pretend that certain key events that GTK does not allow
398 to be used as accelerators are actually something that
404 switch (ev->keyval) {
406 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_uparrow, GdkModifierType(ev->state));
410 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_downarrow, GdkModifierType(ev->state));
414 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_rightarrow, GdkModifierType(ev->state));
418 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_leftarrow, GdkModifierType(ev->state));
430 if (!special_handling_of_unmodified_accelerators ||
431 ev->state & (Gdk::MOD1_MASK|
435 Gdk::CONTROL_MASK)) {
437 /* no special handling or modifiers in effect: accelerate first */
439 #ifdef DEBUG_ACCELERATOR_HANDLING
441 cerr << "\tactivate, then propagate\n";
444 if (!gtk_window_activate_key (win, ev)) {
445 return gtk_window_propagate_key_event (win, ev);
447 #ifdef DEBUG_ACCELERATOR_HANDLING
449 cerr << "\tnot handled\n";
456 /* no modifiers, propagate first */
458 #ifdef DEBUG_ACCELERATOR_HANDLING
460 cerr << "\tpropagate, then activate\n";
463 if (!gtk_window_propagate_key_event (win, ev)) {
464 #ifdef DEBUG_ACCELERATOR_HANDLING
466 cerr << "\tpropagation didn't handle, so activate\n";
469 return gtk_window_activate_key (win, ev);
471 #ifdef DEBUG_ACCELERATOR_HANDLING
473 cerr << "\thandled by propagate\n";
479 #ifdef DEBUG_ACCELERATOR_HANDLING
481 cerr << "\tnot handled\n";
487 Glib::RefPtr<Gdk::Pixbuf>
488 get_xpm (std::string name)
490 if (!xpm_map[name]) {
491 xpm_map[name] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name, "pixmaps"));
494 return (xpm_map[name]);
497 Glib::RefPtr<Gdk::Pixbuf>
498 get_icon (const char* cname)
503 string path = ARDOUR::find_data_file (name, "icons");
506 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
510 return Gdk::Pixbuf::create_from_file (path);
514 longest (vector<string>& strings)
516 if (strings.empty()) {
520 vector<string>::iterator longest = strings.begin();
521 string::size_type longest_length = (*longest).length();
523 vector<string>::iterator i = longest;
526 while (i != strings.end()) {
528 string::size_type len = (*i).length();
530 if (len > longest_length) {
532 longest_length = len;
542 key_is_legal_for_numeric_entry (guint keyval)
560 case GDK_KP_Subtract: