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