/[svn]/gigedit/trunk/src/gigedit/gigedit.cpp
ViewVC logotype

Contents of /gigedit/trunk/src/gigedit/gigedit.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2471 - (show annotations) (download)
Sun Sep 15 17:06:45 2013 UTC (7 years, 7 months ago) by persson
File size: 12147 byte(s)
* fixed compilation error from previous commit (on OS X without gettext)
1 /*
2 * Copyright (C) 2007-2009 Andreas Persson
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2, or (at
7 * your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with program; see the file COPYING. If not, write to the Free
16 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 * 02110-1301 USA.
18 */
19
20 #include "gigedit.h"
21
22 #include <glibmm/dispatcher.h>
23 #include <glibmm/main.h>
24 #include <gtkmm/main.h>
25
26 #include "mainwindow.h"
27
28 #include "global.h"
29
30 #ifdef __APPLE__
31 #include <dlfcn.h>
32 #include <glibmm/fileutils.h>
33 #include <glibmm/miscutils.h>
34 #endif
35
36 namespace {
37
38 // State for a gigedit thread.
39 //
40 // This class is only used when gigedit is run as a plugin and makes
41 // sure that there's only one Gtk::Main event loop. The event loop is
42 // started in a separate thread. The plugin thread just dispatches an
43 // event to the main loop to open a window and then goes to sleep
44 // until the window is closed.
45 //
46 class GigEditState : public sigc::trackable {
47 public:
48 GigEditState(GigEdit* parent) : parent(parent) { }
49 void run(gig::Instrument* pInstrument);
50
51 MainWindow* window;
52
53 private:
54
55 // simple condition variable abstraction
56 class Cond {
57 private:
58 bool pred;
59 Glib::Threads::Mutex mutex;
60 Glib::Threads::Cond cond;
61 public:
62 Cond() : pred(false) { }
63 void signal() {
64 Glib::Threads::Mutex::Lock lock(mutex);
65 pred = true;
66 cond.signal();
67 }
68 void wait() {
69 Glib::Threads::Mutex::Lock lock(mutex);
70 while (!pred) cond.wait(mutex);
71 }
72 };
73
74 #ifdef OLD_THREADS
75 static Glib::StaticMutex mutex;
76 #else
77 static Glib::Threads::Mutex mutex;
78 #endif
79 static Glib::Dispatcher* dispatcher;
80 static GigEditState* current;
81
82 static void main_loop_run(Cond* intialized);
83 static void open_window_static();
84
85 GigEdit* parent;
86 Cond open;
87 Cond close;
88 gig::Instrument* instrument;
89
90 void open_window();
91 void close_window();
92 };
93
94 #ifdef WIN32
95 HINSTANCE gigedit_dll_handle = 0;
96 #endif
97
98 #ifdef __APPLE__
99 std::string gigedit_localedir;
100 #endif
101
102 void init_app() {
103 static bool process_initialized = false;
104 if (!process_initialized) {
105 std::cout << "Initializing 3rd party services needed by gigedit.\n"
106 << std::flush;
107 setlocale(LC_ALL, "");
108
109 #ifdef __APPLE__
110 // Look for pango.modules, gdk-pixbuf.loaders and locale files
111 // under the same dir as the gigedit dylib is installed in.
112 Dl_info info;
113 if (dladdr((void*)&init_app, &info)) {
114 std::string libdir = Glib::path_get_dirname(info.dli_fname);
115
116 if (Glib::getenv("PANGO_SYSCONFDIR") == "" &&
117 Glib::file_test(Glib::build_filename(libdir,
118 "pango/pango.modules"),
119 Glib::FILE_TEST_EXISTS)) {
120 Glib::setenv("PANGO_SYSCONFDIR", libdir, true);
121 }
122 if (Glib::getenv("GDK_PIXBUF_MODULE_FILE") == "") {
123 std::string module_file =
124 Glib::build_filename(libdir,
125 "gtk-2.0/gdk-pixbuf.loaders");
126 if (Glib::file_test(module_file, Glib::FILE_TEST_EXISTS)) {
127 Glib::setenv("GDK_PIXBUF_MODULE_FILE", module_file, true);
128 }
129 }
130 #if HAVE_GETTEXT
131 std::string localedir = Glib::build_filename(libdir, "locale");
132 if (Glib::file_test(localedir, Glib::FILE_TEST_EXISTS)) {
133 gigedit_localedir = localedir;
134 bindtextdomain(GETTEXT_PACKAGE, gigedit_localedir.c_str());
135 } else {
136 bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
137 }
138 #endif
139 }
140
141 // The gtk file dialog stores its recent files state in
142 // ~/.local/share
143 g_mkdir_with_parents(
144 Glib::build_filename(Glib::get_home_dir(),
145 ".local/share").c_str(), 0777);
146 #endif
147
148 #if HAVE_GETTEXT
149
150 #ifdef WIN32
151 #if GLIB_CHECK_VERSION(2, 16, 0)
152 gchar* root =
153 g_win32_get_package_installation_directory_of_module(gigedit_dll_handle);
154 #else
155 gchar* root =
156 g_win32_get_package_installation_directory(NULL, NULL);
157 #endif
158 gchar* temp = g_build_filename(root, "/share/locale", NULL);
159 g_free(root);
160 gchar* localedir = g_win32_locale_filename_from_utf8(temp);
161 g_free(temp);
162 bindtextdomain(GETTEXT_PACKAGE, localedir);
163 g_free(localedir);
164 #elif !defined(__APPLE__)
165 bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
166 #endif
167 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
168 textdomain(GETTEXT_PACKAGE);
169 #endif // HAVE_GETTEXT
170
171 #ifdef OLD_THREADS
172 // make sure thread_init() is called once and ONLY once per process
173 if (!Glib::thread_supported()) Glib::thread_init();
174 #endif
175 process_initialized = true;
176 }
177 }
178
179 void connect_signals(GigEdit* gigedit, MainWindow* mainwindow) {
180 // the signals of the "GigEdit" class are actually just proxies, that
181 // is they simply forward the signals of the internal classes to the
182 // outer world
183 mainwindow->signal_file_structure_to_be_changed().connect(
184 gigedit->signal_file_structure_to_be_changed().make_slot()
185 );
186 mainwindow->signal_file_structure_changed().connect(
187 gigedit->signal_file_structure_changed().make_slot()
188 );
189 mainwindow->signal_samples_to_be_removed().connect(
190 gigedit->signal_samples_to_be_removed().make_slot()
191 );
192 mainwindow->signal_samples_removed().connect(
193 gigedit->signal_samples_removed().make_slot()
194 );
195 mainwindow->signal_region_to_be_changed().connect(
196 gigedit->signal_region_to_be_changed().make_slot()
197 );
198 mainwindow->signal_region_changed().connect(
199 gigedit->signal_region_changed().make_slot()
200 );
201 mainwindow->signal_dimreg_to_be_changed().connect(
202 gigedit->signal_dimreg_to_be_changed().make_slot()
203 );
204 mainwindow->signal_dimreg_changed().connect(
205 gigedit->signal_dimreg_changed().make_slot()
206 );
207 mainwindow->signal_sample_changed().connect(
208 gigedit->signal_sample_changed().make_slot()
209 );
210 mainwindow->signal_sample_ref_changed().connect(
211 gigedit->signal_sample_ref_changed().make_slot()
212 );
213 mainwindow->signal_keyboard_key_hit().connect(
214 gigedit->signal_keyboard_key_hit().make_slot()
215 );
216 mainwindow->signal_keyboard_key_released().connect(
217 gigedit->signal_keyboard_key_released().make_slot()
218 );
219 }
220
221 } // namespace
222
223 GigEdit::GigEdit() {
224 state = NULL;
225 }
226
227 int GigEdit::run(int argc, char* argv[]) {
228 init_app();
229
230 Gtk::Main kit(argc, argv);
231
232 #if HAVE_GETTEXT && defined(__APPLE__)
233 // Gtk::Main binds the gtk locale to a possible non-existent
234 // directory. If we have bundled gtk locale files, we rebind here,
235 // after the Gtk::Main constructor.
236 if (!gigedit_localedir.empty()) {
237 bindtextdomain("gtk20", gigedit_localedir.c_str());
238 }
239 #endif
240
241 MainWindow window;
242 connect_signals(this, &window);
243 if (argc >= 2) window.load_file(argv[1]);
244 kit.run(window);
245 return 0;
246 }
247
248 int GigEdit::run(gig::Instrument* pInstrument) {
249 init_app();
250
251 GigEditState state(this);
252 this->state = &state;
253 state.run(pInstrument);
254 this->state = NULL;
255 return 0;
256 }
257
258 void GigEdit::on_note_on_event(int key, int velocity) {
259 if (!this->state) return;
260 GigEditState* state = (GigEditState*) this->state;
261 state->window->signal_note_on().emit(key, velocity);
262 }
263
264 void GigEdit::on_note_off_event(int key, int velocity) {
265 if (!this->state) return;
266 GigEditState* state = (GigEditState*) this->state;
267 state->window->signal_note_off().emit(key, velocity);
268 }
269
270 sigc::signal<void, gig::File*>& GigEdit::signal_file_structure_to_be_changed() {
271 return file_structure_to_be_changed_signal;
272 }
273
274 sigc::signal<void, gig::File*>& GigEdit::signal_file_structure_changed() {
275 return file_structure_changed_signal;
276 }
277
278 sigc::signal<void, std::list<gig::Sample*> >& GigEdit::signal_samples_to_be_removed() {
279 return samples_to_be_removed_signal;
280 }
281
282 sigc::signal<void>& GigEdit::signal_samples_removed() {
283 return samples_removed_signal;
284 }
285
286 sigc::signal<void, gig::Region*>& GigEdit::signal_region_to_be_changed() {
287 return region_to_be_changed_signal;
288 }
289
290 sigc::signal<void, gig::Region*>& GigEdit::signal_region_changed() {
291 return region_changed_signal;
292 }
293
294 sigc::signal<void, gig::DimensionRegion*>& GigEdit::signal_dimreg_to_be_changed() {
295 return dimreg_to_be_changed_signal;
296 }
297
298 sigc::signal<void, gig::DimensionRegion*>& GigEdit::signal_dimreg_changed() {
299 return dimreg_changed_signal;
300 }
301
302 sigc::signal<void, gig::Sample*>& GigEdit::signal_sample_changed() {
303 return sample_changed_signal;
304 }
305
306 sigc::signal<void, gig::Sample*/*old*/, gig::Sample*/*new*/>& GigEdit::signal_sample_ref_changed() {
307 return sample_ref_changed_signal;
308 }
309
310 sigc::signal<void, int/*key*/, int/*velocity*/>& GigEdit::signal_keyboard_key_hit() {
311 return keyboard_key_hit_signal;
312 }
313
314 sigc::signal<void, int/*key*/, int/*velocity*/>& GigEdit::signal_keyboard_key_released() {
315 return keyboard_key_released_signal;
316 }
317
318 #ifdef OLD_THREADS
319 Glib::StaticMutex GigEditState::mutex = GLIBMM_STATIC_MUTEX_INIT;
320 #else
321 Glib::Threads::Mutex GigEditState::mutex;
322 #endif
323 Glib::Dispatcher* GigEditState::dispatcher = 0;
324 GigEditState* GigEditState::current = 0;
325
326 void GigEditState::open_window_static() {
327 GigEditState* c = GigEditState::current;
328 c->open.signal();
329 c->open_window();
330 }
331
332 void GigEditState::open_window() {
333 window = new MainWindow();
334
335 connect_signals(parent, window);
336 if (instrument) window->load_instrument(instrument);
337
338 window->signal_hide().connect(sigc::mem_fun(this,
339 &GigEditState::close_window));
340 window->present();
341 }
342
343 void GigEditState::close_window() {
344 delete window;
345 close.signal();
346 }
347
348 void GigEditState::main_loop_run(Cond* initialized) {
349 int argc = 1;
350 const char* argv_c[] = { "gigedit" };
351 char** argv = const_cast<char**>(argv_c);
352 Gtk::Main main_loop(argc, argv);
353 #if HAVE_GETTEXT && defined(__APPLE__)
354 if (!gigedit_localedir.empty()) {
355 bindtextdomain("gtk20", gigedit_localedir.c_str());
356 }
357 #endif
358
359 dispatcher = new Glib::Dispatcher();
360 dispatcher->connect(sigc::ptr_fun(&GigEditState::open_window_static));
361 initialized->signal();
362
363 main_loop.run();
364 }
365
366 void GigEditState::run(gig::Instrument* pInstrument) {
367 mutex.lock(); // lock access to static variables
368
369 static bool main_loop_started = false;
370 if (!main_loop_started) {
371 Cond initialized;
372 #ifdef OLD_THREADS
373 Glib::Thread::create(
374 sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),
375 &initialized),
376 false);
377 #else
378 Glib::Threads::Thread::create(
379 sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),
380 &initialized));
381 #endif
382 initialized.wait();
383 main_loop_started = true;
384 }
385 instrument = pInstrument;
386 current = this;
387 dispatcher->emit();
388 open.wait(); // wait until the GUI thread has read current
389 mutex.unlock();
390 close.wait(); // sleep until window is closed
391 }
392
393 #if defined(WIN32)
394 extern "C" {
395 BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
396 {
397 switch (reason) {
398 case DLL_PROCESS_ATTACH:
399 gigedit_dll_handle = instance;
400 break;
401 }
402 return TRUE;
403 }
404 }
405 #endif

  ViewVC Help
Powered by ViewVC