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