1 /*
2  * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
3  * http://www.gnu.org/licenses/lgpl-3.0.html
4  *
5  * $Revision: 11847 $
6  * $Id: globals.cpp 11847 2019-09-08 22:38:06Z fuscated $
7  * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/sdk/globals.cpp $
8  */
9 
10 #include "sdk_precomp.h"
11 
12 #ifndef CB_PRECOMP
13     #include <wx/choicdlg.h>
14     #include <wx/dcmemory.h>
15     #include <wx/file.h>
16     #include <wx/filename.h>
17     #include <wx/filesys.h>
18     #include <wx/image.h>
19     #include <wx/imaglist.h>
20     #include <wx/listctrl.h>
21     #include <wx/menu.h>
22     #include <wx/textdlg.h>
23 
24     #include "wx/wxscintilla.h"
25 
26     #include "cbexception.h"
27     #include "configmanager.h" // ReadBool
28     #include "filemanager.h"
29     #include "globals.h"
30     #include "logmanager.h"
31     #include "manager.h"
32     #include "projectmanager.h"
33 #endif
34 
35 #include <tinyxml.h>
36 
37 #include <wx/dirdlg.h>
38 #include <wx/display.h>
39 #include <wx/filefn.h>
40 #include <wx/fontmap.h>
41 #include <wx/msgdlg.h>
42 #include <wx/tokenzr.h>
43 
44 #include <algorithm>
45 #include <string>
46 
47 #include "filefilters.h"
48 #include "tinywxuni.h"
49 #include "filegroupsandmasks.h"
50 
51 #ifndef __WXMSW__
52     #include <unistd.h> // readlink
53     #include <sys/stat.h> // lstat
54 #endif
55 
56 const wxString DEFAULT_WORKSPACE     = _T("default.workspace");
57 const wxString DEFAULT_ARRAY_SEP     = _T(";");
58 
59 #ifndef __WXMAC__
60 const wxString DEFAULT_CONSOLE_TERM  = _T("xterm -T $TITLE -e");
61 #else
62 const wxString DEFAULT_CONSOLE_TERM  = _T("osascript -e 'tell app \"Terminal\"' -e 'activate' -e 'do script quoted form of \"$SCRIPT\"' -e 'end tell'");
63 #endif
64 const wxString DEFAULT_CONSOLE_SHELL = _T("/bin/sh -c");
65 
66 #if defined __WXMSW__
67 const wxString cbDEFAULT_OPEN_FOLDER_CMD = _T("explorer.exe /select,");
68 #elif defined __WXMAC__
69 const wxString cbDEFAULT_OPEN_FOLDER_CMD = _T("open -R");
70 #else
71 const wxString cbDEFAULT_OPEN_FOLDER_CMD = _T("xdg-open");
72 #endif
73 
GetPlatformsFromString(const wxString & platforms)74 int GetPlatformsFromString(const wxString& platforms)
75 {
76     bool pW = platforms.Contains(_("Windows"));
77     bool pU = platforms.Contains(_("Unix"));
78     bool pM = platforms.Contains(_("Mac"));
79     bool pA = platforms.Contains(_("All"));
80 
81     if (pA || (pW && pU && pM))
82         return spAll;
83 
84     int     p  = 0;
85     if (pW) p |= spWindows;
86     if (pU) p |= spUnix;
87     if (pM) p |= spMac;
88     return p;
89 }
90 
GetStringFromPlatforms(int platforms,bool forceSeparate)91 wxString GetStringFromPlatforms(int platforms, bool forceSeparate)
92 {
93     wxString ret;
94 
95     if (!forceSeparate)
96     {
97         int tmpAll = spWindows | spUnix | spMac;
98         if (((platforms & tmpAll) == tmpAll) || ((platforms & spAll) == spAll))
99             return _("All");
100     }
101 
102     if (platforms & spWindows)
103         ret << _("Windows;");
104     if (platforms & spUnix)
105         ret << _("Unix;");
106     if (platforms & spMac)
107         ret << _("Mac;");
108     return ret;
109 }
110 
111 /*
112     Killerbot : 6 Oktober 2007
113     The method has been extended with a bool to specify if the seperator should be appended at the end.
114     Ultimate goal : every client gives as a value : false.
115     Why : well a seperator should separate, there's nothing to separate at the end (like nothing to separate
116     at the beginning, we don't put one there ...), but some client code is having problems when the separator is
117     not present at the end. So for compatibility issues we have as default value for the new argument true, so the
118     old code has the same behaviour as before [TODO: make those clients no longer dependent on this stupid final separator behaviour]
119     New code will specify false as the bool value, so hoping rather soon a search on this method will have all hits showing
120     false as the last argument ....
121 */
122 
GetStringFromArray(const wxArrayString & array,const wxString & separator,bool SeparatorAtEnd)123 wxString GetStringFromArray(const wxArrayString& array, const wxString& separator, bool SeparatorAtEnd)
124 {
125     wxString out;
126     for (unsigned int i = 0; i < array.GetCount(); ++i)
127     {
128         out << array[i];
129         if (i < array.GetCount() - 1 || SeparatorAtEnd)
130             out << separator;
131     }
132     return out;
133 }
134 
GetArrayFromString(const wxString & text,const wxString & separator,bool trimSpaces)135 wxArrayString GetArrayFromString(const wxString& text, const wxString& separator, bool trimSpaces)
136 {
137     wxArrayString out;
138     wxString search = text;
139     int seplen = separator.Length();
140     while (true)
141     {
142         int idx = search.Find(separator);
143         if (idx == -1)
144         {
145             if (trimSpaces)
146             {
147                 search.Trim(false);
148                 search.Trim(true);
149             }
150             if (!search.IsEmpty())
151                 out.Add(search);
152             break;
153         }
154         wxString part = search.Left(idx);
155         search.Remove(0, idx + seplen);
156         if (trimSpaces)
157         {
158             part.Trim(false);
159             part.Trim(true);
160         }
161         if (!part.IsEmpty())
162             out.Add(part);
163     }
164     return out;
165 }
166 
GetVectorFromString(const wxString & text,const wxString & separator,bool trimSpaces)167 wxStringVec GetVectorFromString(const wxString& text, const wxString& separator, bool trimSpaces)
168 {
169     wxStringVec out;
170     wxString search = text;
171     int seplen = separator.Length();
172     while (true)
173     {
174         int idx = search.Find(separator);
175         if (idx == -1)
176         {
177             if (trimSpaces)
178             {
179                 search.Trim(false);
180                 search.Trim(true);
181             }
182             if (!search.IsEmpty())
183                 out.push_back(search);
184             break;
185         }
186         wxString part = search.Left(idx);
187         search.Remove(0, idx + seplen);
188         if (trimSpaces)
189         {
190             part.Trim(false);
191             part.Trim(true);
192         }
193         if (!part.IsEmpty())
194             out.push_back(part);
195     }
196     return out;
197 }
198 
MakeUniqueArray(const wxArrayString & array,bool caseSens)199 wxArrayString MakeUniqueArray(const wxArrayString& array, bool caseSens)
200 {
201     wxArrayString out;
202     for (size_t i = 0; i < array.GetCount(); ++i)
203     {
204         if (caseSens)
205         {
206           if (out.Index(array[i]) == wxNOT_FOUND)
207               out.Add(array[i]);
208         }
209         else
210         {
211           if (out.Index(array[i].Lower()) == wxNOT_FOUND)
212               out.Add(array[i].Lower()); // append only lower-case for the moment
213         }
214     }
215     return out;
216 }
217 
MakeUniqueString(const wxString & text,const wxString & separator,bool caseSens)218 wxString MakeUniqueString(const wxString& text, const wxString& separator, bool caseSens)
219 {
220     return GetStringFromArray( MakeUniqueArray( GetArrayFromString(text, separator), caseSens ), separator, false );
221 }
222 
AppendArray(const wxArrayString & from,wxArrayString & to)223 void AppendArray(const wxArrayString& from, wxArrayString& to)
224 {
225     for (size_t i = 0; i < from.GetCount(); ++i)
226         to.Add(from[i]);
227 }
228 
UnixFilename(const wxString & filename,wxPathFormat format)229 wxString UnixFilename(const wxString& filename, wxPathFormat format)
230 {
231     wxString result = filename;
232 
233     if (format == wxPATH_NATIVE)
234     {
235         if (platform::windows)
236             format = wxPATH_WIN;
237         else
238             format = wxPATH_UNIX;
239     }
240 
241     // Unc-names always override platform specific settings otherwise they become corrupted
242     bool unc_name = result.StartsWith(_T("\\\\"));
243     if (format == wxPATH_WIN || unc_name) // wxPATH_WIN == wxPATH_DOS == wxPATH_OS2
244     {
245         result.Replace(wxT("/"), wxT("\\"));
246         while (result.Replace(wxT("\\\\"), wxT("\\")))
247             ; // loop for recursive removal of duplicate slashes
248         if (unc_name)
249             result.Prepend(wxT("\\"));
250     }
251     else
252     {
253         result.Replace(wxT("\\"), wxT("/"));
254         while (result.Replace(wxT("//"), wxT("/")))
255             ; // loop for recursive removal of duplicate slashes
256     }
257 
258     return result;
259 }
260 
QuoteStringIfNeeded(wxString & str)261 void QuoteStringIfNeeded(wxString& str)
262 {
263     if ( NeedQuotes(str) )
264         str = wxString(_T("\"")) + str + _T("\"");
265 }
266 
NeedQuotes(const wxString & str)267 bool NeedQuotes(const wxString &str)
268 {
269     bool hasSpace = str.Find(_T(' ')) != -1;
270     bool hasParen = !platform::windows && (str.Find(_T('(')) != -1 || str.Find(_T(')')) != -1);
271     return !str.IsEmpty() && str.GetChar(0) != _T('"') && (hasSpace || hasParen);
272 }
273 
EscapeSpaces(const wxString & str)274 wxString EscapeSpaces(const wxString& str)
275 {
276     wxString ret = str;
277     if (!ret.IsEmpty() && ret[0] != _T('"') && ret[0] != _T('\''))
278     {
279         // TODO: make it faster
280         ret.Replace(_T(" "), _T("\\ "));
281         ret.Replace(_T("\t"), _T("\\\t"));
282     }
283     return ret;
284 }
285 
FileTypeOf(const wxString & filename)286 FileType FileTypeOf(const wxString& filename)
287 {
288     wxString ext = filename.AfterLast(_T('.')).Lower();
289 
290     if (ext.IsSameAs(FileFilters::ASM_EXT) ||
291         ext.IsSameAs(FileFilters::C_EXT) ||
292         ext.IsSameAs(FileFilters::CC_EXT) ||
293         ext.IsSameAs(FileFilters::CPP_EXT) ||
294         ext.IsSameAs(FileFilters::CXX_EXT) ||
295         ext.IsSameAs(FileFilters::CPLPL_EXT) ||
296         ext.IsSameAs(FileFilters::S_EXT) ||
297         ext.IsSameAs(FileFilters::SS_EXT) ||
298         ext.IsSameAs(FileFilters::S62_EXT) ||
299         ext.IsSameAs(FileFilters::D_EXT) ||
300         ext.IsSameAs(FileFilters::F_EXT) ||
301         ext.IsSameAs(FileFilters::F77_EXT) ||
302         ext.IsSameAs(FileFilters::F90_EXT) ||
303         ext.IsSameAs(FileFilters::F95_EXT) ||
304         ext.IsSameAs(FileFilters::FOR_EXT) ||
305         ext.IsSameAs(FileFilters::FPP_EXT) ||
306         ext.IsSameAs(FileFilters::F03_EXT) ||
307         ext.IsSameAs(FileFilters::F08_EXT) ||
308         ext.IsSameAs(FileFilters::JAVA_EXT)
309        )
310         return ftSource;
311 
312     else if (ext.IsSameAs(FileFilters::TPP_EXT) ||
313              ext.IsSameAs(FileFilters::TCC_EXT)
314             )
315         return ftTemplateSource;
316 
317     else if (ext.IsSameAs(FileFilters::H_EXT) ||
318              ext.IsSameAs(FileFilters::HH_EXT) ||
319              ext.IsSameAs(FileFilters::HPP_EXT) ||
320              ext.IsSameAs(FileFilters::HXX_EXT) ||
321              ext.IsSameAs(FileFilters::HPLPL_EXT) ||
322              ext.IsSameAs(FileFilters::INL_EXT)
323             )
324         return ftHeader;
325 
326     else if (ext.IsSameAs(FileFilters::CODEBLOCKS_EXT))
327         return ftCodeBlocksProject;
328 
329     else if (ext.IsSameAs(FileFilters::WORKSPACE_EXT))
330         return ftCodeBlocksWorkspace;
331 
332     else if (ext.IsSameAs(FileFilters::DEVCPP_EXT))
333         return ftDevCppProject;
334 
335     else if (ext.IsSameAs(FileFilters::MSVC6_EXT))
336         return ftMSVC6Project;
337 
338     else if (ext.IsSameAs(FileFilters::MSVC7_EXT))
339         return ftMSVC7Project;
340 
341     else if (ext.IsSameAs(FileFilters::MSVC10_EXT))
342         return ftMSVC10Project;
343 
344     else if (ext.IsSameAs(FileFilters::MSVC6_WORKSPACE_EXT))
345         return ftMSVC6Workspace;
346 
347     else if (ext.IsSameAs(FileFilters::MSVC7_WORKSPACE_EXT))
348         return ftMSVC7Workspace;
349 
350     else if (ext.IsSameAs(FileFilters::XCODE1_EXT))
351         return ftXcode1Project; // Xcode 1.0+ (Mac OS X 10.3)
352 
353     else if (ext.IsSameAs(FileFilters::XCODE2_EXT))
354         return ftXcode2Project; // Xcode 2.1+ (Mac OS X 10.4)
355 
356     else if (ext.IsSameAs(FileFilters::OBJECT_EXT))
357         return ftObject;
358 
359     else if (ext.IsSameAs(FileFilters::XRCRESOURCE_EXT))
360         return ftXRCResource;
361 
362     else if (ext.IsSameAs(FileFilters::RESOURCE_EXT))
363         return ftResource;
364 
365     else if (ext.IsSameAs(FileFilters::RESOURCEBIN_EXT))
366         return ftResourceBin;
367 
368     else if (ext.IsSameAs(FileFilters::STATICLIB_EXT))
369         return ftStaticLib;
370 
371     else if (ext.IsSameAs(FileFilters::DYNAMICLIB_EXT))
372         return ftDynamicLib;
373 
374     else if (ext.IsSameAs(FileFilters::NATIVE_EXT))
375         return ftNative;
376 
377     else if (ext.IsSameAs(FileFilters::EXECUTABLE_EXT))
378         return ftExecutable;
379 
380     else if (ext.IsSameAs(FileFilters::XML_EXT))
381         return ftXMLDocument;
382 
383     else if (ext.IsSameAs(FileFilters::SCRIPT_EXT))
384         return ftScript;
385 
386     // DrewBoo: Before giving up, see if the ProjectManager
387     // considers this extension a source or header
388     // TODO (Morten#5#): Do what DrewBoo said: Try removing the above code
389     // TODO (Morten#3#): This code should actually be a method of filegroups and masks or alike. So we collect all extension specific things in one place. As of now this would break ABI compatibilty with 08.02 so this should happen later.
390     else
391     {
392         // This code breaks ABI compatibility as noted by (Morten#3#) above.
393         // Code commented out by (pecan 2018/04/15). See http://forums.codeblocks.org/index.php/topic,22576.0.html
394         // The user can perform an equivalent objective by:
395         // 1) Fetching FilesGroupsAndMasks and adding the file extention(s) to file masks in the appropriate group.
396         // 2) Using the cbEVT_FILE_ADDED event to set the added file(s) properties (eg., compile and link).
397 
398         //ProjectManager *prjMgr = Manager::Get()->GetProjectManager();
399         //if ( prjMgr )
400         //{
401         //    const FilesGroupsAndMasks* fgm = prjMgr->GetFilesGroupsAndMasks();
402         //    // Since "ext" var has no "." prefixed, but FilesGropupsAndMasks uses
403         //    // dot notation(".ext"), prefix a '.' here.
404         //    wxString dotExt = _T(".") + ext;
405         //   if (fgm)
406         //    {
407         //       for (unsigned int i = 0; i != fgm->GetGroupsCount(); ++i)
408         //       {
409         //            if (fgm->GetGroupName(i) == _T("Sources") && fgm->MatchesMask(dotExt, i))
410         //                return ftSource;
411         //            if (fgm->GetGroupName(i) == _T("Headers") && fgm->MatchesMask(dotExt, i))
412         //                return ftHeader;
413         //       }
414         //    }
415         //}
416     }
417 
418     return ftOther;
419 }
420 
cbFindFileInPATH(const wxString & filename)421 wxString cbFindFileInPATH(const wxString &filename)
422 {
423     wxString pathValues;
424     wxGetEnv(_T("PATH"), &pathValues);
425     if (pathValues.empty())
426         return wxEmptyString;
427 
428     const wxString &sep = platform::windows ? _T(";") : _T(":");
429     wxChar pathSep = wxFileName::GetPathSeparator();
430     const wxArrayString &pathArray = GetArrayFromString(pathValues, sep);
431     for (size_t i = 0; i < pathArray.GetCount(); ++i)
432     {
433         if (wxFileExists(pathArray[i] + pathSep + filename))
434         {
435             if (pathArray[i].AfterLast(pathSep).IsSameAs(_T("bin")))
436                 return pathArray[i];
437         }
438     }
439     return wxEmptyString;
440 }
441 
DoRememberSelectedNodes(wxTreeCtrl * tree,wxArrayString & selectedItemPaths)442 void DoRememberSelectedNodes(wxTreeCtrl* tree, wxArrayString& selectedItemPaths)
443 {
444     wxArrayTreeItemIds items;
445 
446     if (tree->GetSelections(items) < 1 )
447         return;
448 
449     for (size_t i=0; i < items.GetCount(); ++i)
450     {
451         wxString path = wxEmptyString;
452         wxTreeItemId item = items[i];
453         while(item.IsOk())
454         {
455             path = _T("/") + tree->GetItemText(item) + path;
456             item = tree->GetItemParent(item);
457         }
458         if (path != wxEmptyString)
459             selectedItemPaths.Add(path);
460     }
461 }
462 
DoSelectRememberedNode(wxTreeCtrl * tree,const wxTreeItemId & parent,wxString & selectedItemPath)463 void DoSelectRememberedNode(wxTreeCtrl* tree, const wxTreeItemId& parent, wxString& selectedItemPath)
464 {
465     if (tree && !selectedItemPath.IsEmpty())
466     {
467         wxString tmpPath;
468         wxString folder;
469         tmpPath = selectedItemPath;
470         int pos = tmpPath.Find(_T('/'));
471         while (pos == 0)
472         {
473             tmpPath = tmpPath.Right(tmpPath.Length() - pos - 1);
474             pos = tmpPath.Find(_T('/'));
475         }
476 
477         folder = tmpPath.Left(pos);
478         tmpPath = tmpPath.Right(tmpPath.Length() - pos - 1);
479         wxTreeItemId item = parent;
480         wxTreeItemIdValue cookie = nullptr;
481 
482         while (item.IsOk())
483         {
484             if (tree->GetItemText(item) != folder)
485                 item = tree->GetNextSibling(item);
486             else
487             {
488                 if (pos < 0)
489                 {
490                     tree->SelectItem(item);
491                     break;
492                 }
493                 else
494                 {
495                     item = tree->GetNextChild(item, cookie);
496                     DoSelectRememberedNode(tree, item, tmpPath);
497                 }
498             }
499         }
500 
501     }
502 }
503 
DoRememberExpandedNodes(wxTreeCtrl * tree,const wxTreeItemId & parent,wxArrayString & nodePaths,wxString & path)504 bool DoRememberExpandedNodes(wxTreeCtrl* tree, const wxTreeItemId& parent, wxArrayString& nodePaths, wxString& path)
505 {
506     // remember expanded tree nodes of this tree
507     if (!tree || !parent.IsOk())
508         return false;
509 
510     wxString originalPath = path;
511     bool found = false;
512 
513     wxTreeItemIdValue cookie = nullptr;
514 
515     wxTreeItemId child = tree->GetFirstChild(parent, cookie);
516     while (child.IsOk())
517     {
518         if (tree->ItemHasChildren(child) && tree->IsExpanded(child))
519         {
520             found = true;
521             path << _T("/") << tree->GetItemText(child);
522             DoRememberExpandedNodes(tree, child, nodePaths, path);
523             nodePaths.Add(path);
524             path = originalPath;
525         }
526         child = tree->GetNextChild(parent, cookie);
527     }
528     return found;
529 }
530 
DoExpandRememberedNode(wxTreeCtrl * tree,const wxTreeItemId & parent,const wxString & path)531 void DoExpandRememberedNode(wxTreeCtrl* tree, const wxTreeItemId& parent, const wxString& path)
532 {
533     if (!path.IsEmpty())
534     {
535         //Manager::Get()->GetLogManager()->Log(mltDevDebug, path);
536         wxString tmpPath;
537         tmpPath = path;
538         wxString folder;
539         int pos = tmpPath.Find(_T('/'));
540         while (pos == 0)
541         {
542             tmpPath = tmpPath.Right(tmpPath.Length() - pos - 1);
543             pos = tmpPath.Find(_T('/'));
544         }
545 
546         if (pos < 0) // no '/'
547         {
548             folder = tmpPath;
549             tmpPath.Clear();
550         }
551         else
552         {
553             folder = tmpPath.Left(pos);
554             tmpPath = tmpPath.Right(tmpPath.Length() - pos - 1);
555         }
556 
557         //Manager::Get()->GetLogManager()->Log(mltDevDebug, "%s, %s", folder.c_str(), tmpPath.c_str());
558 
559         wxTreeItemIdValue cookie = nullptr;
560 
561         wxTreeItemId child = tree->GetFirstChild(parent, cookie);
562         while (child.IsOk())
563         {
564             wxString itemText = tree->GetItemText(child);
565             if (itemText.Matches(folder))
566             {
567                 tree->Expand(child);
568                 DoExpandRememberedNode(tree, child, tmpPath);
569                 break;
570             }
571             child = tree->GetNextChild(parent, cookie);
572         }
573     }
574 }
575 
SaveTreeState(wxTreeCtrl * tree,const wxTreeItemId & parent,wxArrayString & nodePaths,wxArrayString & selectedItemPaths)576 void SaveTreeState(wxTreeCtrl* tree, const wxTreeItemId& parent, wxArrayString& nodePaths, wxArrayString& selectedItemPaths)
577 {
578     nodePaths.Clear();
579     if (!parent.IsOk() || !tree || !tree->ItemHasChildren(parent) || !tree->IsExpanded(parent))
580         return;
581 
582     wxString tmp;
583     if (!DoRememberExpandedNodes(tree, parent, nodePaths, tmp))
584         nodePaths.Add(tmp); // just the tree root
585 
586     selectedItemPaths.Clear();
587     DoRememberSelectedNodes(tree, selectedItemPaths);
588 }
589 
RestoreTreeState(wxTreeCtrl * tree,const wxTreeItemId & parent,wxArrayString & nodePaths,wxArrayString & selectedItemPaths)590 void RestoreTreeState(wxTreeCtrl* tree, const wxTreeItemId& parent, wxArrayString& nodePaths, wxArrayString& selectedItemPaths)
591 {
592     if (!parent.IsOk() || !tree)
593         return;
594 
595     if (nodePaths.GetCount() == 0)
596     {
597         tree->Collapse(parent);
598         return;
599     }
600 
601     for (unsigned int i = 0; i < nodePaths.GetCount(); ++i)
602         DoExpandRememberedNode(tree, parent, nodePaths[i]);
603 
604     nodePaths.Clear();
605     for (unsigned int i = 0; i < selectedItemPaths.GetCount(); ++i)
606         DoSelectRememberedNode(tree, tree->GetRootItem(), selectedItemPaths[i]);
607 
608     selectedItemPaths.Clear();
609 }
610 
CreateDirRecursively(const wxString & full_path,int perms)611 bool CreateDirRecursively(const wxString& full_path, int perms)
612 {
613     wxFileName tmp(full_path);
614     if (wxDirExists(tmp.GetPath())) // early out, even if full_path is a filename, but the path already exists
615         return true;
616 
617     wxArrayString dirs;
618     wxString currdir;
619     currdir = tmp.GetVolume() + tmp.GetVolumeSeparator() + wxFILE_SEP_PATH;
620     dirs = tmp.GetDirs();
621 
622     for (size_t i = 0; i < dirs.GetCount(); ++i)
623     {
624         currdir << dirs[i];
625         if (!wxDirExists(currdir) && !wxMkdir(currdir, perms))
626             return false;
627         currdir << wxFILE_SEP_PATH;
628     }
629     return true;
630 }
631 
CreateDir(const wxString & full_path,int perms)632 bool CreateDir(const wxString& full_path, int perms)
633 {
634     if (!wxDirExists(full_path) && !wxMkdir(full_path, perms))
635         return false;
636 
637     return true;
638 }
639 
ChooseDirectory(wxWindow * parent,const wxString & message,const wxString & initialPath,const wxString & basePath,bool askToMakeRelative,bool showCreateDirButton)640 wxString ChooseDirectory(wxWindow* parent,
641                          const wxString& message,
642                          const wxString& initialPath,
643                          const wxString& basePath,
644                          bool askToMakeRelative, // relative to initialPath
645                          bool showCreateDirButton) // where supported
646 {
647     wxDirDialog dlg(parent, message, _T(""),
648                     (showCreateDirButton ? wxDD_NEW_DIR_BUTTON : 0) | wxRESIZE_BORDER);
649     dlg.SetPath(initialPath);
650     PlaceWindow(&dlg);
651     if (dlg.ShowModal() != wxID_OK)
652         return wxEmptyString;
653 
654     wxFileName path(dlg.GetPath());
655     if (askToMakeRelative && !basePath.IsEmpty())
656     {
657         // ask the user if he wants it to be kept as relative
658         if (cbMessageBox(_("Keep this as a relative path?"),
659                          _("Question"),
660                          wxICON_QUESTION | wxYES_NO) == wxID_YES)
661         {
662             path.MakeRelativeTo(basePath);
663         }
664     }
665     return path.GetFullPath();
666 }
667 
668 // Reads a wxString from a file. File must be open. File is closed automatically.
cbRead(wxFile & file,wxString & st,wxFontEncoding encoding)669 bool cbRead(wxFile& file, wxString& st, wxFontEncoding encoding)
670 {
671     st.Empty();
672     if (!file.IsOpened())
673         return false;
674 
675     int len = file.Length();
676     if (!len)
677     {
678         file.Close();
679         return true;
680     }
681 
682     char* buff = new char[len+1];
683     if (!buff) // remark by killerbot : this is useless, since when out of mem --> exception (this is not malloc you know)
684     {
685         file.Close();
686         return false;
687     }
688     file.Read((void*)buff, len);
689     file.Close();
690     buff[len]='\0';
691 
692     DetectEncodingAndConvert(buff, st, encoding);
693     delete [] buff;
694 
695     return true;
696 }
697 
cbReadFileContents(wxFile & file,wxFontEncoding encoding)698 wxString cbReadFileContents(wxFile& file, wxFontEncoding encoding)
699 {
700     wxString st;
701     cbRead(file, st, encoding);
702     return st;
703 }
704 
705 // Writes a wxString to a file. File must be open. File is closed automatically.
cbWrite(wxFile & file,const wxString & buff,wxFontEncoding encoding)706 bool cbWrite(wxFile& file, const wxString& buff, wxFontEncoding encoding)
707 {
708     bool result = false;
709     if (file.IsOpened())
710     {
711         wxCSConv conv(encoding);
712         result = file.Write(buff,conv);
713         if (result)
714             file.Flush();
715         file.Close();
716     }
717     return result;
718 }
719 
720 // Writes a wxString to a file. Takes care of unicode and uses a temporary file
721 // to save first and then it copies it over the original.
cbSaveToFile(const wxString & filename,const wxString & contents,wxFontEncoding encoding,bool bom,bool robust)722 bool cbSaveToFile(const wxString& filename, const wxString& contents, wxFontEncoding encoding,
723                   bool bom, bool robust)
724 {
725     return Manager::Get()->GetFileManager()->Save(filename, contents, encoding, bom, robust);
726 }
727 
728 // Save a TinyXML document correctly, even if the path contains unicode characters.
cbSaveTinyXMLDocument(TiXmlDocument * doc,const wxString & filename)729 bool cbSaveTinyXMLDocument(TiXmlDocument* doc, const wxString& filename)
730 {
731     return TinyXML::SaveDocument(filename, doc);
732 }
733 
734 // Return @c str as a proper unicode-compatible string
cbC2U(const char * str)735 wxString cbC2U(const char* str)
736 {
737 #if wxUSE_UNICODE
738     return wxString(str, wxConvUTF8);
739 #else
740     return wxString(str);
741 #endif
742 }
743 
744 // Return multibyte (C string) representation of the string
cbU2C(const wxString & str)745 const wxWX2MBbuf cbU2C(const wxString& str)
746 {
747     if (platform::unicode)
748         return str.mb_str(wxConvUTF8);
749     else
750         return str.mb_str();
751 }
752 
753 // Try converting a C-string from different encodings until a possible match is found.
754 // This tries the following encoding converters (in the same order):
755 // utf8, system, default and iso8859-1 to iso8859-15
DetectEncodingAndConvert(const char * strIn,wxString & strOut,wxFontEncoding possibleEncoding)756 wxFontEncoding DetectEncodingAndConvert(const char* strIn, wxString& strOut, wxFontEncoding possibleEncoding)
757 {
758     wxFontEncoding encoding = possibleEncoding;
759     strOut.Clear();
760 
761     if (platform::unicode)
762     {
763         if (possibleEncoding != wxFONTENCODING_UTF16 &&
764             possibleEncoding != wxFONTENCODING_UTF16LE &&
765             possibleEncoding != wxFONTENCODING_UTF16BE &&
766             possibleEncoding != wxFONTENCODING_UTF32 &&
767             possibleEncoding != wxFONTENCODING_UTF32LE &&
768             possibleEncoding != wxFONTENCODING_UTF32BE)
769         {
770             // crashes deep in the runtime (windows, at least)
771             // if one of the above encodings, hence the guard
772             wxCSConv conv(possibleEncoding);
773             strOut = wxString(strIn, conv);
774 
775             if (strOut.Length() == 0)
776             {
777                 // oops! wrong encoding...
778 
779                 // try utf8 first, if that was not what was asked for
780                 if (possibleEncoding != wxFONTENCODING_UTF8)
781                 {
782                     encoding = wxFONTENCODING_UTF8;
783                     strOut = wxString(strIn, wxConvUTF8);
784                 }
785 
786                 // check again: if still not right, try system encoding, default encoding and then iso8859-1 to iso8859-15
787                 if (strOut.Length() == 0)
788                 {
789                     for (int i = wxFONTENCODING_SYSTEM; i < wxFONTENCODING_ISO8859_MAX; ++i)
790                     {
791                         encoding = (wxFontEncoding)i;
792                         if (encoding == possibleEncoding)
793                             continue; // skip if same as what was asked
794                         wxCSConv csconv(encoding);
795                         strOut = wxString(strIn, csconv);
796                         if (strOut.Length() != 0)
797                             break; // got it!
798                     }
799                 }
800             }
801         }
802         else
803         {
804             strOut = (const wxChar*) strIn;
805         }
806     }
807     else
808     {
809         strOut = (const wxChar*) strIn;
810     }
811     return encoding;
812 }
813 
GetEOLStr(int eolMode)814 wxString GetEOLStr(int eolMode)
815 {
816     if (eolMode == -1)
817     {
818         static const int defEOL = platform::windows ? wxSCI_EOL_CRLF : wxSCI_EOL_LF;
819         eolMode = Manager::Get()->GetConfigManager(wxT("editor"))->ReadInt(wxT("/eol/eolmode"), defEOL);
820         if (eolMode == 3) // auto-detect EOL
821             eolMode = defEOL;
822     }
823     switch (eolMode)
824     {
825       case wxSCI_EOL_CR:
826           return wxT("\r");
827       case wxSCI_EOL_LF:
828           return wxT("\n");
829       default: // wxSCI_EOL_CRLF
830           return wxT("\r\n");
831     }
832 }
833 
URLEncode(const wxString & str)834 wxString URLEncode(const wxString &str) // not sure this is 100% standards compliant, but I hope so
835 {
836     wxString ret;
837     wxString t;
838     for (unsigned int i = 0; i < str.length(); ++i)
839     {
840         wxChar c = str[i];
841         if (  (c >= _T('A') && c <= _T('Z'))
842            || (c >= _T('a') && c <= _T('z'))
843            || (c >= _T('0') && c <= _T('9'))
844            || (c == _T('.'))
845            || (c == _T('-'))
846            || (c == _T('_')) )
847 
848             ret.Append(c);
849         else if (c == _T(' '))
850             ret.Append(_T('+'));
851         else
852         {
853             t.sprintf(_T("%%%02X"), (unsigned int) c);
854             ret.Append(t);
855         }
856     }
857     return ret;
858 }
859 
860 /** Adds support for backtick'd expressions under Windows. */
861 typedef std::map<wxString, wxString> BackticksMap;
862 BackticksMap m_Backticks; // all calls share the same cache
ExpandBackticks(wxString & str)863 wxString ExpandBackticks(wxString& str) // backticks are written in-place to str
864 {
865     wxString ret;
866 
867     // this function is not windows-only anymore because we parse the backticked command's output
868     // for compiler/linker search dirs
869 
870     size_t start = str.find(_T('`'));
871     if (start == wxString::npos)
872         return ret; // no backticks here
873     size_t end = str.find(_T('`'), start + 1);
874     if (end == wxString::npos)
875         return ret; // no ending backtick; error?
876 
877     while (start != wxString::npos && end != wxString::npos)
878     {
879         wxString cmd = str.substr(start + 1, end - start - 1);
880         cmd.Trim(true);
881         cmd.Trim(false);
882         if (cmd.IsEmpty())
883             break;
884 
885         wxString bt;
886         BackticksMap::iterator it = m_Backticks.find(cmd);
887         if (it != m_Backticks.end()) // in the cache :)
888             bt = it->second;
889         else
890         {
891             Manager::Get()->GetLogManager()->DebugLog(F(_T("Caching result of `%s`"), cmd.wx_str()));
892             wxArrayString output;
893             if (platform::WindowsVersion() >= platform::winver_WindowsNT2000)
894                 wxExecute(_T("cmd /c ") + cmd, output, wxEXEC_NODISABLE);
895             else
896                 wxExecute(cmd,                 output, wxEXEC_NODISABLE);
897             bt = GetStringFromArray(output, _T(" "), false);
898             // add it in the cache
899             m_Backticks[cmd] = bt;
900             Manager::Get()->GetLogManager()->DebugLog(_T("Cached"));
901         }
902         ret << bt << _T(' ');
903         str = str.substr(0, start) + bt + str.substr(end + 1, wxString::npos);
904 
905         // find next occurrence
906         start = str.find(_T('`'));
907         end = str.find(_T('`'), start + 1);
908     }
909 
910     return ret; // return a list of the replaced expressions
911 }
912 
CopyMenu(wxMenu * mnu,bool with_accelerators)913 wxMenu* CopyMenu(wxMenu* mnu, bool with_accelerators)
914 {
915     if (!mnu || mnu->GetMenuItemCount() < 1)
916         return nullptr;
917     wxMenu* theMenu = new wxMenu();
918 
919     for (size_t i = 0; i < mnu->GetMenuItemCount();++i)
920     {
921         wxMenuItem* tmpItem = mnu->FindItemByPosition(i);
922         wxMenuItem* theItem = new wxMenuItem(nullptr,
923                                              tmpItem->GetId(),
924                                              with_accelerators?tmpItem->GetItemLabel():tmpItem->GetItemLabelText(),
925                                              tmpItem->GetHelp(),
926                                              tmpItem->GetKind(),
927                                              CopyMenu(tmpItem->GetSubMenu()));
928         theMenu->Append(theItem);
929     }
930     return theMenu;
931 }
932 
IsWindowReallyShown(wxWindow * win)933 bool IsWindowReallyShown(wxWindow* win)
934 {
935     while (win && win->IsShown())
936     {
937         win = win->GetParent();
938         if (!win)
939             return true;
940     }
941     return false;
942 }
943 
NormalizePath(wxFileName & f,const wxString & base)944 bool NormalizePath(wxFileName& f,const wxString& base)
945 {
946     bool result = true;
947 //    if (!f.IsAbsolute())
948     {
949         f.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, base);
950         result = f.IsOk();
951     }
952     return result;
953 }
954 
955 // Checks whether 'suffix' could be a suffix of 'path' and therefore represents
956 // the same path. This is used to check whether a relative path could represent
957 // the same path as absolute path. For instance, for
958 // suffix = sdk/globals.cpp
959 // path = /home/user/codeblocks/trunk/src/sdk/globals.cpp
960 // it returns true. The function expects that 'path' is normalized and compares
961 // 'path' with 'suffix' starting from the end of the path. When it reaches .. in
962 // 'suffix' it gives up (there is no way to check relative filename names
963 // exactly) and if the path compared so far is identical, it returns true
IsSuffixOfPath(wxFileName const & suffix,wxFileName const & path)964 bool IsSuffixOfPath(wxFileName const & suffix, wxFileName const & path)
965 {
966     if (path.GetFullName() != suffix.GetFullName())
967     {
968         return false;
969     }
970 
971     wxArrayString suffixDirArray = suffix.GetDirs();
972     wxArrayString pathDirArray = path.GetDirs();
973 
974     int j = pathDirArray.GetCount() - 1;
975     for (int i = suffixDirArray.GetCount() - 1; i >= 0; i--)
976     {
977         // skip paths like /./././ and ////
978         if (suffixDirArray[i] == _T(".") || suffixDirArray[i] == _T(""))
979             continue;
980 
981         // suffix has more directories than path - cannot represent the same path
982         if (j < 0)
983             return false;
984 
985         // suffix contains ".." - from now on we cannot precisely determine
986         // whether suffix and path match - we assume that they do
987         if (suffixDirArray[i] == _T(".."))
988             return true;
989         // the corresponding directories of the two paths differ
990         else if (suffixDirArray[i] != pathDirArray[j])
991             return false;
992 
993         j--;
994     }
995 
996     if (suffix.IsAbsolute() && (j >= 0 || suffix.GetVolume() != path.GetVolume()))
997         return false;
998 
999     // 'suffix' is a suffix of 'path'
1000     return true;
1001 }
1002 
cbResolveSymLinkedDirPath(wxString & dirpath)1003 bool cbResolveSymLinkedDirPath(wxString& dirpath)
1004 {
1005 #ifdef _WIN32
1006     return false;
1007 #else
1008     if (dirpath.empty())
1009         return false;
1010     if (dirpath.Last() == wxFILE_SEP_PATH)
1011         dirpath.RemoveLast();
1012 
1013     struct stat fileStats;
1014     if (lstat(dirpath.mb_str(wxConvUTF8), &fileStats) != 0)
1015         return false;
1016 
1017     // If the path is a symbolic link, then try to resolve it.
1018     // This is needed to prevent infinite loops, when a folder is pointing to itself or its parent folder.
1019     if (S_ISLNK(fileStats.st_mode))
1020     {
1021         char buffer[4096];
1022         int result = readlink(dirpath.mb_str(wxConvUTF8), buffer, WXSIZEOF(buffer) - 1);
1023         if (result != -1)
1024         {
1025             buffer[result] = '\0'; // readlink() doesn't NUL-terminate the buffer
1026             wxString pathStr(buffer, wxConvUTF8);
1027             wxFileName fileName = wxFileName::DirName(pathStr);
1028 
1029             // If this is a relative symbolic link, we need to make it absolute.
1030             if (!fileName.IsAbsolute())
1031             {
1032                 wxFileName dirNamePath;
1033                 if (dirpath.Last() == wxFILE_SEP_PATH)
1034                     dirNamePath = wxFileName::DirName(dirpath);
1035                 else
1036                     dirNamePath = wxFileName::DirName(dirpath + wxFILE_SEP_PATH);
1037                 dirNamePath.RemoveLastDir();
1038                 // Make the new filename absolute relative to the parent folder.
1039                 fileName.MakeAbsolute(dirNamePath.GetFullPath());
1040             }
1041 
1042             wxString fullPath = fileName.GetFullPath();
1043             if (!fullPath.empty() && fullPath.Last() == wxT('.')) // this case should be handled because of a bug in wxWidgets
1044                 fullPath.RemoveLast();
1045             if (fullPath.length() > 1 && fullPath.Last() == wxFILE_SEP_PATH)
1046                 fullPath.RemoveLast();
1047             dirpath = fullPath;
1048             return true;
1049         }
1050     }
1051 
1052     return false;
1053 #endif // _WIN32
1054 }
1055 
cbResolveSymLinkedDirPathRecursive(wxString dirpath)1056 wxString cbResolveSymLinkedDirPathRecursive(wxString dirpath)
1057 {
1058     while (cbResolveSymLinkedDirPath(dirpath))
1059         ;
1060     return dirpath;
1061 }
1062 
1063 // function to check the common controls version
1064 #ifdef __WXMSW__
1065 #include <windows.h>
1066 #include <shlwapi.h>
UsesCommonControls6()1067 bool UsesCommonControls6()
1068 {
1069     bool result = false;
1070     HINSTANCE hinstDll;
1071     hinstDll = LoadLibrary(_T("comctl32.dll"));
1072     if (hinstDll)
1073     {
1074         DLLGETVERSIONPROC pDllGetVersion;
1075         pDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinstDll, "DllGetVersion");
1076 
1077         if (pDllGetVersion)
1078         {
1079             DLLVERSIONINFO dvi;
1080             HRESULT hr;
1081 
1082             ZeroMemory(&dvi, sizeof(dvi));
1083             dvi.cbSize = sizeof(dvi);
1084 
1085             hr = (*pDllGetVersion)(&dvi);
1086 
1087             if (SUCCEEDED(hr))
1088                result = dvi.dwMajorVersion == 6;
1089         }
1090 
1091         FreeLibrary(hinstDll);
1092     }
1093     return result;
1094 }
1095 #else
UsesCommonControls6()1096 bool UsesCommonControls6()
1097 {
1098     // for non-windows platforms, return true
1099     // as this is only used for knowing if bitmaps support transparency or not
1100     return true;
1101 }
1102 #endif
1103 
cbLoadImageFromFS(wxImage & image,const wxString & filename,wxBitmapType bitmapType,wxFileSystem & fs)1104 static void cbLoadImageFromFS(wxImage &image, const wxString& filename, wxBitmapType bitmapType,
1105                               wxFileSystem& fs)
1106 {
1107     // cache this, can't change while we 're running :)
1108     static bool oldCommonControls = !UsesCommonControls6();
1109 
1110     wxFSFile* f = fs.OpenFile(filename);
1111     if (f)
1112     {
1113         wxInputStream* is = f->GetStream();
1114         image.LoadFile(*is, bitmapType);
1115         delete f;
1116     }
1117     if (oldCommonControls && image.HasAlpha())
1118         image.ConvertAlphaToMask();
1119 }
1120 
cbLoadBitmap(const wxString & filename,wxBitmapType bitmapType,wxFileSystem * fs)1121 wxBitmap cbLoadBitmap(const wxString& filename, wxBitmapType bitmapType, wxFileSystem *fs)
1122 {
1123     wxImage im;
1124     if (fs)
1125         cbLoadImageFromFS(im, filename, bitmapType, *fs);
1126     else
1127     {
1128         wxFileSystem defaultFS;
1129         cbLoadImageFromFS(im, filename, bitmapType, defaultFS);
1130     }
1131 
1132     if (!im.IsOk())
1133         return wxNullBitmap;
1134 
1135     return wxBitmap(im);
1136 }
1137 
cbLoadBitmapScaled(const wxString & filename,wxBitmapType bitmapType,double scaleFactor,wxFileSystem * fs)1138 wxBitmap cbLoadBitmapScaled(const wxString& filename, wxBitmapType bitmapType, double scaleFactor,
1139                             wxFileSystem *fs)
1140 {
1141 
1142     wxImage im;
1143     if (fs)
1144         cbLoadImageFromFS(im, filename, bitmapType, *fs);
1145     else
1146     {
1147         wxFileSystem defaultFS;
1148         cbLoadImageFromFS(im, filename, bitmapType, defaultFS);
1149     }
1150 
1151     if (!im.IsOk())
1152         return wxNullBitmap;
1153 
1154 #if defined(__WXOSX__) || (defined(__WXGTK3__) && wxCHECK_VERSION(3, 1, 2))
1155     return wxBitmap(im, -1, scaleFactor);
1156 #else
1157     (void)scaleFactor;
1158     return wxBitmap(im);
1159 #endif // defined(__WXOSX__) || (defined(__WXGTK3__) && wxCHECK_VERSION(3, 1, 2))
1160 }
1161 
cbGetContentScaleFactor(const wxWindow & window)1162 double cbGetContentScaleFactor(const wxWindow &window)
1163 {
1164 #if wxCHECK_VERSION(3, 0, 0)
1165     return window.GetContentScaleFactor();
1166 #else
1167     return 1.0;
1168 #endif // wxCHECK_VERSION(3, 0, 0)
1169 }
1170 
1171 #ifdef __WXGTK__
1172 // GTK 2 doesn't support scaling.
1173 // GTK 3 supports scaling, but doesn't support fractional values.
1174 // In both cases we need to make up our on scaling value.
1175 // For other platforms the value returned by GetContentScalingFactor seems adequate.
cbGetActualContentScaleFactor(cb_unused const wxWindow & window)1176 double cbGetActualContentScaleFactor(cb_unused const wxWindow &window)
1177 {
1178 #if wxCHECK_VERSION(3, 0, 0)
1179     // It is possible to use the window to find a display, but unfortunately this doesn't work well,
1180     // because we call this function mostly on windows which haven't been shown. This leads to
1181     // warnings in the log about ClientToScreen failures.
1182     // If there are problems on multi-monitor setups we should think about some other solution. :(
1183     const wxSize ppi = wxGetDisplayPPI();
1184     return ppi.y / 96.0;
1185 #else // wxCHECK_VERSION(3, 0, 0)
1186     // This code is the simplest version which works in the most common case.
1187     // If people complain that multi-monitor setups behave strangely, this should be revised with
1188     // direct calls to GTK/GDK functions.
1189 
1190     // This function might return bad results for multi screen setups.
1191     const wxSize mm = wxGetDisplaySizeMM();
1192     if (mm.x == 0 || mm.y == 0)
1193         return 1.0;
1194     const wxSize pixels = wxGetDisplaySize();
1195 
1196     const double ppiX = wxRound((pixels.x * inches2mm) / mm.x);
1197     const double ppiY = wxRound((pixels.y * inches2mm) / mm.y);
1198 
1199     // My guess is that smaller scaling factor would look better. Probably it has effect only in
1200     // multi monitor setups where there are monitors with different dpi.
1201     return std::min(ppiX / 96.0, ppiY /96.0);
1202 #endif // wxCHECK_VERSION(3, 0, 0)
1203 }
1204 #else // __WXGTK__
cbGetActualContentScaleFactor(const wxWindow & window)1205 double cbGetActualContentScaleFactor(const wxWindow &window)
1206 {
1207     return cbGetContentScaleFactor(window);
1208 }
1209 #endif // __WXGTK__
1210 
cbFindMinSize(int targetSize,const int possibleSize[],int numWidths)1211 int cbFindMinSize(int targetSize, const int possibleSize[], int numWidths)
1212 {
1213     int selected = possibleSize[0];
1214     for (int ii = 0; ii < numWidths; ++ii)
1215     {
1216         if (possibleSize[ii] <= targetSize)
1217             selected = possibleSize[ii];
1218         else
1219             break;
1220     }
1221     return selected;
1222 }
1223 
cbFindMinSize16to64(int targetSize)1224 int cbFindMinSize16to64(int targetSize)
1225 {
1226     const int sizes[] = { 16, 20, 24, 28, 32, 40, 48, 56, 64 };
1227     return cbFindMinSize(targetSize, sizes, cbCountOf(sizes));
1228 }
1229 
cbMakeScaledImageList(int size,double scaleFactor,int & outActualSize)1230 std::unique_ptr<wxImageList> cbMakeScaledImageList(int size, double scaleFactor,
1231                                                    int &outActualSize)
1232 {
1233 #ifdef __WXMSW__
1234     outActualSize = size;
1235 #else
1236     outActualSize = floor(size / scaleFactor);
1237 #endif // __WXMSW__
1238 
1239     return std::unique_ptr<wxImageList>(new wxImageList(outActualSize, outActualSize));
1240 }
1241 
cbAddBitmapToImageList(wxImageList & list,const wxBitmap & bitmap,int size,int listSize,double scaleFactor)1242 bool cbAddBitmapToImageList(wxImageList &list, const wxBitmap &bitmap, int size, int listSize,
1243                             double scaleFactor)
1244 {
1245     if (bitmap.IsOk())
1246     {
1247         list.Add(bitmap);
1248         return true;
1249     }
1250     else
1251     {
1252         wxBitmap missingBitmap;
1253 #if wxCHECK_VERSION(3, 1, 0)
1254         missingBitmap.CreateScaled(listSize, listSize,  wxBITMAP_SCREEN_DEPTH, scaleFactor);
1255 #else
1256         (void)scaleFactor;
1257         missingBitmap.Create(listSize, listSize);
1258 #endif // wxCHECK_VERSION(3, 1, 0)
1259 
1260         {
1261             // Draw red square image. Do the drawing in a separate scope, because we need to
1262             // deselect the missing bitmap from the DC before calling the Add method.
1263             wxMemoryDC dc;
1264             dc.SelectObject(missingBitmap);
1265             dc.SetBrush(*wxRED_BRUSH);
1266             dc.DrawRectangle(0, 0, size, size);
1267         }
1268 
1269         list.Add(missingBitmap);
1270         return false;
1271     }
1272 }
1273 
1274 // this doesn't work under wxGTK, and is only needed on wxMSW, we work around it on wxGTK
1275 #ifdef __WXMSW__
SetSettingsIconsStyle(wxListCtrl * lc,SettingsIconsStyle style)1276 void SetSettingsIconsStyle(wxListCtrl* lc, SettingsIconsStyle style)
1277 {
1278     long flags = lc->GetWindowStyleFlag();
1279     switch (style)
1280     {
1281 #if wxCHECK_VERSION(3, 0, 0)
1282         case sisNoIcons: flags = (flags & ~wxLC_MASK_TYPE) | wxLC_LIST; break;
1283 #else
1284         case sisNoIcons: flags = (flags & ~wxLC_MASK_TYPE) | wxLC_SMALL_ICON; break;
1285 #endif
1286         default: flags = (flags & ~wxLC_MASK_TYPE) | wxLC_ICON; break;
1287     }
1288     lc->SetWindowStyleFlag(flags);
1289 }
1290 #else
SetSettingsIconsStyle(cb_unused wxListCtrl * lc,cb_unused SettingsIconsStyle style)1291 void SetSettingsIconsStyle(cb_unused wxListCtrl* lc, cb_unused SettingsIconsStyle style) {}
1292 #endif
1293 
GetSettingsIconsStyle(cb_unused wxListCtrl * lc)1294 SettingsIconsStyle GetSettingsIconsStyle(cb_unused wxListCtrl* lc)
1295 {
1296     return GetSettingsIconsStyle();
1297 }
1298 
GetSettingsIconsStyle()1299 SettingsIconsStyle GetSettingsIconsStyle()
1300 {
1301     return SettingsIconsStyle(Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/environment/settings_size"), 0));
1302 }
1303 
cbGetMonitorRectForWindow(wxWindow * window)1304 wxRect cbGetMonitorRectForWindow(wxWindow *window)
1305 {
1306     wxRect monitorRect;
1307     if (wxDisplay::GetCount() > 0)
1308     {
1309         int displayIdx = wxDisplay::GetFromWindow(window);
1310         if (displayIdx == wxNOT_FOUND)
1311             displayIdx = 0;
1312         wxDisplay display(displayIdx);
1313         monitorRect = display.GetClientArea();
1314         // This is needed because on Linux the client area returned for the first monitor in a twin
1315         // monitor setup with nVidia card is spanning the two monitors.
1316         // The intersection function will return just the client for the specified monitor.
1317         monitorRect = display.GetGeometry().Intersect(monitorRect);
1318     }
1319     else
1320     {
1321         int width, height;
1322         wxDisplaySize(&width, &height);
1323         monitorRect = wxRect(0, 0, width, height);
1324     }
1325     return monitorRect;
1326 }
1327 
cbGetChildWindowPlacement(ConfigManager & appConfig)1328 cbChildWindowPlacement cbGetChildWindowPlacement(ConfigManager &appConfig)
1329 {
1330     int intChildWindowPlacement = appConfig.ReadInt(wxT("/dialog_placement/child_placement"),
1331                                                     int(cbChildWindowPlacement::CenterOnParent));
1332     if (intChildWindowPlacement < 0 || intChildWindowPlacement >= 3)
1333         intChildWindowPlacement = 0;
1334 
1335     return cbChildWindowPlacement(intChildWindowPlacement);
1336 }
1337 
PlaceWindow(wxTopLevelWindow * w,cbPlaceDialogMode mode,bool enforce)1338 void PlaceWindow(wxTopLevelWindow *w, cbPlaceDialogMode mode, bool enforce)
1339 {
1340     if (!w)
1341         cbThrow(_T("Passed NULL pointer to PlaceWindow."));
1342 
1343     int the_mode;
1344 
1345     if (!enforce)
1346     {
1347         ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("app"));
1348         const cbChildWindowPlacement placement =  cbGetChildWindowPlacement(*cfg);
1349         switch (placement)
1350         {
1351             case cbChildWindowPlacement::CenterOnParent:
1352                 w->CenterOnParent();
1353                 return;
1354             case cbChildWindowPlacement::CenterOnDisplay:
1355             {
1356                 if (mode == pdlBest)
1357                     the_mode = cfg->ReadInt(_T("/dialog_placement/dialog_position"), (int) pdlCentre);
1358                 else
1359                     the_mode = (int) mode;
1360                 break;
1361             }
1362             case cbChildWindowPlacement::LeaveToWM:
1363                 return;
1364         }
1365     }
1366 
1367     wxWindow* referenceWindow = Manager::Get()->GetAppWindow();
1368     if (!referenceWindow)    // no application window available, so this is as good as we can get
1369         referenceWindow = w;
1370 
1371     const wxRect monitorRect = cbGetMonitorRectForWindow(referenceWindow);
1372     wxRect windowRect = w->GetRect();
1373 
1374     switch(the_mode)
1375     {
1376         case pdlCentre:
1377         {
1378             windowRect.x = monitorRect.x + (monitorRect.width  - windowRect.width)/2;
1379             windowRect.y = monitorRect.y  + (monitorRect.height - windowRect.height)/2;
1380         }
1381         break;
1382 
1383 
1384         case pdlHead:
1385         {
1386             windowRect.x = monitorRect.x + (monitorRect.width  - windowRect.width)/2;
1387             windowRect.y = monitorRect.y  + (monitorRect.height - windowRect.height)/3;
1388         }
1389         break;
1390 
1391 
1392         case pdlConstrain:
1393         {
1394             int x1 = windowRect.x;
1395             int x2 = windowRect.x + windowRect.width;
1396             int y1 = windowRect.y;
1397             int y2 = windowRect.y + windowRect.height;
1398 
1399             if (windowRect.width > monitorRect.width) // cannot place without clipping, so centre it
1400             {
1401                 x1 = monitorRect.x + (monitorRect.width  - windowRect.width)/2;
1402                 x2 = x1 + windowRect.width;
1403             }
1404             else
1405             {
1406                 x2 = std::min(monitorRect.GetRight(), windowRect.GetRight());
1407                 x1 = std::max(x2 - windowRect.width, monitorRect.x);
1408                 x2 = x1 + windowRect.width;
1409             }
1410             if (windowRect.height > monitorRect.height) // cannot place without clipping, so centre it
1411             {
1412                 y1 = monitorRect.y + (monitorRect.height  - windowRect.height)/2;
1413                 y2 = y1 + windowRect.height;
1414             }
1415             else
1416             {
1417                 y2 = std::min(monitorRect.GetBottom(), windowRect.GetBottom());
1418                 y1 = std::max(y2 - windowRect.height, monitorRect.y);
1419                 y2 = y1 + windowRect.height;
1420             }
1421             windowRect = wxRect(x1, y1, x2-x1, y2-y1);
1422         }
1423         break;
1424 
1425         case pdlClip:
1426         {
1427             int x1 = windowRect.x;
1428             int x2 = windowRect.x + windowRect.width;
1429             int y1 = windowRect.y;
1430             int y2 = windowRect.y + windowRect.height;
1431 
1432             x1 = std::max(x1, monitorRect.x);
1433             x2 = std::min(x2, monitorRect.GetRight());
1434             y1 = std::max(y1, monitorRect.y);
1435             y2 = std::min(y2, monitorRect.GetBottom());
1436 
1437             windowRect = wxRect(x1, y1, x2-x1, y2-y1);
1438         }
1439         break;
1440     }
1441 
1442     w->SetSize(windowRect.x,  windowRect.y, windowRect.width, windowRect.height, wxSIZE_ALLOW_MINUS_ONE);
1443 }
1444 
cbFixWindowSizeAndPlace(wxTopLevelWindow * const w)1445 void cbFixWindowSizeAndPlace(wxTopLevelWindow* const w)
1446 {
1447     if (w == nullptr)
1448         return;
1449 
1450     const int displayNumber = wxDisplay::GetFromWindow(w); // The display number this window is targeted from the layout file
1451     if (displayNumber == wxNOT_FOUND)
1452     {
1453         // this window is not on a valid display.
1454         // Place the window to the centre of the current display
1455         const wxDisplay currentDisplay;
1456 
1457         const int displayHeight = currentDisplay.GetClientArea().GetHeight();
1458         const int displayWidth  = currentDisplay.GetClientArea().GetWidth();
1459         const int panelHeight   = w->GetSize().GetHeight();
1460         const int panelWidth    = w->GetSize().GetWidth();
1461         if (panelHeight > displayHeight ||
1462             panelWidth  > displayWidth)
1463         {
1464             // If the window is bigger then the current display
1465             // Rescale the window to 1/3 of the current resolution
1466             // try to keep the aspect ratio
1467             const float windowRatio = (float) panelHeight / panelWidth;
1468             const float scaledWidth = displayWidth / 3.0f;
1469             float scaledHeight = scaledWidth * windowRatio;
1470             if (scaledHeight > displayHeight)
1471             {
1472                 // If the window is still to tall we break the aspect ratio
1473                 scaledHeight = displayHeight / 3.0f;
1474             }
1475             w->SetSize(scaledWidth, scaledHeight);
1476         }
1477         // Replace it to the centre of the screen...
1478         Manager::Get()->GetLogManager()->Log(wxString::Format(_("Window \"%s\" was on an invalid display, relocate it to main display"), w->GetTitle().ToUTF8().data()));
1479         PlaceWindow(w, pdlCentre, true);
1480     }
1481 }
1482 
cbDirAccessCheck(const wxString & dir)1483 DirAccessCheck cbDirAccessCheck(const wxString& dir)
1484 {
1485     wxString actualDir = dir;
1486     // append ending path separator if needed
1487     if (actualDir.Last() != _T('/') && actualDir.Last() != _T('\\'))
1488         actualDir << wxFILE_SEP_PATH;
1489 
1490     if (!wxDirExists(actualDir))
1491         return dacInvalidDir;
1492 
1493     wxString testFile = wxFileName::CreateTempFileName(actualDir);
1494     if (!testFile.IsEmpty())
1495     {
1496         // ok, write-access confirmed
1497         // now remove the temporary file and return success
1498         wxRemoveFile(testFile);
1499         return dacReadWrite;
1500     }
1501 
1502     // if we reached here, the directory is not writable
1503     return dacReadOnly;
1504 }
1505 
1506 
1507 namespace platform
1508 {
cb_get_os()1509     windows_version_t cb_get_os()
1510     {
1511         if (!platform::windows)
1512         {
1513             return winver_NotWindows;
1514         }
1515         else
1516         {
1517 
1518             int famWin95 = wxOS_WINDOWS_9X;
1519             int famWinNT = wxOS_WINDOWS_NT;
1520 
1521             int Major = 0;
1522             int Minor = 0;
1523             int family = wxGetOsVersion(&Major, &Minor);
1524 
1525             if (family == famWin95)
1526                  return winver_Windows9598ME;
1527 
1528             if (family == famWinNT)
1529             {
1530                 if (Major == 5 && Minor == 0)
1531                     return winver_WindowsNT2000;
1532 
1533                 if (Major == 5 && Minor == 1)
1534                     return winver_WindowsXP;
1535 
1536                 if (Major == 5 && Minor == 2)
1537                     return winver_WindowsServer2003;
1538 
1539                 if (Major == 6 && Minor == 0)
1540                     return winver_WindowsVista;
1541 
1542                 if (Major == 6 && Minor == 1)
1543                     return winver_Windows7;
1544             }
1545 
1546             return winver_UnknownWindows;
1547         }
1548     }
1549 
WindowsVersion()1550     windows_version_t WindowsVersion()
1551     {
1552         static const windows_version_t theOS = cb_get_os();
1553         return theOS;
1554     }
1555 }
1556 
1557 // returns the real path of a file by resolving symlinks
1558 // not yet optimal but should do for now
1559 // one thing that's not checked yet are circular symlinks - watch out!
realpath(const wxString & path)1560 wxString realpath(const wxString& path)
1561 {
1562 #ifdef __WXMSW__
1563     // no symlinks support on windows
1564     return path;
1565 #else
1566     char buf[2048] = {};
1567     struct stat buffer;
1568     std::string ret = (const char*)cbU2C(path);
1569     size_t lastPos = 0;
1570     size_t slashPos = ret.find('/', lastPos);
1571     while (slashPos != std::string::npos)
1572     {
1573         if (lstat(ret.substr(0, slashPos).c_str(), &buffer) == 0)
1574         {
1575             if (S_ISLNK(buffer.st_mode))
1576             {
1577                 int s = readlink(ret.substr(0, slashPos).c_str(), buf, sizeof(buf));
1578                 buf[s] = 0;
1579                 if (s > 0 && buf[0] != '/' && buf[0] != '~')
1580                 {
1581                     // relative
1582                     ret = ret.substr(0, lastPos) + buf + ret.substr(slashPos, ret.size() - slashPos);
1583                 }
1584                 else
1585                 {
1586                     // absolute
1587                     ret = buf + ret.substr(slashPos, ret.size() - slashPos);
1588 
1589                     // start again at the beginning in case the path returned also
1590                     // has symlinks. For example if using /etc/alternatives this will
1591                     // be the case
1592                     s = 0;
1593                 }
1594                 slashPos = s;
1595             }
1596         }
1597 
1598         while (ret[++slashPos] == '/')
1599             ;
1600         lastPos = slashPos;
1601         slashPos = ret.find('/', slashPos);
1602     }
1603     return cbC2U(ret.c_str());
1604 #endif
1605 }
1606 
cbMessageBox(const wxString & message,const wxString & caption,int style,wxWindow * parent,int x,int y)1607 int cbMessageBox(const wxString& message, const wxString& caption, int style, wxWindow *parent, int x, int y)
1608 {
1609     if (!parent)
1610         parent = Manager::Get()->GetAppWindow();
1611 
1612     // Cannot create a wxMessageDialog with a NULL as parent
1613     if (!parent)
1614     {
1615       // wxMessage*Box* returns any of: wxYES, wxNO, wxCANCEL, wxOK.
1616       int answer = wxMessageBox(message, caption, style, parent, x, y);
1617       switch (answer)
1618       {
1619         // map answer to the one of wxMessage*Dialog* to ensure compatibility
1620         case (wxOK):
1621           return wxID_OK;
1622         case (wxCANCEL):
1623           return wxID_CANCEL;
1624         case (wxYES):
1625           return wxID_YES;
1626         case (wxNO):
1627           return wxID_NO;
1628         default:
1629           return -1; // NOTE: Cannot happen unless wxWidgets API changes
1630       }
1631     }
1632 
1633     wxMessageDialog dlg(parent, message, caption, style, wxPoint(x,y));
1634     PlaceWindow(&dlg);
1635     // wxMessage*Dialog* returns any of wxID_OK, wxID_CANCEL, wxID_YES, wxID_NO
1636     return dlg.ShowModal();
1637 }
1638 
cbGetSingleChoiceIndex(const wxString & message,const wxString & caption,const wxArrayString & choices,wxWindow * parent,const wxSize & size,int initialSelection)1639 DLLIMPORT int cbGetSingleChoiceIndex(const wxString& message, const wxString& caption,
1640                                      const wxArrayString& choices, wxWindow *parent,
1641                                      const wxSize &size, int initialSelection)
1642 {
1643     if (!parent)
1644         parent = Manager::Get()->GetAppWindow();
1645 
1646     wxSingleChoiceDialog dialog(parent, message, caption, choices);
1647     dialog.SetSelection(initialSelection);
1648     dialog.SetSize(size);
1649     PlaceWindow(&dialog);
1650     return (dialog.ShowModal() == wxID_OK ? dialog.GetSelection() : -1);
1651 }
1652 
cbGetMultiChoiceDialog(const wxString & message,const wxString & caption,const wxArrayString & choices,wxWindow * parent,const wxSize & size,const wxArrayInt & initialSelection)1653 DLLIMPORT wxArrayInt cbGetMultiChoiceDialog(const wxString& message, const wxString& caption,
1654                                      const wxArrayString& choices, wxWindow *parent,
1655                                      const wxSize& size, const wxArrayInt& initialSelection)
1656 {
1657     if (!parent)
1658         parent = Manager::Get()->GetAppWindow();
1659 
1660     wxMultiChoiceDialog dialog(parent, message, caption, choices);
1661     dialog.SetSelections(initialSelection);
1662     dialog.SetSize(size);
1663     PlaceWindow(&dialog);
1664 
1665     if (dialog.ShowModal() == wxID_OK)
1666         return dialog.GetSelections();
1667     else
1668         return wxArrayInt();
1669 }
1670 
1671 #if wxCHECK_VERSION(3, 0, 0)
1672 const char* cbGetTextFromUserPromptStr = wxGetTextFromUserPromptStr;
1673 #else
1674 const wxChar* cbGetTextFromUserPromptStr = wxGetTextFromUserPromptStr;
1675 #endif // wxCHECK_VERSION
1676 
cbGetTextFromUser(const wxString & message,const wxString & caption,const wxString & defaultValue,wxWindow * parent,wxCoord x,wxCoord y,bool centre)1677 wxString cbGetTextFromUser(const wxString& message, const wxString& caption, const wxString& defaultValue,
1678                            wxWindow *parent, wxCoord x, wxCoord y, bool centre)
1679 {
1680     if (!parent)
1681         parent = Manager::Get()->GetAppWindow();
1682 
1683     long style = wxTextEntryDialogStyle;
1684     if (centre)
1685         style |= wxCENTRE;
1686     else
1687         style &= ~wxCENTRE;
1688 
1689     wxTextEntryDialog dialog(parent, message, caption, defaultValue, style, wxPoint(x, y));
1690     PlaceWindow(&dialog);
1691     wxString str;
1692     if (dialog.ShowModal() == wxID_OK)
1693         str = dialog.GetValue();
1694     return str;
1695 }
1696 
1697 
MakeImageList(int baseSize,wxWindow & treeParent)1698 std::unique_ptr<wxImageList> cbProjectTreeImages::MakeImageList(int baseSize, wxWindow &treeParent)
1699 {
1700     static const wxString imgs[fvsLast] =
1701     {
1702         // NOTE: Keep in sync with FileVisualState in globals.h!
1703 
1704         // The following are related to (editable, source-) file states
1705         _T("file.png"),                  // fvsNormal
1706         _T("file-missing.png"),          // fvsMissing,
1707         _T("file-modified.png"),         // fvsModified,
1708         _T("file-readonly.png"),         // fvsReadOnly,
1709 
1710         // The following are related to version control systems (vc)
1711         _T("rc-file-added.png"),         // fvsVcAdded,
1712         _T("rc-file-conflict.png"),      // fvsVcConflict,
1713         _T("rc-file-missing.png"),       // fvsVcMissing,
1714         _T("rc-file-modified.png"),      // fvsVcModified,
1715         _T("rc-file-outofdate.png"),     // fvsVcOutOfDate,
1716         _T("rc-file-uptodate.png"),      // fvsVcUpToDate,
1717         _T("rc-file-requireslock.png"),  // fvsVcRequiresLock,
1718         _T("rc-file-external.png"),      // fvsVcExternal,
1719         _T("rc-file-gotlock.png"),       // fvsVcGotLock,
1720         _T("rc-file-lockstolen.png"),    // fvsVcLockStolen,
1721         _T("rc-file-mismatch.png"),      // fvsVcMismatch,
1722         _T("rc-file-noncontrolled.png"), // fvsVcNonControlled,
1723 
1724         // The following are related to C::B workspace/project/folder/virtual
1725         _T("workspace.png"),             // fvsWorkspace,         WorkspaceIconIndex()
1726         _T("workspace-readonly.png"),    // fvsWorkspaceReadOnly, WorkspaceIconIndex(true)
1727         _T("project.png"),               // fvsProject,           ProjectIconIndex()
1728         _T("project-readonly.png"),      // fvsProjectReadOnly,   ProjectIconIndex(true)
1729         _T("folder_open.png"),           // fvsFolder,            FolderIconIndex()
1730         _T("vfolder_open.png"),          // fvsVirtualFolder,     VirtualFolderIconIndex()
1731     };
1732 
1733     const double scaleFactor = cbGetContentScaleFactor(treeParent);
1734     const int targetHeight = floor(baseSize * cbGetActualContentScaleFactor(treeParent));
1735     const int size = cbFindMinSize16to64(targetHeight);
1736 
1737     int imageListSize;
1738     std::unique_ptr<wxImageList> images = cbMakeScaledImageList(size, scaleFactor, imageListSize);
1739 
1740     const wxString prefix = ConfigManager::ReadDataPath()
1741                           + wxString::Format(_T("/resources.zip#zip:images/tree/%dx%d/"),
1742                                              size, size);
1743     wxBitmap bmp;
1744     for (const wxString &img : imgs)
1745     {
1746         bmp = cbLoadBitmapScaled(prefix + img, wxBITMAP_TYPE_PNG, scaleFactor);
1747         cbAddBitmapToImageList(*images, bmp, size, imageListSize, scaleFactor);
1748     }
1749     return images;
1750 }
1751 
WorkspaceIconIndex(bool read_only)1752 int cbProjectTreeImages::WorkspaceIconIndex(bool read_only)
1753 {
1754     if (read_only)
1755         return (int)fvsWorkspaceReadOnly;
1756 
1757     return (int)fvsWorkspace;
1758 }
1759 
ProjectIconIndex(bool read_only)1760 int cbProjectTreeImages::ProjectIconIndex(bool read_only)
1761 {
1762     if (read_only)
1763         return (int)fvsProjectReadOnly;
1764 
1765     return (int)fvsProject;
1766 }
1767 
FolderIconIndex()1768 int cbProjectTreeImages::FolderIconIndex()
1769 {
1770     return (int)fvsFolder;
1771 }
1772 
VirtualFolderIconIndex()1773 int cbProjectTreeImages::VirtualFolderIconIndex()
1774 {
1775     return (int)fvsVirtualFolder;
1776 }
1777