merge resolution with master
[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         hide();
113 #else
114         set_keep_above (false);
115         get_window()->restack (win.get_window(), false);
116 #endif
117 }
118
119 void
120 Splash::pop_front ()
121 {
122
123 #ifdef __APPLE__
124         if (get_window()) {
125                 show ();
126         }
127 #else
128         set_keep_above (true);
129 #endif
130 }
131
132 void
133 Splash::on_realize ()
134 {
135         Window::on_realize ();
136         get_window()->set_decorations (Gdk::WMDecoration(0));
137         layout->set_font_description (get_style()->get_font());
138 }
139
140 bool
141 Splash::on_button_release_event (GdkEventButton* ev)
142 {
143         RefPtr<Gdk::Window> window = get_window();
144         
145         if (!window || ev->window != window->gobj()) {
146                 return false;
147         }
148         
149         hide ();
150         return true;
151 }
152
153 bool
154 Splash::expose (GdkEventExpose* ev)
155 {
156         RefPtr<Gdk::Window> window = darea.get_window();
157
158         /* note: height & width need to be constrained to the pixbuf size
159            in case a WM provides us with a screwy allocation
160         */
161
162         window->draw_pixbuf (get_style()->get_bg_gc (STATE_NORMAL), pixbuf,
163                              ev->area.x, ev->area.y,
164                              ev->area.x, ev->area.y,
165                              min ((pixbuf->get_width() - ev->area.x), ev->area.width),
166                              min ((pixbuf->get_height() - ev->area.y), ev->area.height),
167                              Gdk::RGB_DITHER_NONE, 0, 0);
168
169         Glib::RefPtr<Gtk::Style> style = darea.get_style();
170         Glib::RefPtr<Gdk::GC> white = style->get_white_gc();
171
172         window->draw_layout (white, 10, pixbuf->get_height() - 30, layout);
173
174         /* this must execute AFTER the GDK idle update mechanism 
175          */
176        
177         if (expose_is_the_one) {
178                 Glib::signal_idle().connect (sigc::mem_fun (this, &Splash::idle_after_expose),
179                                              GDK_PRIORITY_REDRAW+2);
180         }
181
182         return true;
183 }
184
185 void
186 Splash::boot_message (std::string msg)
187 {
188         message (msg);
189 }
190
191 bool
192 Splash::idle_after_expose ()
193 {
194         expose_done = true;
195         return false;
196 }
197
198 void
199 Splash::display ()
200 {
201         bool was_mapped = is_mapped ();
202         
203         if (!was_mapped) {
204                 expose_done = false;
205                 expose_is_the_one = false;
206         } 
207
208         pop_front ();
209         present ();
210         
211         if (!was_mapped) {
212                 while (!expose_done) {
213                         gtk_main_iteration ();
214                 }
215                 gdk_display_flush (gdk_display_get_default());
216         }
217 }
218
219 void
220 Splash::message (const string& msg)
221 {
222         string str ("<b>");
223         str += Glib::Markup::escape_text (msg);
224         str += "</b>";
225
226         layout->set_markup (str);
227         Glib::RefPtr<Gdk::Window> win = darea.get_window();
228         
229         if (win) {
230                 expose_done = false;
231
232                 if (win->is_visible ()) {
233                         win->invalidate_rect (Gdk::Rectangle (0, darea.get_height() - 30, darea.get_width(), 30), true);
234                 } else {
235                         darea.queue_draw ();
236                 }
237
238                 while (!expose_done) {
239                         gtk_main_iteration ();
240                 }
241                 gdk_display_flush (gdk_display_get_default());
242         }
243 }
244
245 bool
246 Splash::on_map_event (GdkEventAny* ev)
247 {
248         expose_is_the_one = true;
249         return Window::on_map_event (ev);
250 }