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 pane_handler (GdkEventButton* ev, Gtk::Paned* pane)
230 if (ev->window != Gtkmm2ext::get_paned_handle (*pane)) {
234 if (Keyboard::is_delete_event (ev)) {
239 pos = pane->get_position ();
241 if (dynamic_cast<VPaned*>(pane)) {
242 cmp = pane->get_height();
244 cmp = pane->get_width();
247 /* we have to use approximations here because we can't predict the
248 exact position or sizes of the pane (themes, etc)
251 if (pos < 10 || abs (pos - cmp) < 10) {
253 /* already collapsed: restore it (note that this is cast from a pointer value to int, which is tricky on 64bit */
255 pane->set_position ((intptr_t) pane->get_data ("rpos"));
259 int collapse_direction;
261 /* store the current position */
263 pane->set_data ("rpos", (gpointer) pos);
265 /* collapse to show the relevant child in full */
267 collapse_direction = (intptr_t) pane->get_data ("collapse-direction");
269 if (collapse_direction) {
270 pane->set_position (1);
272 if (dynamic_cast<VPaned*>(pane)) {
273 pane->set_position (pane->get_height());
275 pane->set_position (pane->get_width());
286 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
288 /* In GTK+2, styles aren't set up correctly if the widget is not
289 attached to a toplevel window that has a screen pointer.
292 static Gtk::Window* window = 0;
295 window = new Window (WINDOW_TOPLEVEL);
302 foo.set_name (style);
305 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
309 r = waverc->fg[state].red / 257;
310 g = waverc->fg[state].green / 257;
311 b = waverc->fg[state].blue / 257;
312 /* what a hack ... "a" is for "active" */
313 if (state == Gtk::STATE_NORMAL && rgba) {
314 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
316 } else if (attr == "bg") {
318 r = waverc->bg[state].red / 257;
319 g = waverc->bg[state].green / 257;
320 b = waverc->bg[state].blue / 257;
321 } else if (attr == "base") {
322 r = waverc->base[state].red / 257;
323 g = waverc->base[state].green / 257;
324 b = waverc->base[state].blue / 257;
325 } else if (attr == "text") {
326 r = waverc->text[state].red / 257;
327 g = waverc->text[state].green / 257;
328 b = waverc->text[state].blue / 257;
331 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
336 if (state == Gtk::STATE_NORMAL && rgba) {
337 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
339 return (uint32_t) RGB_TO_UINT(r,g,b);
344 canvas_item_visible (ArdourCanvas::Item* item)
346 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
350 set_color (Gdk::Color& c, int rgb)
352 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
356 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
358 GtkWindow* win = window.gobj();
359 GtkWidget* focus = gtk_window_get_focus (win);
360 bool special_handling_of_unmodified_accelerators = false;
362 #undef DEBUG_ACCELERATOR_HANDLING
363 #ifdef DEBUG_ACCELERATOR_HANDLING
364 bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
368 if (GTK_IS_ENTRY(focus)) {
369 special_handling_of_unmodified_accelerators = true;
373 #ifdef DEBUG_ACCELERATOR_HANDLING
375 cerr << "Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " focus is an entry ? "
376 << special_handling_of_unmodified_accelerators
381 /* This exists to allow us to override the way GTK handles
382 key events. The normal sequence is:
384 a) event is delivered to a GtkWindow
385 b) accelerators/mnemonics are activated
386 c) if (b) didn't handle the event, propagate to
387 the focus widget and/or focus chain
389 The problem with this is that if the accelerators include
390 keys without modifiers, such as the space bar or the
391 letter "e", then pressing the key while typing into
392 a text entry widget results in the accelerator being
393 activated, instead of the desired letter appearing
396 There is no good way of fixing this, but this
397 represents a compromise. The idea is that
398 key events involving modifiers (not Shift)
399 get routed into the activation pathway first, then
400 get propagated to the focus widget if necessary.
402 If the key event doesn't involve modifiers,
403 we deliver to the focus widget first, thus allowing
404 it to get "normal text" without interference
407 Of course, this can also be problematic: if there
408 is a widget with focus, then it will swallow
409 all "normal text" accelerators.
413 if (!special_handling_of_unmodified_accelerators) {
415 /* pretend that certain key events that GTK does not allow
416 to be used as accelerators are actually something that
422 switch (ev->keyval) {
424 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_uparrow, GdkModifierType(ev->state));
428 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_downarrow, GdkModifierType(ev->state));
432 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_rightarrow, GdkModifierType(ev->state));
436 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_leftarrow, GdkModifierType(ev->state));
448 if (!special_handling_of_unmodified_accelerators ||
449 ev->state & (Gdk::MOD1_MASK|
453 Gdk::CONTROL_MASK)) {
455 /* no special handling or modifiers in effect: accelerate first */
457 #ifdef DEBUG_ACCELERATOR_HANDLING
459 cerr << "\tactivate, then propagate\n";
462 if (!gtk_window_activate_key (win, ev)) {
463 return gtk_window_propagate_key_event (win, ev);
465 #ifdef DEBUG_ACCELERATOR_HANDLING
467 cerr << "\tnot handled\n";
474 /* no modifiers, propagate first */
476 #ifdef DEBUG_ACCELERATOR_HANDLING
478 cerr << "\tactivate, then propagate\n";
481 if (!gtk_window_propagate_key_event (win, ev)) {
482 return gtk_window_activate_key (win, ev);
486 #ifdef DEBUG_ACCELERATOR_HANDLING
488 cerr << "\tnot handled\n";
494 Glib::RefPtr<Gdk::Pixbuf>
495 get_xpm (std::string name)
497 if (!xpm_map[name]) {
498 xpm_map[name] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name, "pixmaps"));
501 return (xpm_map[name]);
504 Glib::RefPtr<Gdk::Pixbuf>
505 get_icon (const char* cname)
510 string path = ARDOUR::find_data_file (name, "icons");
513 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
517 return Gdk::Pixbuf::create_from_file (path);
521 longest (vector<string>& strings)
523 if (strings.empty()) {
527 vector<string>::iterator longest = strings.begin();
528 string::size_type longest_length = (*longest).length();
530 vector<string>::iterator i = longest;
533 while (i != strings.end()) {
535 string::size_type len = (*i).length();
537 if (len > longest_length) {
539 longest_length = len;