1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: MadEditApp.cpp
3 // Description:
4 // Author: madedit@gmail.com
5 // Licence: GPL
6 ///////////////////////////////////////////////////////////////////////////////
7
8 #include "MadEditApp.h"
9 #include "MadEditFrame.h"
10 #include "MadOptionsDialog.h"
11 #include "MadUtils.h"
12
13 #include "MadEdit/MadEdit.h"
14
15 #include <wx/filename.h>
16 #include <wx/stdpaths.h>
17 #include <wx/fileconf.h>
18
19 IMPLEMENT_APP(MadEditApp)
20
21
22 #ifdef _DEBUG
23 #include <crtdbg.h>
24 #define new new(_NORMAL_BLOCK ,__FILE__, __LINE__)
25 #endif
26
27 #ifdef __WXMSW__
28 HANDLE g_Mutex=NULL;
29 #endif
30
31 wxLocale g_Locale;
32
33 wxString g_MadEditAppDir;
34 wxString g_MadEditHomeDir;
35 wxString g_MadEditConfigName;
36 bool g_DoNotSaveSettings=false;
37 bool g_ResetAllKeys=false;
38
39 wxChar *g_LanguageString[]=
40 {
41 wxT("System Default"),
42 wxT("\u7B80\u4F53\u4E2D\u6587(Chinese Simplified)"),
43 wxT("\u6B63\u9AD4\u4E2D\u6587(Chinese Traditional)"),
44 wxT("English"),
45 wxT("Italiano(Italian)"),
46 wxT("\u65E5\u6587(Japanese)"),
47 };
48 int g_LanguageValue[]=
49 {
50 wxLANGUAGE_DEFAULT,
51 wxLANGUAGE_CHINESE_SIMPLIFIED,
52 wxLANGUAGE_CHINESE_TRADITIONAL,
53 wxLANGUAGE_ENGLISH_US,
54 wxLANGUAGE_ITALIAN,
55 wxLANGUAGE_JAPANESE,
56 };
57 extern const size_t g_LanguageCount = sizeof(g_LanguageValue)/sizeof(int);
58
59 #ifdef __WXGTK__
60
61 // the codes of SingleInstance checking and
62 // SendMessage to previous instance under GTK+
63 // are from gcin (http://www.csie.nctu.edu.tw/~cp76/gcin/)
64
65 #include <X11/Xatom.h>
66 #include <gtk/gtk.h>
67 #include <gdk/gdkx.h>
68 #include <wx/gtk/win_gtk.h>
69
70 Atom g_MadEdit_atom;
71 Display *g_Display=NULL;
72
my_gdk_filter(GdkXEvent * xevent,GdkEvent * event,gpointer data)73 static GdkFilterReturn my_gdk_filter(GdkXEvent *xevent,
74 GdkEvent *event,
75 gpointer data)
76 {
77 XEvent *xeve = (XEvent *)xevent;
78
79 if (xeve->type == PropertyNotify)
80 {
81 XPropertyEvent *xprop = &xeve->xproperty;
82
83 if (xprop->atom == g_MadEdit_atom)
84 {
85 Atom actual_type;
86 int actual_format;
87 u_long nitems,bytes_after;
88 char *message;
89
90 if (XGetWindowProperty(g_Display, xprop->window, g_MadEdit_atom, 0, 1024*16,
91 False, AnyPropertyType, &actual_type, &actual_format,
92 &nitems,&bytes_after,(unsigned char**)&message) != Success)
93 {
94 //dbg("err prop");
95 return GDK_FILTER_REMOVE;
96 }
97
98 const wxWCharBuffer wcstr = wxConvUTF8.cMB2WX(message);
99 size_t datalen = wcslen((const wchar_t *)wcstr);
100
101 OnReceiveMessage((const wchar_t *)wcstr, datalen*sizeof(wchar_t));
102
103 XFree(message);
104 return GDK_FILTER_REMOVE;
105 }
106 }
107
108 //if (XFilterEvent(xeve, None) == True)
109 // return GDK_FILTER_REMOVE;
110
111 return GDK_FILTER_CONTINUE;
112 }
113
send_message(Window madedit_win,const wxString & msg)114 void send_message(Window madedit_win, const wxString &msg)
115 {
116 Window mwin = XCreateSimpleWindow(g_Display, DefaultRootWindow(g_Display),
117 0,0,90,90,1,0,0);
118
119 const wxCharBuffer data_utf8 = wxConvUTF8.cWX2MB( msg );
120 size_t datalen_utf8 = strlen(data_utf8);
121
122 XChangeProperty(g_Display, mwin , g_MadEdit_atom, XA_STRING, 8,
123 PropModeReplace, (unsigned char*)(const char*)data_utf8, datalen_utf8 + 1);
124
125 XPropertyEvent eve;
126
127 eve.type=PropertyNotify;
128 eve.window=mwin;
129 eve.state=PropertyNewValue;
130 eve.atom=g_MadEdit_atom;
131 XSendEvent(g_Display, madedit_win, False, 0, (XEvent *)&eve);
132 XSync(g_Display,0);
133 sleep(1);
134
135 XDestroyWindow(g_Display, mwin);
136 }
137
138 #endif
139
140
DeleteConfig()141 void DeleteConfig()
142 {
143 if(g_DoNotSaveSettings==false)
144 {
145 wxFileConfig *cfg=(wxFileConfig *)wxFileConfig::Get(false);
146
147 if(g_ResetAllKeys==false)
148 {
149 // save MadEdit::KeyBindings
150 cfg->SetPath(wxT("/KeyBindings"));
151 MadEdit::ms_KeyBindings.SaveToConfig_New(cfg);
152 }
153 else
154 {
155 cfg->DeleteGroup(wxT("/KeyBindings"));
156 }
157 cfg->DeleteGroup(wxT("/EditKeys"));
158 cfg->DeleteGroup(wxT("/MenuKeys"));
159
160 delete cfg;
161 }
162
163 MadEdit::ms_KeyBindings.FreeCommandTextMap();
164
165 FontWidthManager::Save();
166 FontWidthManager::FreeMem();
167
168 wxFileConfig::Set(NULL);
169 }
170
OnInit()171 bool MadEditApp::OnInit()
172 {
173 wxFileName filename(GetExecutablePath());
174 filename.MakeAbsolute();
175 g_MadEditAppDir=filename.GetPath(wxPATH_GET_VOLUME|wxPATH_GET_SEPARATOR);
176
177 #ifdef __WXMSW__
178 g_MadEditHomeDir=g_MadEditAppDir;
179 #else //linux: ~/.madedit
180 g_MadEditHomeDir=wxStandardPaths::Get().GetUserDataDir() +wxFILE_SEP_PATH;
181 if(!wxDirExists(g_MadEditHomeDir))
182 {
183 wxLogNull nolog; // no error message
184 wxMkdir(g_MadEditHomeDir);
185 }
186 #endif
187
188 //wxLogMessage(g_MadEditAppDir);
189 //wxLogMessage(g_MadEditHomeDir);
190
191
192 // parse commandline to filenames, every file is with a trailing char '|', ex: filename1|filename2|
193 wxString filenames;
194 for(int i=1;i<argc;i++)
195 {
196 wxFileName filename(argv[i]);
197 filename.MakeAbsolute();
198 filenames += filename.GetFullPath() + wxT('|');
199 }
200
201
202 // init wxConfig
203 g_MadEditConfigName=g_MadEditHomeDir+ GetAppName()+ wxT(".cfg");
204
205 wxFileConfig *cfg=new wxFileConfig(wxEmptyString, wxEmptyString, g_MadEditConfigName, wxEmptyString, wxCONFIG_USE_RELATIVE_PATH|wxCONFIG_USE_NO_ESCAPE_CHARACTERS);
206 cfg->SetExpandEnvVars(false);
207 cfg->SetRecordDefaults(true);
208 wxFileConfig::Set(cfg);
209
210 bool bSingleInstance=true;
211 cfg->Read(wxT("/MadEdit/SingleInstance"), &bSingleInstance, true);
212
213 if(bSingleInstance)
214 {
215 // check SingleInstance and send filenames to previous MadEdit
216 #ifdef __WXMSW__
217 g_Mutex = CreateMutex(NULL, true, wxT("MadEdit_App"));
218 if(GetLastError() == ERROR_ALREADY_EXISTS)
219 {
220 extern const wxChar *wxCanvasClassNameNR; // class name of MadEditFrame
221 wxChar title[256]={0};
222 HWND prevapp = ::FindWindowEx(NULL, NULL, wxCanvasClassNameNR, NULL);
223 for(;;) // find wxCanvasClassNameNR
224 {
225 if(prevapp)
226 {
227 int len = ::GetWindowText(prevapp, title, 256);
228 if(len>=8 && title[len-1]==wxT(' ')) // last wchar is space?
229 {
230 title[7]=0;
231 if(lstrcmp(title, wxT("MadEdit"))==0) // compare first 7 wchars
232 break;
233 }
234 }
235 else
236 {
237 Sleep(50);
238 }
239 prevapp =::FindWindowEx(NULL, prevapp, wxCanvasClassNameNR, NULL);
240 }
241
242 if(prevapp != NULL) // send msg to the previous instance
243 {
244 WINDOWPLACEMENT wp;
245 wp.length=sizeof(WINDOWPLACEMENT);
246 GetWindowPlacement(prevapp, &wp);
247 if(wp.showCmd!=SW_SHOWMINIMIZED)
248 {
249 ::SetForegroundWindow(prevapp);
250 }
251
252 COPYDATASTRUCT cds =
253 {
254 (ULONG_PTR)prevapp,
255 DWORD((filenames.length()+1)*sizeof(wxChar)),
256 (PVOID)(const wxChar*)filenames.c_str()
257 };
258
259 ::SendMessage(prevapp, WM_COPYDATA, 0, (LPARAM) &cds);
260
261 g_DoNotSaveSettings = true;
262 DeleteConfig();
263 return false;
264 }
265 }
266 #elif defined(__WXGTK__)
267 g_Display=GDK_DISPLAY();
268 g_MadEdit_atom = XInternAtom(g_Display, "g_MadEdit_atom", False);
269 Window madedit_win;
270
271 if ((madedit_win=XGetSelectionOwner(g_Display, g_MadEdit_atom))!=None)
272 {
273 send_message(madedit_win, filenames);
274
275 g_DoNotSaveSettings = true;
276 DeleteConfig();
277 return false;
278 }
279 #endif
280 }
281
282
283 #ifdef __WXGTK__
284 bool bDisableWarningMessage = true;
285 cfg->Read(wxT("/MadEdit/DisableWarningMessage"), &bDisableWarningMessage, true);
286 if(bDisableWarningMessage)
287 {
288 // redirect "IPP request failed" message to /dev/null
289 int fdnull = open ("/dev/null", O_WRONLY, 0);
290 if(fdnull >= 0)
291 {
292 dup2(fdnull, STDERR_FILENO);
293 }
294 }
295 #endif
296
297
298 // init locale
299 wxString strlang;
300 cfg->Read(wxT("/MadEdit/Language"), &strlang);
301 int lang=g_LanguageValue[0];
302 if(!strlang.IsEmpty())
303 {
304 strlang.MakeLower();
305 for(size_t idx=1; idx<g_LanguageCount; idx++)
306 {
307 if(strlang == wxString(g_LanguageString[idx]).Lower())
308 {
309 lang=g_LanguageValue[idx];
310 break;
311 }
312 }
313 }
314
315 g_Locale.Init(lang);
316 g_Locale.AddCatalogLookupPathPrefix(wxT("./locale/"));
317 g_Locale.AddCatalogLookupPathPrefix(g_MadEditAppDir+wxT("locale/"));
318 #ifndef __WXMSW__
319 g_Locale.AddCatalogLookupPathPrefix(g_MadEditHomeDir+wxT("locale/"));
320 # if defined (DATA_DIR)
321 g_Locale.AddCatalogLookupPathPrefix(wxT(DATA_DIR"/locale/"));
322 # endif
323
324 #endif
325 g_Locale.AddCatalog(wxT("madedit"));
326
327 // set colors
328 SetHtmlColors();
329
330 bool maximize=false;
331 #ifdef __WXMSW__
332 cfg->Read(wxT("/MadEdit/WindowMaximize"), &maximize, false);
333 #endif
334 wxPoint pos=wxDefaultPosition;
335 wxSize size=wxDefaultSize;
336
337 if(!maximize)
338 {
339 long x=0,y=0,w=0,h=0;
340 cfg->Read(wxT("/MadEdit/WindowLeft"), &x);
341 cfg->Read(wxT("/MadEdit/WindowTop"), &y);
342 cfg->Read(wxT("/MadEdit/WindowWidth"), &w);
343 cfg->Read(wxT("/MadEdit/WindowHeight"), &h);
344
345 if(x+w>0 && y+h>0)
346 //if(w>0 && h>0)
347 {
348 size.x=w;
349 size.y=h;
350
351 pos.x=x;
352 pos.y=y;
353 }
354 }
355
356
357 // load FontWidth buffers
358 cfg->Read(wxT("/MadEdit/FontWidthBufferMaxCount"), &FontWidthManager::MaxCount, 10);
359 if(FontWidthManager::MaxCount < 4) FontWidthManager::MaxCount=4;
360 else if(FontWidthManager::MaxCount>40) FontWidthManager::MaxCount=40;
361 FontWidthManager::Init(g_MadEditHomeDir);
362
363
364 // create the main frame
365 MadEditFrame *myFrame = new MadEditFrame(NULL, 1 , wxEmptyString, pos, size);
366 SetTopWindow(myFrame);
367
368 #ifdef __WXMSW__
369 if(maximize)
370 {
371 WINDOWPLACEMENT wp;
372 wp.length=sizeof(WINDOWPLACEMENT);
373 GetWindowPlacement((HWND)myFrame->GetHWND(), &wp);
374 wp.showCmd=SW_SHOWMAXIMIZED;
375 SetWindowPlacement((HWND)myFrame->GetHWND(), &wp);
376 }
377 #endif
378
379 myFrame->Show(TRUE);
380
381
382 #if defined(__WXGTK__)
383 if(bSingleInstance)
384 {
385 GtkPizza *pizza = GTK_PIZZA(myFrame->m_mainWidget);
386 Window win=GDK_WINDOW_XWINDOW(pizza->bin_window);
387 XSetSelectionOwner(g_Display, g_MadEdit_atom, win, CurrentTime);
388 gdk_window_add_filter(NULL, my_gdk_filter, NULL);
389 }
390 #endif
391
392 // reload files previously opened
393 wxString files;
394 cfg->Read(wxT("/MadEdit/ReloadFilesList"), &files);
395 if(!files.IsEmpty())
396 {
397 filenames = files + filenames;
398 }
399
400 if(!filenames.IsEmpty())
401 {
402 // use OnReceiveMessage() to open the files
403 OnReceiveMessage(filenames.c_str(), (filenames.size()+1)*sizeof(wxChar));
404 }
405
406 if(myFrame->OpenedFileCount()==0)
407 {
408 myFrame->OpenFile(wxEmptyString, false);
409 }
410
411 return TRUE;
412 }
413
OnExit()414 int MadEditApp::OnExit()
415 {
416 // save settings in FrameClose();
417 return 0;
418 }
419