~ubuntu-branches/ubuntu/trusty/aegisub/trusty

« back to all changes in this revision

Viewing changes to src/main.cpp

  • Committer: Package Import Robot
  • Author(s): Sebastian Reichel
  • Date: 2012-03-16 22:58:00 UTC
  • Revision ID: package-import@ubuntu.com-20120316225800-yfb8h9e5n04rk46a
Tags: upstream-2.1.9
ImportĀ upstreamĀ versionĀ 2.1.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) 2005, Rodrigo Braz Monteiro
 
2
// All rights reserved.
 
3
//
 
4
// Redistribution and use in source and binary forms, with or without
 
5
// modification, are permitted provided that the following conditions are met:
 
6
//
 
7
//   * Redistributions of source code must retain the above copyright notice,
 
8
//     this list of conditions and the following disclaimer.
 
9
//   * Redistributions in binary form must reproduce the above copyright notice,
 
10
//     this list of conditions and the following disclaimer in the documentation
 
11
//     and/or other materials provided with the distribution.
 
12
//   * Neither the name of the Aegisub Group nor the names of its contributors
 
13
//     may be used to endorse or promote products derived from this software
 
14
//     without specific prior written permission.
 
15
//
 
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
17
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
18
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
19
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 
20
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
21
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
22
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
23
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
24
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
25
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
26
// POSSIBILITY OF SUCH DAMAGE.
 
27
//
 
28
// -----------------------------------------------------------------------------
 
29
//
 
30
// AEGISUB
 
31
//
 
32
// Website: http://aegisub.cellosoft.com
 
33
// Contact: mailto:zeratul@cellosoft.com
 
34
//
 
35
 
 
36
 
 
37
////////////
 
38
// Includes
 
39
#include "config.h"
 
40
 
 
41
#include <wx/wxprec.h>
 
42
#include <wx/config.h>
 
43
#include <wx/filename.h>
 
44
#include <wx/msgdlg.h>
 
45
#include <wx/mimetype.h>
 
46
#include <wx/utils.h>
 
47
#include <wx/stdpaths.h>
 
48
#include <wx/filefn.h>
 
49
#include <wx/datetime.h>
 
50
 
 
51
#include "main.h"
 
52
#include "frame_main.h"
 
53
#include "options.h"
 
54
#include "hotkeys.h"
 
55
#include "dialog_associations.h"
 
56
#include "ass_file.h"
 
57
#include "audio_box.h"
 
58
#include "audio_display.h"
 
59
#include "export_framerate.h"
 
60
#include "ass_export_filter.h"
 
61
#include "ass_time.h"
 
62
#include "ass_dialogue.h"
 
63
#include "subs_grid.h"
 
64
#include "subtitle_format.h"
 
65
#include "video_context.h"
 
66
#include "standard_paths.h"
 
67
#ifdef WITH_AUTOMATION
 
68
#include "auto4_base.h"
 
69
#endif
 
70
#include "version.h"
 
71
#include "plugin_manager.h"
 
72
 
 
73
 
 
74
///////////////////
 
75
// wxWidgets macro
 
76
IMPLEMENT_APP(AegisubApp)
 
77
 
 
78
 
 
79
#ifdef WITH_STARTUPLOG
 
80
#define StartupLog(a) MessageBox(0, a, _T("Aegisub startup log"), 0)
 
81
#else
 
82
#define StartupLog(a)
 
83
#endif
 
84
 
 
85
#ifdef __VISUALC__
 
86
#define MS_VC_EXCEPTION 0x406d1388
 
87
 
 
88
typedef struct tagTHREADNAME_INFO {
 
89
        DWORD dwType; // must be 0x1000
 
90
        LPCSTR szName; // pointer to name (in same addr space)
 
91
        DWORD dwThreadID; // thread ID (-1 caller thread)
 
92
        DWORD dwFlags; // reserved for future use, most be zero
 
93
} THREADNAME_INFO;
 
94
 
 
95
void SetThreadName(DWORD dwThreadID, LPCSTR szThreadName) {
 
96
        THREADNAME_INFO info;
 
97
        info.dwType = 0x1000;
 
98
        info.szName = szThreadName;
 
99
        info.dwThreadID = dwThreadID;
 
100
        info.dwFlags = 0;
 
101
        __try {
 
102
                RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
 
103
        }
 
104
        __except (EXCEPTION_CONTINUE_EXECUTION) {}
 
105
}
 
106
#endif
 
107
 
 
108
///////////////////////////
 
109
// Initialization function
 
110
// -----------------------
 
111
// Gets called when application starts, creates MainFrame
 
112
bool AegisubApp::OnInit() {
 
113
#ifdef __VISUALC__
 
114
        SetThreadName((DWORD) -1,"AegiMain");
 
115
#endif
 
116
 
 
117
        StartupLog(_T("Inside OnInit"));
 
118
        frame = NULL;
 
119
        try {
 
120
                // Initialize randomizer
 
121
                StartupLog(_T("Initialize random generator"));
 
122
                srand(time(NULL));
 
123
 
 
124
                // locale for loading options
 
125
                StartupLog(_T("Set initial locale"));
 
126
                setlocale(LC_NUMERIC, "C");
 
127
                setlocale(LC_CTYPE, "C");
 
128
 
 
129
                // App name (yeah, this is a little weird to get rid of an odd warning)
 
130
#ifdef __WXMSW__
 
131
                SetAppName(_T("Aegisub"));
 
132
#else
 
133
#ifdef __WXMAC__
 
134
                SetAppName(_T("Aegisub"));
 
135
#else
 
136
                SetAppName(_T("aegisub"));
 
137
#endif
 
138
#endif
 
139
 
 
140
                // Crash handling
 
141
#if !defined(_DEBUG) || defined(WITH_EXCEPTIONS)
 
142
                StartupLog(_T("Install exception handler"));
 
143
                wxHandleFatalExceptions(true);
 
144
#endif
 
145
 
 
146
                // Set config file
 
147
                StartupLog(_T("Load configuration"));
 
148
                Options.LoadDefaults();
 
149
#ifdef __WXMSW__
 
150
                // Try loading configuration from the install dir if one exists there
 
151
                if (wxFileName::FileExists(StandardPaths::DecodePath(_T("?data/config.dat")))) {
 
152
                        Options.SetFile(StandardPaths::DecodePath(_T("?data/config.dat")));
 
153
                        Options.Load();
 
154
 
 
155
                        if (Options.AsBool(_T("Local config"))) {
 
156
                                // Local config, make ?user mean ?data so all user settings are placed in install dir
 
157
                                StandardPaths::SetPathValue(_T("?user"), StandardPaths::DecodePath(_T("?data")));
 
158
                        }
 
159
                        else {
 
160
                                // Not local config, we don't want that config.dat file here any more
 
161
                                // It might be a leftover from a really old install
 
162
                                wxRemoveFile(StandardPaths::DecodePath(_T("?data/config.dat")));
 
163
                        }
 
164
                }
 
165
#endif
 
166
                // TODO: Check if we can write to config.dat and warn the user if we can't
 
167
                // If we had local config, ?user now means ?data so this will still be loaded from the correct location
 
168
                Options.SetFile(StandardPaths::DecodePath(_T("?user/config.dat")));
 
169
                Options.Load();
 
170
 
 
171
                StartupLog(_T("Store options back"));
 
172
                Options.SetInt(_T("Last Version"),GetSVNRevision());
 
173
                Options.LoadDefaults(false,true);       // Override options based on version number
 
174
                Options.Save();
 
175
                AssTime::UseMSPrecision = Options.AsBool(_T("Use nonstandard Milisecond Times"));
 
176
 
 
177
                // Set hotkeys file
 
178
                StartupLog(_T("Load hotkeys"));
 
179
                Hotkeys.SetFile(StandardPaths::DecodePath(_T("?user/hotkeys.dat")));
 
180
                Hotkeys.Load();
 
181
 
 
182
                StartupLog(_T("Initialize final locale"));
 
183
 
 
184
                // Set locale
 
185
                int lang = Options.AsInt(_T("Locale Code"));
 
186
                if (lang == -1) {
 
187
                        lang = locale.PickLanguage();
 
188
                        Options.SetInt(_T("Locale Code"),lang);
 
189
                        Options.Save();
 
190
                }
 
191
                locale.Init(lang);
 
192
 
 
193
                // Load plugins
 
194
                plugins = new PluginManager();
 
195
                plugins->RegisterBuiltInPlugins();
 
196
 
 
197
                // Load Automation scripts
 
198
#ifdef WITH_AUTOMATION
 
199
                StartupLog(_T("Load global Automation scripts"));
 
200
                global_scripts = new Automation4::AutoloadScriptManager(Options.AsText(_T("Automation Autoload Path")));
 
201
#endif
 
202
 
 
203
                // Load export filters
 
204
                StartupLog(_T("Prepare export filters"));
 
205
                AssExportFilterChain::PrepareFilters();
 
206
 
 
207
                // Set association
 
208
#if !defined(_DEBUG) || defined(WITH_EXCEPTIONS)
 
209
                StartupLog(_T("Check file type associations"));
 
210
                CheckFileAssociations(0);
 
211
#endif
 
212
 
 
213
                // Get parameter subs
 
214
                StartupLog(_T("Parse command line"));
 
215
                wxArrayString subs;
 
216
                for (int i=1;i<argc;i++) {
 
217
                        subs.Add(argv[i]);
 
218
                }
 
219
 
 
220
                // Open main frame
 
221
                StartupLog(_T("Create main window"));
 
222
                frame = new FrameMain(subs);
 
223
                SetTopWindow(frame);
 
224
        }
 
225
 
 
226
        catch (const wchar_t *err) {
 
227
                wxMessageBox(err,_T("Fatal error while initializing"));
 
228
                return false;
 
229
        }
 
230
 
 
231
        catch (...) {
 
232
                wxMessageBox(_T("Unhandled exception"),_T("Fatal error while initializing"));
 
233
                return false;
 
234
        }
 
235
 
 
236
        StartupLog(_T("Initialization complete"));
 
237
        return true;
 
238
}
 
239
 
 
240
 
 
241
////////
 
242
// Exit
 
243
int AegisubApp::OnExit() {
 
244
        SubtitleFormat::DestroyFormats();
 
245
        VideoContext::Clear();
 
246
        delete plugins;
 
247
        Options.Clear();
 
248
#ifdef WITH_AUTOMATION
 
249
        delete global_scripts;
 
250
#endif
 
251
        return wxApp::OnExit();
 
252
}
 
253
 
 
254
 
 
255
#if !defined(_DEBUG) || defined(WITH_EXCEPTIONS)
 
256
/////////////////////////////////////////////
 
257
// Message displayed on unhandled exceptions
 
258
const static wxChar unhandled_exception_message[] = _T("Oops, Aegisub has crashed!\n\nI have tried to emergency-save a copy of your file, and a crash log file has been generated.\n\nYou can find the emergency-saved file in:\n%s\n\nIf you submit the crash log to the Aegisub team, we will investigate the problem and attempt to fix it. You can find the crashlog in:\n%s\n\nAegisub will now close.");
 
259
const static wxChar unhandled_exception_message_nocrashlog[] = _T("Oops, Aegisub has crashed!\n\nI have tried to emergency-save a copy of your file.\n\nYou can find the emergency-saved file in:\n%s\n\nAegisub will now close.");
 
260
 
 
261
///////////////////////
 
262
// Unhandled exception
 
263
void AegisubApp::OnUnhandledException() {
 
264
        // Attempt to recover file
 
265
        wxFileName origfile(AssFile::top->filename);
 
266
        wxString path = Options.AsText(_T("Auto recovery path"));
 
267
        if (path.IsEmpty()) path = StandardPaths::DecodePath(_T("?user/"));
 
268
        wxFileName dstpath(path);
 
269
        if (!dstpath.IsAbsolute()) path = StandardPaths::DecodePathMaybeRelative(path, _T("?user/"));
 
270
        path += _T("/");
 
271
        dstpath.Assign(path);
 
272
        if (!dstpath.DirExists()) wxMkdir(path);
 
273
        wxString filename = path + origfile.GetName() + _T(".RECOVER.ass");
 
274
        AssFile::top->Save(filename,false,false);
 
275
 
 
276
        // Inform user of crash
 
277
        wxMessageBox(wxString::Format(unhandled_exception_message, filename.c_str(), StandardPaths::DecodePath(_T("?user/crashlog.txt")).c_str()), _T("Unhandled exception"), wxOK | wxICON_ERROR, NULL);
 
278
}
 
279
 
 
280
 
 
281
///////////////////
 
282
// Fatal exception
 
283
void AegisubApp::OnFatalException() {
 
284
        // Attempt to recover file
 
285
        wxFileName origfile(AssFile::top->filename);
 
286
        wxString path = Options.AsText(_T("Auto recovery path"));
 
287
        if (path.IsEmpty()) path = StandardPaths::DecodePath(_T("?user/"));
 
288
        wxFileName dstpath(path);
 
289
        if (!dstpath.IsAbsolute()) path = StandardPaths::DecodePathMaybeRelative(path, _T("?user/"));
 
290
        path += _T("/");
 
291
        dstpath.Assign(path);
 
292
        if (!dstpath.DirExists()) wxMkdir(path);
 
293
        wxString filename = path + origfile.GetName() + _T(".RECOVER.ass");
 
294
        AssFile::top->Save(filename,false,false);
 
295
 
 
296
#if wxUSE_STACKWALKER == 1
 
297
        // Stack walk
 
298
        StackWalker walker(_T("Fatal exception"));
 
299
        walker.WalkFromException();
 
300
 
 
301
        // Inform user of crash
 
302
        wxMessageBox(wxString::Format(unhandled_exception_message, filename.c_str(), StandardPaths::DecodePath(_T("?user/crashlog.txt")).c_str()), _T("Fatal exception"), wxOK | wxICON_ERROR, NULL);
 
303
#else
 
304
        // Inform user of crash
 
305
        wxMessageBox(wxString::Format(unhandled_exception_message_nocrashlog, filename.c_str()), _T("Fatal exception"), wxOK | wxICON_ERROR, NULL);
 
306
#endif
 
307
}
 
308
#endif
 
309
 
 
310
 
 
311
////////////////
 
312
// Stack walker
 
313
#if wxUSE_STACKWALKER == 1
 
314
void StackWalker::OnStackFrame(const wxStackFrame &frame) {
 
315
        wxString dst = wxString::Format(_T("%03i - 0x%08X: "),frame.GetLevel(),frame.GetAddress()) + frame.GetName();
 
316
        if (frame.HasSourceLocation()) dst += _T(" on ") + frame.GetFileName() + wxString::Format(_T(":%i"),frame.GetLine());
 
317
        if (file.is_open()) {
 
318
                file << dst.mb_str() << std::endl;
 
319
        }
 
320
        else wxLogMessage(dst);
 
321
}
 
322
 
 
323
StackWalker::StackWalker(wxString cause) {
 
324
        file.open(wxString(StandardPaths::DecodePath(_T("?user/crashlog.txt"))).mb_str(),std::ios::out | std::ios::app);
 
325
        if (file.is_open()) {
 
326
                wxDateTime time = wxDateTime::Now();
 
327
                wxString timeStr = _T("---") + time.FormatISODate() + _T(" ") + time.FormatISOTime() + _T("------------------");
 
328
                formatLen = timeStr.Length();
 
329
                file << std::endl << timeStr.mb_str(wxConvLocal);
 
330
                file << "\nVER - " << GetAegisubLongVersionString().mb_str(wxConvUTF8);
 
331
                file << "\nFTL - Begining stack dump for \"" << cause.mb_str(wxConvUTF8) <<"\":\n";
 
332
        }
 
333
}
 
334
 
 
335
StackWalker::~StackWalker() {
 
336
        if (file.is_open()) {
 
337
                char dashes[1024];
 
338
                int i = 0;
 
339
                for (i=0;i<formatLen;i++) dashes[i] = '-';
 
340
                dashes[i] = 0;
 
341
                file << "End of stack dump.\n";
 
342
                file << dashes;
 
343
                file << "\n";
 
344
                file.close();
 
345
        }
 
346
}
 
347
#endif
 
348
 
 
349
 
 
350
 
 
351
//////////////////
 
352
// Call main loop
 
353
int AegisubApp::OnRun() {
 
354
        wxString error;
 
355
 
 
356
        // Run program
 
357
        try {
 
358
                if (m_exitOnFrameDelete == Later) m_exitOnFrameDelete = Yes;
 
359
                return MainLoop();
 
360
        }
 
361
 
 
362
        // Catch errors
 
363
        catch (wxString &err) { error = err; }
 
364
        catch (wxChar *err) { error = err; }
 
365
        catch (std::exception &e) {     error = wxString(_T("std::exception: ")) + wxString(e.what(),wxConvUTF8); }
 
366
        catch (...) { error = _T("Program terminated in error."); }
 
367
 
 
368
        // Report errors
 
369
        if (!error.IsEmpty()) {
 
370
                std::ofstream file;
 
371
                file.open(wxString(StandardPaths::DecodePath(_T("?user/crashlog.txt"))).mb_str(),std::ios::out | std::ios::app);
 
372
                if (file.is_open()) {
 
373
                        wxDateTime time = wxDateTime::Now();
 
374
                        wxString timeStr = _T("---") + time.FormatISODate() + _T(" ") + time.FormatISOTime() + _T("------------------");
 
375
                        file << std::endl << timeStr.mb_str(wxConvLocal);
 
376
                        file << "\nVER - " << GetAegisubLongVersionString().mb_str(wxConvUTF8);
 
377
                        file << "\nEXC - Aegisub has crashed with unhandled exception \"" << error.mb_str(wxConvLocal) <<"\".\n";
 
378
                        int formatLen = timeStr.Length();
 
379
                        char dashes[1024];
 
380
                        int i = 0;
 
381
                        for (i=0;i<formatLen;i++) dashes[i] = '-';
 
382
                        dashes[i] = 0;
 
383
                        file << dashes;
 
384
                        file << "\n";
 
385
                        file.close();
 
386
                }
 
387
 
 
388
                OnUnhandledException();
 
389
        }
 
390
 
 
391
        ExitMainLoop();
 
392
        return 1;
 
393
}
 
394
 
 
395
 
 
396
////////////
 
397
// Open URL
 
398
void AegisubApp::OpenURL(wxString url) {
 
399
        wxLaunchDefaultBrowser(url, wxBROWSER_NEW_WINDOW);
 
400
}
 
401
 
 
402
 
 
403
////////////////
 
404
// Apple events
 
405
#ifdef __WXMAC__
 
406
void AegisubApp::MacOpenFile(const wxString &filename) {
 
407
        if (frame != NULL && !filename.empty()) {
 
408
                frame->LoadSubtitles(filename);
 
409
                wxFileName filepath(filename);
 
410
                Options.SetText(_T("Last open subtitles path"), filepath.GetPath());
 
411
        }
 
412
}
 
413
#endif
 
414
 
 
415
 
 
416
///////////////
 
417
// Event table
 
418
BEGIN_EVENT_TABLE(AegisubApp,wxApp)
 
419
        EVT_MOUSEWHEEL(AegisubApp::OnMouseWheel)
 
420
        EVT_KEY_DOWN(AegisubApp::OnKey)
 
421
END_EVENT_TABLE()
 
422
 
 
423
 
 
424
/////////////////////
 
425
// Mouse wheel moved
 
426
void AegisubApp::OnMouseWheel(wxMouseEvent &event) {
 
427
        wxPoint pt;
 
428
        wxWindow *target = wxFindWindowAtPointer(pt);
 
429
        if (frame && (target == frame->audioBox->audioDisplay || target == frame->SubsBox)) {
 
430
#if wxCHECK_VERSION(2,9,0)
 
431
                if (target->IsShownOnScreen()) target->GetEventHandler()->ProcessEvent(event);
 
432
#else
 
433
                if (target->IsShownOnScreen()) target->AddPendingEvent(event);
 
434
#endif
 
435
                else event.Skip();
 
436
        }
 
437
        else event.Skip();
 
438
}
 
439
 
 
440
 
 
441
///////////////
 
442
// Key pressed
 
443
void AegisubApp::OnKey(wxKeyEvent &event) {
 
444
        //frame->audioBox->audioDisplay->AddPendingEvent(event);
 
445
        if (!event.GetSkipped()) {
 
446
                event.Skip();
 
447
        }
 
448
}