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