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.
25 #include <libart_lgpl/art_misc.h>
26 #include <gtkmm/window.h>
27 #include <gtkmm/combo.h>
28 #include <gtkmm/label.h>
29 #include <gtkmm/paned.h>
30 #include <gtk/gtkpaned.h>
32 #include <gtkmm2ext/utils.h>
33 #include <ardour/ardour.h>
35 #include "ardour_ui.h"
39 #include "rgb_macros.h"
40 #include "canvas_impl.h"
49 fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width)
52 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
54 layout->set_font_description (font);
59 ustring::iterator last = ustr.end();
60 --last; /* now points at final entry */
62 while (!ustr.empty()) {
64 layout->set_text (ustr);
67 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
69 if (width < pixel_width) {
81 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
87 /* xpm2rgb copied from nixieclock, which bore the legend:
89 nixieclock - a nixie desktop timepiece
90 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
92 and was released under the GPL.
96 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
98 static long vals[256], val;
99 uint32_t t, x, y, colors, cpp;
101 unsigned char *savergb, *rgb;
105 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
106 error << string_compose (_("bad XPM header %1"), xpm[0])
111 savergb = rgb = (unsigned char*)art_alloc (h * w * 3);
113 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
114 for (t = 0; t < colors; ++t) {
115 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
119 // COLORMAP -> RGB CONVERSION
120 // Get low 3 bytes from vals[]
124 for (y = h-1; y > 0; --y) {
126 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
127 val = vals[(int)*p++];
128 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
129 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
130 *(rgb+0) = val & 0xff; // 0:R
138 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
140 static long vals[256], val;
141 uint32_t t, x, y, colors, cpp;
143 unsigned char *savergb, *rgb;
148 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
149 error << string_compose (_("bad XPM header %1"), xpm[0])
154 savergb = rgb = (unsigned char*)art_alloc (h * w * 4);
156 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
158 if (strstr (xpm[1], "None")) {
159 sscanf (xpm[1], "%c", &transparent);
166 for (; t < colors; ++t) {
167 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
171 // COLORMAP -> RGB CONVERSION
172 // Get low 3 bytes from vals[]
176 for (y = h-1; y > 0; --y) {
180 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
182 if (transparent && (*p++ == transparent)) {
190 *(rgb+3) = alpha; // 3: alpha
191 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
192 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
193 *(rgb+0) = val & 0xff; // 0:R
200 ArdourCanvas::Points*
201 get_canvas_points (string who, uint32_t npoints)
203 // cerr << who << ": wants " << npoints << " canvas points" << endl;
204 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
205 if (npoints > (uint32_t) gdk_screen_width() + 4) {
209 return new ArdourCanvas::Points (npoints);
212 Pango::FontDescription
213 get_font_for_style (string widgetname)
215 Gtk::Window window (WINDOW_TOPLEVEL);
217 Glib::RefPtr<Gtk::Style> style;
220 foobar.set_name (widgetname);
221 foobar.ensure_style();
223 style = foobar.get_style ();
224 return style->get_font();
228 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
230 /* In GTK+2, styles aren't set up correctly if the widget is not
231 attached to a toplevel window that has a screen pointer.
234 static Gtk::Window* window = 0;
237 window = new Window (WINDOW_TOPLEVEL);
244 foo.set_name (style);
247 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
251 r = waverc->fg[state].red / 257;
252 g = waverc->fg[state].green / 257;
253 b = waverc->fg[state].blue / 257;
254 /* what a hack ... "a" is for "active" */
255 if (state == Gtk::STATE_NORMAL && rgba) {
256 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
258 } else if (attr == "bg") {
260 r = waverc->bg[state].red / 257;
261 g = waverc->bg[state].green / 257;
262 b = waverc->bg[state].blue / 257;
263 } else if (attr == "base") {
264 r = waverc->base[state].red / 257;
265 g = waverc->base[state].green / 257;
266 b = waverc->base[state].blue / 257;
267 } else if (attr == "text") {
268 r = waverc->text[state].red / 257;
269 g = waverc->text[state].green / 257;
270 b = waverc->text[state].blue / 257;
273 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
278 if (state == Gtk::STATE_NORMAL && rgba) {
279 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
281 return (uint32_t) RGB_TO_UINT(r,g,b);
286 canvas_item_visible (ArdourCanvas::Item* item)
288 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
292 set_color (Gdk::Color& c, int rgb)
294 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
298 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
300 GtkWindow* win = window.gobj();
301 GtkWidget* focus = gtk_window_get_focus (win);
302 bool special_handling_of_unmodified_accelerators = false;
304 #undef DEBUG_ACCELERATOR_HANDLING
305 #ifdef DEBUG_ACCELERATOR_HANDLING
306 bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
310 if (GTK_IS_ENTRY(focus)) {
311 special_handling_of_unmodified_accelerators = true;
315 #ifdef DEBUG_ACCELERATOR_HANDLING
317 cerr << "Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " focus is an entry ? "
318 << special_handling_of_unmodified_accelerators
323 /* This exists to allow us to override the way GTK handles
324 key events. The normal sequence is:
326 a) event is delivered to a GtkWindow
327 b) accelerators/mnemonics are activated
328 c) if (b) didn't handle the event, propagate to
329 the focus widget and/or focus chain
331 The problem with this is that if the accelerators include
332 keys without modifiers, such as the space bar or the
333 letter "e", then pressing the key while typing into
334 a text entry widget results in the accelerator being
335 activated, instead of the desired letter appearing
338 There is no good way of fixing this, but this
339 represents a compromise. The idea is that
340 key events involving modifiers (not Shift)
341 get routed into the activation pathway first, then
342 get propagated to the focus widget if necessary.
344 If the key event doesn't involve modifiers,
345 we deliver to the focus widget first, thus allowing
346 it to get "normal text" without interference
349 Of course, this can also be problematic: if there
350 is a widget with focus, then it will swallow
351 all "normal text" accelerators.
355 if (!special_handling_of_unmodified_accelerators) {
357 /* pretend that certain key events that GTK does not allow
358 to be used as accelerators are actually something that
364 switch (ev->keyval) {
366 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_uparrow, GdkModifierType(ev->state));
370 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_downarrow, GdkModifierType(ev->state));
374 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_rightarrow, GdkModifierType(ev->state));
378 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_leftarrow, GdkModifierType(ev->state));
390 if (!special_handling_of_unmodified_accelerators ||
391 ev->state & (Gdk::MOD1_MASK|
395 Gdk::CONTROL_MASK)) {
397 /* no special handling or modifiers in effect: accelerate first */
399 #ifdef DEBUG_ACCELERATOR_HANDLING
401 cerr << "\tactivate, then propagate\n";
404 if (!gtk_window_activate_key (win, ev)) {
405 return gtk_window_propagate_key_event (win, ev);
407 #ifdef DEBUG_ACCELERATOR_HANDLING
409 cerr << "\tnot handled\n";
416 /* no modifiers, propagate first */
418 #ifdef DEBUG_ACCELERATOR_HANDLING
420 cerr << "\tactivate, then propagate\n";
423 if (!gtk_window_propagate_key_event (win, ev)) {
424 return gtk_window_activate_key (win, ev);
428 #ifdef DEBUG_ACCELERATOR_HANDLING
430 cerr << "\tnot handled\n";
436 Glib::RefPtr<Gdk::Pixbuf>
437 get_xpm (std::string name)
439 if (!xpm_map[name]) {
440 xpm_map[name] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name, "pixmaps"));
443 return (xpm_map[name]);
446 Glib::RefPtr<Gdk::Pixbuf>
447 get_icon (const char* cname)
452 string path = ARDOUR::find_data_file (name, "icons");
455 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
459 return Gdk::Pixbuf::create_from_file (path);
463 longest (vector<string>& strings)
465 if (strings.empty()) {
469 vector<string>::iterator longest = strings.begin();
470 string::size_type longest_length = (*longest).length();
472 vector<string>::iterator i = longest;
475 while (i != strings.end()) {
477 string::size_type len = (*i).length();
479 if (len > longest_length) {
481 longest_length = len;
491 key_is_legal_for_numeric_entry (guint keyval)
509 case GDK_KP_Subtract: