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, bool with_ellipses)
52 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
53 ustring::size_type shorter_by = 0;
56 layout->set_font_description (font);
61 ustring::iterator last = ustr.end();
62 --last; /* now points at final entry */
66 while (!ustr.empty()) {
68 layout->set_text (txt);
71 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
73 if (width < pixel_width) {
81 if (with_ellipses && shorter_by > 3) {
93 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
99 /* xpm2rgb copied from nixieclock, which bore the legend:
101 nixieclock - a nixie desktop timepiece
102 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
104 and was released under the GPL.
108 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
110 static long vals[256], val;
111 uint32_t t, x, y, colors, cpp;
113 unsigned char *savergb, *rgb;
117 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
118 error << string_compose (_("bad XPM header %1"), xpm[0])
123 savergb = rgb = (unsigned char*)art_alloc (h * w * 3);
125 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
126 for (t = 0; t < colors; ++t) {
127 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
131 // COLORMAP -> RGB CONVERSION
132 // Get low 3 bytes from vals[]
136 for (y = h-1; y > 0; --y) {
138 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
139 val = vals[(int)*p++];
140 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
141 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
142 *(rgb+0) = val & 0xff; // 0:R
150 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
152 static long vals[256], val;
153 uint32_t t, x, y, colors, cpp;
155 unsigned char *savergb, *rgb;
160 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
161 error << string_compose (_("bad XPM header %1"), xpm[0])
166 savergb = rgb = (unsigned char*)art_alloc (h * w * 4);
168 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
170 if (strstr (xpm[1], "None")) {
171 sscanf (xpm[1], "%c", &transparent);
178 for (; t < colors; ++t) {
179 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
183 // COLORMAP -> RGB CONVERSION
184 // Get low 3 bytes from vals[]
188 for (y = h-1; y > 0; --y) {
192 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
194 if (transparent && (*p++ == transparent)) {
202 *(rgb+3) = alpha; // 3: alpha
203 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
204 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
205 *(rgb+0) = val & 0xff; // 0:R
212 ArdourCanvas::Points*
213 get_canvas_points (string who, uint32_t npoints)
215 // cerr << who << ": wants " << npoints << " canvas points" << endl;
216 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
217 if (npoints > (uint32_t) gdk_screen_width() + 4) {
221 return new ArdourCanvas::Points (npoints);
224 Pango::FontDescription
225 get_font_for_style (string widgetname)
227 Gtk::Window window (WINDOW_TOPLEVEL);
229 Glib::RefPtr<Gtk::Style> style;
232 foobar.set_name (widgetname);
233 foobar.ensure_style();
235 style = foobar.get_style ();
236 return style->get_font();
240 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
242 /* In GTK+2, styles aren't set up correctly if the widget is not
243 attached to a toplevel window that has a screen pointer.
246 static Gtk::Window* window = 0;
249 window = new Window (WINDOW_TOPLEVEL);
256 foo.set_name (style);
259 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
263 r = waverc->fg[state].red / 257;
264 g = waverc->fg[state].green / 257;
265 b = waverc->fg[state].blue / 257;
267 /* what a hack ... "a" is for "active" */
268 if (state == Gtk::STATE_NORMAL && rgba) {
269 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
271 } else if (attr == "bg") {
273 r = waverc->bg[state].red / 257;
274 g = waverc->bg[state].green / 257;
275 b = waverc->bg[state].blue / 257;
276 } else if (attr == "base") {
277 r = waverc->base[state].red / 257;
278 g = waverc->base[state].green / 257;
279 b = waverc->base[state].blue / 257;
280 } else if (attr == "text") {
281 r = waverc->text[state].red / 257;
282 g = waverc->text[state].green / 257;
283 b = waverc->text[state].blue / 257;
286 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
291 if (state == Gtk::STATE_NORMAL && rgba) {
292 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
294 return (uint32_t) RGB_TO_UINT(r,g,b);
299 canvas_item_visible (ArdourCanvas::Item* item)
301 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
305 set_color (Gdk::Color& c, int rgb)
307 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
311 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
313 GtkWindow* win = window.gobj();
314 GtkWidget* focus = gtk_window_get_focus (win);
315 bool special_handling_of_unmodified_accelerators = false;
317 #undef DEBUG_ACCELERATOR_HANDLING
318 #ifdef DEBUG_ACCELERATOR_HANDLING
319 bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
323 if (GTK_IS_ENTRY(focus)) {
324 special_handling_of_unmodified_accelerators = true;
328 #ifdef DEBUG_ACCELERATOR_HANDLING
330 cerr << "Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " focus is an entry ? "
331 << special_handling_of_unmodified_accelerators
336 /* This exists to allow us to override the way GTK handles
337 key events. The normal sequence is:
339 a) event is delivered to a GtkWindow
340 b) accelerators/mnemonics are activated
341 c) if (b) didn't handle the event, propagate to
342 the focus widget and/or focus chain
344 The problem with this is that if the accelerators include
345 keys without modifiers, such as the space bar or the
346 letter "e", then pressing the key while typing into
347 a text entry widget results in the accelerator being
348 activated, instead of the desired letter appearing
351 There is no good way of fixing this, but this
352 represents a compromise. The idea is that
353 key events involving modifiers (not Shift)
354 get routed into the activation pathway first, then
355 get propagated to the focus widget if necessary.
357 If the key event doesn't involve modifiers,
358 we deliver to the focus widget first, thus allowing
359 it to get "normal text" without interference
362 Of course, this can also be problematic: if there
363 is a widget with focus, then it will swallow
364 all "normal text" accelerators.
368 if (!special_handling_of_unmodified_accelerators) {
370 /* pretend that certain key events that GTK does not allow
371 to be used as accelerators are actually something that
377 switch (ev->keyval) {
379 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_uparrow, GdkModifierType(ev->state));
383 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_downarrow, GdkModifierType(ev->state));
387 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_rightarrow, GdkModifierType(ev->state));
391 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_leftarrow, GdkModifierType(ev->state));
403 if (!special_handling_of_unmodified_accelerators ||
404 ev->state & (Gdk::MOD1_MASK|
408 Gdk::CONTROL_MASK)) {
410 /* no special handling or modifiers in effect: accelerate first */
412 #ifdef DEBUG_ACCELERATOR_HANDLING
414 cerr << "\tactivate, then propagate\n";
417 if (!gtk_window_activate_key (win, ev)) {
418 return gtk_window_propagate_key_event (win, ev);
420 #ifdef DEBUG_ACCELERATOR_HANDLING
422 cerr << "\tnot handled\n";
429 /* no modifiers, propagate first */
431 #ifdef DEBUG_ACCELERATOR_HANDLING
433 cerr << "\tactivate, then propagate\n";
436 if (!gtk_window_propagate_key_event (win, ev)) {
437 return gtk_window_activate_key (win, ev);
441 #ifdef DEBUG_ACCELERATOR_HANDLING
443 cerr << "\tnot handled\n";
449 Glib::RefPtr<Gdk::Pixbuf>
450 get_xpm (std::string name)
452 if (!xpm_map[name]) {
453 xpm_map[name] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name, "pixmaps"));
456 return (xpm_map[name]);
459 Glib::RefPtr<Gdk::Pixbuf>
460 get_icon (const char* cname)
465 string path = ARDOUR::find_data_file (name, "icons");
468 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
472 return Gdk::Pixbuf::create_from_file (path);
476 longest (vector<string>& strings)
478 if (strings.empty()) {
482 vector<string>::iterator longest = strings.begin();
483 string::size_type longest_length = (*longest).length();
485 vector<string>::iterator i = longest;
488 while (i != strings.end()) {
490 string::size_type len = (*i).length();
492 if (len > longest_length) {
494 longest_length = len;
504 key_is_legal_for_numeric_entry (guint keyval)
522 case GDK_KP_Subtract:
553 short_path (ustring path, uint32_t target_characters)
555 ustring::size_type last_sep;
556 ustring::size_type len = path.length();
557 const char separator = '/';
559 if (len <= target_characters) {
563 if ((last_sep = path.find_last_of (separator)) == ustring::npos) {
565 /* just a filename, but its too long anyway */
567 if (target_characters > 3) {
568 return path.substr (0, target_characters - 3) + ustring ("...");
570 /* stupid caller, just hand back the whole thing */
575 if (len - last_sep >= target_characters) {
577 /* even the filename itself is too long */
579 if (target_characters > 3) {
580 return path.substr (last_sep+1, target_characters - 3) + ustring ("...");
582 /* stupid caller, just hand back the whole thing */
587 uint32_t so_far = (len - last_sep);
588 uint32_t space_for = target_characters - so_far;
590 if (space_for >= 3) {
592 res += path.substr (last_sep - space_for);
595 /* remove part of the end */
597 res += path.substr (last_sep - space_for, len - last_sep + space_for - 3);