Merge branch 'master' into cairocanvas
[ardour.git] / gtk2_ardour / splash.cc
1 /*
2     Copyright (C) 2008 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #include <string>
21
22 #include "pbd/failed_constructor.h"
23 #include "pbd/file_utils.h"
24
25 #include "ardour/ardour.h"
26 #include "ardour/filesystem_paths.h"
27
28 #ifdef check
29 #undef check
30 #endif
31
32 #include "gui_thread.h"
33 #include "splash.h"
34
35 #include "i18n.h"
36
37 using namespace Gtk;
38 using namespace Glib;
39 using namespace PBD;
40 using namespace std;
41 using namespace ARDOUR;
42
43 Splash* Splash::the_splash = 0;
44
45 Splash::Splash ()
46 {
47         assert (the_splash == 0);
48         
49         std::string splash_file;
50
51         if (!find_file_in_search_path (ardour_data_search_path(), "splash.png", splash_file)) {
52                 cerr << "Cannot find splash screen image file\n";
53                 throw failed_constructor();
54         }
55
56         try {
57                 pixbuf = Gdk::Pixbuf::create_from_file (splash_file);
58         }
59
60         catch (...) {
61                 cerr << "Cannot construct splash screen image\n";
62                 throw failed_constructor();
63         }
64
65         darea.set_size_request (pixbuf->get_width(), pixbuf->get_height());
66         pop_front ();
67         set_position (WIN_POS_CENTER);
68         darea.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
69         darea.set_double_buffered (false);
70
71         layout = create_pango_layout ("");
72         string str = "<b>";
73         string i18n = string_compose (_("%1 loading ..."), PROGRAM_NAME);
74         str += i18n;
75         str += "</b>";
76
77         layout->set_markup (str);
78
79         darea.show ();
80         darea.signal_expose_event().connect (sigc::mem_fun (*this, &Splash::expose));
81
82         add (darea);
83
84         set_default_size (pixbuf->get_width(), pixbuf->get_height());
85         set_resizable (false);
86         set_type_hint(Gdk::WINDOW_TYPE_HINT_SPLASHSCREEN);
87         the_splash = this;
88
89         expose_done = false;
90         expose_is_the_one = false;
91
92         ARDOUR::BootMessage.connect (msg_connection, invalidator (*this), boost::bind (&Splash::boot_message, this, _1), gui_context());
93 }
94
95 Splash::~Splash ()
96 {
97         the_splash = 0;
98 }
99
100 void
101 Splash::pop_back_for (Gtk::Window& win)
102 {
103 #ifdef __APPLE__
104         /* April 2013: window layering on OS X is a bit different to X Window. at present,
105            the "restack()" functionality in GDK will only operate on windows in the same
106            "level" (e.g. two normal top level windows, or two utility windows) and will not
107            work across them. The splashscreen is on its own "StatusWindowLevel" so restacking 
108            is not going to work.
109
110            So for OS X, we just hide ourselves.
111         */
112         (void) win;
113         hide();
114 #else
115         set_keep_above (false);
116         get_window()->restack (win.get_window(), false);
117 #endif
118 }
119
120 void
121 Splash::pop_front ()
122 {
123
124 #ifdef __APPLE__
125         if (get_window()) {
126                 show ();
127         }
128 #else
129         set_keep_above (true);
130 #endif
131 }
132
133 void
134 Splash::on_realize ()
135 {
136         Window::on_realize ();
137         get_window()->set_decorations (Gdk::WMDecoration(0));
138         layout->set_font_description (get_style()->get_font());
139 }
140
141 bool
142 Splash::on_button_release_event (GdkEventButton* ev)
143 {
144         RefPtr<Gdk::Window> window = get_window();
145         
146         if (!window || ev->window != window->gobj()) {
147                 return false;
148         }
149         
150         hide ();
151         return true;
152 }
153
154 bool
155 Splash::expose (GdkEventExpose* ev)
156 {
157         RefPtr<Gdk::Window> window = darea.get_window();
158
159         /* note: height & width need to be constrained to the pixbuf size
160            in case a WM provides us with a screwy allocation
161         */
162
163         window->draw_pixbuf (get_style()->get_bg_gc (STATE_NORMAL), pixbuf,
164                              ev->area.x, ev->area.y,
165                              ev->area.x, ev->area.y,
166                              min ((pixbuf->get_width() - ev->area.x), ev->area.width),
167                              min ((pixbuf->get_height() - ev->area.y), ev->area.height),
168                              Gdk::RGB_DITHER_NONE, 0, 0);
169
170         Glib::RefPtr<Gtk::Style> style = darea.get_style();
171         Glib::RefPtr<Gdk::GC> white = style->get_white_gc();
172
173         window->draw_layout (white, 10, pixbuf->get_height() - 30, layout);
174
175         /* this must execute AFTER the GDK idle update mechanism 
176          */
177        
178         if (expose_is_the_one) {
179                 Glib::signal_idle().connect (sigc::mem_fun (this, &Splash::idle_after_expose),
180                                              GDK_PRIORITY_REDRAW+2);
181         }
182
183         return true;
184 }
185
186 void
187 Splash::boot_message (std::string msg)
188 {
189         message (msg);
190 }
191
192 bool
193 Splash::idle_after_expose ()
194 {
195         expose_done = true;
196         return false;
197 }
198
199 void
200 Splash::display ()
201 {
202         bool was_mapped = is_mapped ();
203         
204         if (!was_mapped) {
205                 expose_done = false;
206                 expose_is_the_one = false;
207         } 
208
209         pop_front ();
210         present ();
211         
212         if (!was_mapped) {
213                 while (!expose_done) {
214                         gtk_main_iteration ();
215                 }
216                 gdk_display_flush (gdk_display_get_default());
217         }
218 }
219
220 void
221 Splash::message (const string& msg)
222 {
223         string str ("<b>");
224         str += Glib::Markup::escape_text (msg);
225         str += "</b>";
226
227         layout->set_markup (str);
228         Glib::RefPtr<Gdk::Window> win = darea.get_window();
229         
230         if (win) {
231                 expose_done = false;
232
233                 if (win->is_visible ()) {
234                         win->invalidate_rect (Gdk::Rectangle (0, darea.get_height() - 30, darea.get_width(), 30), true);
235                 } else {
236                         darea.queue_draw ();
237                 }
238
239                 while (!expose_done) {
240                         if(gtk_main_iteration ()) return; // quit was called
241                 }
242                 gdk_display_flush (gdk_display_get_default());
243         }
244 }
245
246 bool
247 Splash::on_map_event (GdkEventAny* ev)
248 {
249         expose_is_the_one = true;
250         return Window::on_map_event (ev);
251 }