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) {
82 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
88 /* xpm2rgb copied from nixieclock, which bore the legend:
90 nixieclock - a nixie desktop timepiece
91 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
93 and was released under the GPL.
97 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
99 static long vals[256], val;
100 uint32_t t, x, y, colors, cpp;
102 unsigned char *savergb, *rgb;
106 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
107 error << string_compose (_("bad XPM header %1"), xpm[0])
112 savergb = rgb = (unsigned char*)art_alloc (h * w * 3);
114 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
115 for (t = 0; t < colors; ++t) {
116 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
120 // COLORMAP -> RGB CONVERSION
121 // Get low 3 bytes from vals[]
125 for (y = h-1; y > 0; --y) {
127 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
128 val = vals[(int)*p++];
129 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
130 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
131 *(rgb+0) = val & 0xff; // 0:R
139 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
141 static long vals[256], val;
142 uint32_t t, x, y, colors, cpp;
144 unsigned char *savergb, *rgb;
149 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
150 error << string_compose (_("bad XPM header %1"), xpm[0])
155 savergb = rgb = (unsigned char*)art_alloc (h * w * 4);
157 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
159 if (strstr (xpm[1], "None")) {
160 sscanf (xpm[1], "%c", &transparent);
167 for (; t < colors; ++t) {
168 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
172 // COLORMAP -> RGB CONVERSION
173 // Get low 3 bytes from vals[]
177 for (y = h-1; y > 0; --y) {
181 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
183 if (transparent && (*p++ == transparent)) {
191 *(rgb+3) = alpha; // 3: alpha
192 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
193 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
194 *(rgb+0) = val & 0xff; // 0:R
201 ArdourCanvas::Points*
202 get_canvas_points (string who, uint32_t npoints)
204 // cerr << who << ": wants " << npoints << " canvas points" << endl;
205 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
206 if (npoints > (uint32_t) gdk_screen_width() + 4) {
210 return new ArdourCanvas::Points (npoints);
213 Pango::FontDescription
214 get_font_for_style (string widgetname)
216 Gtk::Window window (WINDOW_TOPLEVEL);
218 Glib::RefPtr<Gtk::Style> style;
221 foobar.set_name (widgetname);
222 foobar.ensure_style();
224 style = foobar.get_style ();
225 return style->get_font();
229 pane_handler (GdkEventButton* ev, Gtk::Paned* pane)
231 if (ev->window != Gtkmm2ext::get_paned_handle (*pane)) {
235 if (Keyboard::is_delete_event (ev)) {
240 pos = pane->get_position ();
242 if (dynamic_cast<VPaned*>(pane)) {
243 cmp = pane->get_height();
245 cmp = pane->get_width();
248 /* we have to use approximations here because we can't predict the
249 exact position or sizes of the pane (themes, etc)
252 if (pos < 10 || abs (pos - cmp) < 10) {
254 /* already collapsed: restore it (note that this is cast from a pointer value to int, which is tricky on 64bit */
256 pane->set_position ((intptr_t) pane->get_data ("rpos"));
260 int collapse_direction;
262 /* store the current position */
264 pane->set_data ("rpos", (gpointer) pos);
266 /* collapse to show the relevant child in full */
268 collapse_direction = (intptr_t) pane->get_data ("collapse-direction");
270 if (collapse_direction) {
271 pane->set_position (1);
273 if (dynamic_cast<VPaned*>(pane)) {
274 pane->set_position (pane->get_height());
276 pane->set_position (pane->get_width());
287 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
289 /* In GTK+2, styles aren't set up correctly if the widget is not
290 attached to a toplevel window that has a screen pointer.
293 static Gtk::Window* window = 0;
296 window = new Window (WINDOW_TOPLEVEL);
303 foo.set_name (style);
306 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
310 r = waverc->fg[state].red / 257;
311 g = waverc->fg[state].green / 257;
312 b = waverc->fg[state].blue / 257;
313 /* what a hack ... "a" is for "active" */
314 if (state == Gtk::STATE_NORMAL && rgba) {
315 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
317 } else if (attr == "bg") {
319 r = waverc->bg[state].red / 257;
320 g = waverc->bg[state].green / 257;
321 b = waverc->bg[state].blue / 257;
322 } else if (attr == "base") {
323 r = waverc->base[state].red / 257;
324 g = waverc->base[state].green / 257;
325 b = waverc->base[state].blue / 257;
326 } else if (attr == "text") {
327 r = waverc->text[state].red / 257;
328 g = waverc->text[state].green / 257;
329 b = waverc->text[state].blue / 257;
332 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
337 if (state == Gtk::STATE_NORMAL && rgba) {
338 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
340 return (uint32_t) RGB_TO_UINT(r,g,b);
345 canvas_item_visible (ArdourCanvas::Item* item)
347 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
351 set_color (Gdk::Color& c, int rgb)
353 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
357 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
359 GtkWindow* win = window.gobj();
360 GtkWidget* focus = gtk_window_get_focus (win);
361 bool special_handling_of_unmodified_accelerators = false;
364 if (GTK_IS_ENTRY(focus)) {
365 special_handling_of_unmodified_accelerators = true;
369 /* This exists to allow us to override the way GTK handles
370 key events. The normal sequence is:
372 a) event is delivered to a GtkWindow
373 b) accelerators/mnemonics are activated
374 c) if (b) didn't handle the event, propagate to
375 the focus widget and/or focus chain
377 The problem with this is that if the accelerators include
378 keys without modifiers, such as the space bar or the
379 letter "e", then pressing the key while typing into
380 a text entry widget results in the accelerator being
381 activated, instead of the desired letter appearing
384 There is no good way of fixing this, but this
385 represents a compromise. The idea is that
386 key events involving modifiers (not Shift)
387 get routed into the activation pathway first, then
388 get propagated to the focus widget if necessary.
390 If the key event doesn't involve modifiers,
391 we deliver to the focus widget first, thus allowing
392 it to get "normal text" without interference
395 Of course, this can also be problematic: if there
396 is a widget with focus, then it will swallow
397 all "normal text" accelerators.
401 if (!special_handling_of_unmodified_accelerators) {
403 /* pretend that certain key events that GTK does not allow
404 to be used as accelerators are actually something that
410 switch (ev->keyval) {
412 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_uparrow, GdkModifierType(ev->state));
416 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_downarrow, GdkModifierType(ev->state));
420 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_rightarrow, GdkModifierType(ev->state));
424 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_leftarrow, GdkModifierType(ev->state));
436 if (!special_handling_of_unmodified_accelerators ||
437 ev->state & (Gdk::MOD1_MASK|
441 Gdk::CONTROL_MASK)) {
443 /* no special handling or modifiers in effect: accelerate first */
445 if (!gtk_window_activate_key (win, ev)) {
446 return gtk_window_propagate_key_event (win, ev);
452 /* no modifiers, propagate first */
454 if (!gtk_window_propagate_key_event (win, ev)) {
455 return gtk_window_activate_key (win, ev);
462 Glib::RefPtr<Gdk::Pixbuf>
463 get_xpm (std::string name)
465 if (!xpm_map[name]) {
466 xpm_map[name] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name, "pixmaps"));
469 return (xpm_map[name]);
473 longest (vector<string>& strings)
475 if (strings.empty()) {
479 vector<string>::iterator longest = strings.begin();
480 string::size_type longest_length = (*longest).length();
482 vector<string>::iterator i = longest;
485 while (i != strings.end()) {
487 string::size_type len = (*i).length();
489 if (len > longest_length) {
491 longest_length = len;