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 fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
51 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
52 ustring::size_type shorter_by = 0;
55 layout->set_font_description (font);
60 ustring::iterator last = ustr.end();
61 --last; /* now points at final entry */
65 while (!ustr.empty()) {
67 layout->set_text (txt);
70 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
72 if (width < pixel_width) {
80 if (with_ellipses && shorter_by > 3) {
92 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
98 /* xpm2rgb copied from nixieclock, which bore the legend:
100 nixieclock - a nixie desktop timepiece
101 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
103 and was released under the GPL.
107 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
109 static long vals[256], val;
110 uint32_t t, x, y, colors, cpp;
112 unsigned char *savergb, *rgb;
116 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
117 error << string_compose (_("bad XPM header %1"), xpm[0])
122 savergb = rgb = (unsigned char*) malloc (h * w * 3);
124 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
125 for (t = 0; t < colors; ++t) {
126 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
130 // COLORMAP -> RGB CONVERSION
131 // Get low 3 bytes from vals[]
135 for (y = h-1; y > 0; --y) {
137 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
138 val = vals[(int)*p++];
139 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
140 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
141 *(rgb+0) = val & 0xff; // 0:R
149 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
151 static long vals[256], val;
152 uint32_t t, x, y, colors, cpp;
154 unsigned char *savergb, *rgb;
159 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
160 error << string_compose (_("bad XPM header %1"), xpm[0])
165 savergb = rgb = (unsigned char*) malloc (h * w * 4);
167 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
169 if (strstr (xpm[1], "None")) {
170 sscanf (xpm[1], "%c", &transparent);
177 for (; t < colors; ++t) {
178 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
182 // COLORMAP -> RGB CONVERSION
183 // Get low 3 bytes from vals[]
187 for (y = h-1; y > 0; --y) {
191 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
193 if (transparent && (*p++ == transparent)) {
201 *(rgb+3) = alpha; // 3: alpha
202 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
203 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
204 *(rgb+0) = val & 0xff; // 0:R
211 ArdourCanvas::Points*
212 get_canvas_points (string who, uint32_t npoints)
214 // cerr << who << ": wants " << npoints << " canvas points" << endl;
215 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
216 if (npoints > (uint32_t) gdk_screen_width() + 4) {
220 return new ArdourCanvas::Points (npoints);
223 Pango::FontDescription
224 get_font_for_style (string widgetname)
226 Gtk::Window window (WINDOW_TOPLEVEL);
228 Glib::RefPtr<Gtk::Style> style;
231 foobar.set_name (widgetname);
232 foobar.ensure_style();
234 style = foobar.get_style ();
235 return style->get_font();
239 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
241 /* In GTK+2, styles aren't set up correctly if the widget is not
242 attached to a toplevel window that has a screen pointer.
245 static Gtk::Window* window = 0;
248 window = new Window (WINDOW_TOPLEVEL);
255 foo.set_name (style);
258 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
262 r = waverc->fg[state].red / 257;
263 g = waverc->fg[state].green / 257;
264 b = waverc->fg[state].blue / 257;
266 /* what a hack ... "a" is for "active" */
267 if (state == Gtk::STATE_NORMAL && rgba) {
268 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
270 } else if (attr == "bg") {
272 r = waverc->bg[state].red / 257;
273 g = waverc->bg[state].green / 257;
274 b = waverc->bg[state].blue / 257;
275 } else if (attr == "base") {
276 r = waverc->base[state].red / 257;
277 g = waverc->base[state].green / 257;
278 b = waverc->base[state].blue / 257;
279 } else if (attr == "text") {
280 r = waverc->text[state].red / 257;
281 g = waverc->text[state].green / 257;
282 b = waverc->text[state].blue / 257;
285 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
290 if (state == Gtk::STATE_NORMAL && rgba) {
291 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
293 return (uint32_t) RGB_TO_UINT(r,g,b);
298 canvas_item_visible (ArdourCanvas::Item* item)
300 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
304 set_color (Gdk::Color& c, int rgb)
306 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
310 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
312 GtkWindow* win = window.gobj();
313 GtkWidget* focus = gtk_window_get_focus (win);
314 bool special_handling_of_unmodified_accelerators = false;
316 #undef DEBUG_ACCELERATOR_HANDLING
317 #ifdef DEBUG_ACCELERATOR_HANDLING
318 bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
322 if (GTK_IS_ENTRY(focus)) {
323 special_handling_of_unmodified_accelerators = true;
327 #ifdef DEBUG_ACCELERATOR_HANDLING
329 cerr << "Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " focus is an entry ? "
330 << special_handling_of_unmodified_accelerators
335 /* This exists to allow us to override the way GTK handles
336 key events. The normal sequence is:
338 a) event is delivered to a GtkWindow
339 b) accelerators/mnemonics are activated
340 c) if (b) didn't handle the event, propagate to
341 the focus widget and/or focus chain
343 The problem with this is that if the accelerators include
344 keys without modifiers, such as the space bar or the
345 letter "e", then pressing the key while typing into
346 a text entry widget results in the accelerator being
347 activated, instead of the desired letter appearing
350 There is no good way of fixing this, but this
351 represents a compromise. The idea is that
352 key events involving modifiers (not Shift)
353 get routed into the activation pathway first, then
354 get propagated to the focus widget if necessary.
356 If the key event doesn't involve modifiers,
357 we deliver to the focus widget first, thus allowing
358 it to get "normal text" without interference
361 Of course, this can also be problematic: if there
362 is a widget with focus, then it will swallow
363 all "normal text" accelerators.
367 if (!special_handling_of_unmodified_accelerators) {
369 /* pretend that certain key events that GTK does not allow
370 to be used as accelerators are actually something that
376 switch (ev->keyval) {
378 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_uparrow, GdkModifierType(ev->state));
382 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_downarrow, GdkModifierType(ev->state));
386 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_rightarrow, GdkModifierType(ev->state));
390 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_leftarrow, GdkModifierType(ev->state));
402 if (!special_handling_of_unmodified_accelerators ||
403 ev->state & (Gdk::MOD1_MASK|
407 Gdk::CONTROL_MASK)) {
409 /* no special handling or modifiers in effect: accelerate first */
411 #ifdef DEBUG_ACCELERATOR_HANDLING
413 cerr << "\tactivate, then propagate\n";
416 if (!gtk_window_activate_key (win, ev)) {
417 return gtk_window_propagate_key_event (win, ev);
419 #ifdef DEBUG_ACCELERATOR_HANDLING
421 cerr << "\tnot handled\n";
428 /* no modifiers, propagate first */
430 #ifdef DEBUG_ACCELERATOR_HANDLING
432 cerr << "\tactivate, then propagate\n";
435 if (!gtk_window_propagate_key_event (win, ev)) {
436 return gtk_window_activate_key (win, ev);
440 #ifdef DEBUG_ACCELERATOR_HANDLING
442 cerr << "\tnot handled\n";
448 Glib::RefPtr<Gdk::Pixbuf>
449 get_xpm (std::string name)
451 if (!xpm_map[name]) {
452 xpm_map[name] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name, "pixmaps"));
455 return (xpm_map[name]);
458 Glib::RefPtr<Gdk::Pixbuf>
459 get_icon (const char* cname)
464 string path = ARDOUR::find_data_file (name, "icons");
467 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
471 return Gdk::Pixbuf::create_from_file (path);
475 longest (vector<string>& strings)
477 if (strings.empty()) {
481 vector<string>::iterator longest = strings.begin();
482 string::size_type longest_length = (*longest).length();
484 vector<string>::iterator i = longest;
487 while (i != strings.end()) {
489 string::size_type len = (*i).length();
491 if (len > longest_length) {
493 longest_length = len;
503 key_is_legal_for_numeric_entry (guint keyval)
521 case GDK_KP_Subtract:
552 short_path (ustring path, uint32_t target_characters)
554 ustring::size_type last_sep;
555 ustring::size_type len = path.length();
556 const char separator = '/';
558 if (len <= target_characters) {
562 if ((last_sep = path.find_last_of (separator)) == ustring::npos) {
564 /* just a filename, but its too long anyway */
566 if (target_characters > 3) {
567 return path.substr (0, target_characters - 3) + ustring ("...");
569 /* stupid caller, just hand back the whole thing */
574 if (len - last_sep >= target_characters) {
576 /* even the filename itself is too long */
578 if (target_characters > 3) {
579 return path.substr (last_sep+1, target_characters - 3) + ustring ("...");
581 /* stupid caller, just hand back the whole thing */
586 uint32_t so_far = (len - last_sep);
587 uint32_t space_for = target_characters - so_far;
589 if (space_for >= 3) {
591 res += path.substr (last_sep - space_for);
594 /* remove part of the end */
596 res += path.substr (last_sep - space_for, len - last_sep + space_for - 3);