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;
363 if (GTK_IS_ENTRY(focus)) {
364 special_handling_of_unmodified_accelerators = true;
368 /* This exists to allow us to override the way GTK handles
369 key events. The normal sequence is:
371 a) event is delivered to a GtkWindow
372 b) accelerators/mnemonics are activated
373 c) if (b) didn't handle the event, propagate to
374 the focus widget and/or focus chain
376 The problem with this is that if the accelerators include
377 keys without modifiers, such as the space bar or the
378 letter "e", then pressing the key while typing into
379 a text entry widget results in the accelerator being
380 activated, instead of the desired letter appearing
383 There is no good way of fixing this, but this
384 represents a compromise. The idea is that
385 key events involving modifiers (not Shift)
386 get routed into the activation pathway first, then
387 get propagated to the focus widget if necessary.
389 If the key event doesn't involve modifiers,
390 we deliver to the focus widget first, thus allowing
391 it to get "normal text" without interference
394 Of course, this can also be problematic: if there
395 is a widget with focus, then it will swallow
396 all "normal text" accelerators.
400 if (!special_handling_of_unmodified_accelerators) {
402 /* pretend that certain key events that GTK does not allow
403 to be used as accelerators are actually something that
409 switch (ev->keyval) {
411 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_uparrow, GdkModifierType(ev->state));
415 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_downarrow, GdkModifierType(ev->state));
419 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_rightarrow, GdkModifierType(ev->state));
423 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_leftarrow, GdkModifierType(ev->state));
435 if (!special_handling_of_unmodified_accelerators ||
436 ev->state & (Gdk::MOD1_MASK|
440 Gdk::CONTROL_MASK)) {
442 /* no special handling or modifiers in effect: accelerate first */
444 if (!gtk_window_activate_key (win, ev)) {
445 return gtk_window_propagate_key_event (win, ev);
451 /* no modifiers, propagate first */
453 if (!gtk_window_propagate_key_event (win, ev)) {
454 return gtk_window_activate_key (win, ev);
461 Glib::RefPtr<Gdk::Pixbuf>
462 get_xpm (std::string name)
464 if (!xpm_map[name]) {
465 xpm_map[name] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name, "pixmaps"));
468 return (xpm_map[name]);
471 Glib::RefPtr<Gdk::Pixbuf>
472 get_icon (const char* cname)
477 string path = ARDOUR::find_data_file (name, "icons");
480 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
484 return Gdk::Pixbuf::create_from_file (path);
488 longest (vector<string>& strings)
490 if (strings.empty()) {
494 vector<string>::iterator longest = strings.begin();
495 string::size_type longest_length = (*longest).length();
497 vector<string>::iterator i = longest;
500 while (i != strings.end()) {
502 string::size_type len = (*i).length();
504 if (len > longest_length) {
506 longest_length = len;