|
|
1.1 root 1: /*
2: Copyright (c) 2008 TrueCrypt Foundation. All rights reserved.
3:
4: Governed by the TrueCrypt License 2.4 the full text of which is contained
5: in the file License.txt included in TrueCrypt binary and source code
6: distribution packages.
7: */
8:
9: #include <wx/mimetype.h>
10: #include <wx/sckipc.h>
11: #include "System.h"
12:
13: #ifdef TC_UNIX
14: #include <signal.h>
15: #include <sys/utsname.h>
16: #include "Platform/Unix/Process.h"
17: #endif
18:
19: #include "Application.h"
20: #include "GraphicUserInterface.h"
21: #include "FatalErrorHandler.h"
22: #include "Forms/DeviceSelectionDialog.h"
23: #include "Forms/MainFrame.h"
24: #include "Forms/MountOptionsDialog.h"
25:
26: namespace TrueCrypt
27: {
28: GraphicUserInterface::GraphicUserInterface () : BackgroundMode (false), mMainFrame (nullptr)
29: {
30: #ifdef TC_UNIX
31: signal (SIGHUP, OnSignal);
32: signal (SIGINT, OnSignal);
33: signal (SIGQUIT, OnSignal);
34: signal (SIGTERM, OnSignal);
35: #endif
36:
37: #ifdef TC_MACOSX
38: wxApp::s_macHelpMenuTitleName = _("Help");
39: #endif
40: }
41:
42: GraphicUserInterface::~GraphicUserInterface ()
43: {
44: FatalErrorHandler::Deregister();
45:
46: #ifdef TC_UNIX
47: signal (SIGHUP, SIG_DFL);
48: signal (SIGINT, SIG_DFL);
49: signal (SIGQUIT, SIG_DFL);
50: signal (SIGTERM, SIG_DFL);
51: #endif
52: }
53:
54: void GraphicUserInterface::AppendToListCtrl (wxListCtrl *listCtrl, const vector <wstring> &itemFields, int imageIndex, void *itemDataPtr) const
55: {
56: InsertToListCtrl (listCtrl, listCtrl->GetItemCount(), itemFields, imageIndex, itemDataPtr);
57: }
58:
59: wxMenuItem *GraphicUserInterface::AppendToMenu (wxMenu &menu, const wxString &label, wxEvtHandler *handler, wxObjectEventFunction handlerFunction, int itemId) const
60: {
61: wxMenuItem *item = new wxMenuItem (&menu, itemId, label);
62: menu.Append (item);
63:
64: if (handler)
65: handler->Connect (item->GetId(), wxEVT_COMMAND_MENU_SELECTED, handlerFunction);
66:
67: return item;
68: }
69:
70: bool GraphicUserInterface::AskYesNo (const wxString &message, bool defaultYes, bool warning) const
71: {
72: return ShowMessage (message,
73: wxYES_NO | (warning ? wxICON_EXCLAMATION : wxICON_QUESTION) | (defaultYes ? wxYES_DEFAULT : wxNO_DEFAULT)
74: ) == wxYES;
75: }
76:
77: void GraphicUserInterface::AutoDismountVolumes (VolumeInfoList mountedVolumes, bool alwaysForce)
78: {
79: size_t mountedVolumeCount = Core->GetMountedVolumes().size();
80: try
81: {
82: wxBusyCursor busy;
83: DismountVolumes (mountedVolumes, alwaysForce ? true : GetPreferences().ForceAutoDismount, false);
84: }
85: catch (...) { }
86:
87: if (Core->GetMountedVolumes().size() < mountedVolumeCount)
88: OnVolumesAutoDismounted();
89: }
90:
91: void GraphicUserInterface::BeginInteractiveBusyState (wxWindow *window)
92: {
93: static auto_ptr <wxCursor> arrowWaitCursor;
94:
95: if (arrowWaitCursor.get() == nullptr)
96: arrowWaitCursor.reset (new wxCursor (wxCURSOR_ARROWWAIT));
97:
98: window->SetCursor (*arrowWaitCursor);
99: }
100:
101: void GraphicUserInterface::ChangePassword (shared_ptr <VolumePath> volumePath, shared_ptr <VolumePassword> password, shared_ptr <KeyfileList> keyfiles, shared_ptr <VolumePassword> newPassword, shared_ptr <KeyfileList> newKeyfiles) const
102: {
103: Gui->ShowError (_("This feature is currently supported only in text mode."));
104: throw UserAbort (SRC_POS);
105: }
106:
107: void GraphicUserInterface::ClearListCtrlSelection (wxListCtrl *listCtrl) const
108: {
109: foreach (long item, GetListCtrlSelectedItems (listCtrl))
110: listCtrl->SetItemState (item, 0, wxLIST_STATE_SELECTED);
111: }
112:
113: void GraphicUserInterface::DoShowError (const wxString &message) const
114: {
115: ShowMessage (message, wxOK | wxICON_ERROR);
116: }
117:
118: void GraphicUserInterface::DoShowInfo (const wxString &message) const
119: {
120: ShowMessage (message, wxOK | wxICON_INFORMATION);
121: }
122:
123: void GraphicUserInterface::DoShowString (const wxString &str) const
124: {
125: ShowMessage (str, wxOK);
126: }
127:
128: void GraphicUserInterface::DoShowWarning (const wxString &message) const
129: {
130: ShowMessage (message, wxOK
131: #ifndef TC_MACOSX
132: | wxICON_EXCLAMATION
133: #endif
134: );
135: }
136:
137: void GraphicUserInterface::EndInteractiveBusyState (wxWindow *window) const
138: {
139: static auto_ptr <wxCursor> arrowCursor;
140:
141: if (arrowCursor.get() == nullptr)
142: arrowCursor.reset (new wxCursor (wxCURSOR_ARROW));
143:
144: window->SetCursor (*arrowCursor);
145: }
146:
147: wxTopLevelWindow *GraphicUserInterface::GetActiveWindow () const
148: {
149: #ifdef TC_WINDOWS
150: return dynamic_cast <wxTopLevelWindow *> (wxGetActiveWindow());
151: #endif
152: if (wxTopLevelWindows.size() == 1)
153: return dynamic_cast <wxTopLevelWindow *> (wxTopLevelWindows.front());
154:
155: #ifdef __WXGTK__
156: wxLongLong startTime = wxGetLocalTimeMillis();
157: do
158: {
159: #endif
160: foreach (wxWindow *window, wxTopLevelWindows)
161: {
162: wxTopLevelWindow *topLevelWin = dynamic_cast <wxTopLevelWindow *> (window);
163: if (topLevelWin && topLevelWin->IsActive() && topLevelWin->IsShown())
164: return topLevelWin;
165: }
166: #ifdef __WXGTK__
167: Yield(); // GTK does a lot of operations asynchronously, which makes it prone to many race conditions
168: } while (wxGetLocalTimeMillis() - startTime < 500);
169: #endif
170:
171: return dynamic_cast <wxTopLevelWindow *> (GetTopWindow());
172: }
173:
174: shared_ptr <GetStringFunctor> GraphicUserInterface::GetAdminPasswordRequestHandler ()
175: {
176: struct AdminPasswordRequestHandler : public GetStringFunctor
177: {
178: virtual string operator() ()
179: {
180: wxPasswordEntryDialog dialog (Gui->GetActiveWindow(), LangString["ENTER_PASSWORD"] + L":", _("Administrator privileges required"));
181:
182: if (dialog.ShowModal() != wxID_OK)
183: throw UserAbort (SRC_POS);
184:
185: return StringConverter::ToSingle (wstring (dialog.GetValue()));
186: }
187: };
188:
189: return shared_ptr <GetStringFunctor> (new AdminPasswordRequestHandler);
190: }
191:
192: int GraphicUserInterface::GetCharHeight (wxWindow *window) const
193: {
194: int width;
195: int height;
196: window->GetTextExtent (L"a", &width, &height);
197:
198: if (height < 1)
199: return 14;
200:
201: return height;
202: }
203:
204: int GraphicUserInterface::GetCharWidth (wxWindow *window) const
205: {
206: int width;
207: int height;
208: window->GetTextExtent (L"a", &width, &height);
209:
210: if (width < 1)
211: return 7;
212:
213: return width;
214: }
215:
216: wxFont GraphicUserInterface::GetDefaultBoldFont (wxWindow *window) const
217: {
218: return wxFont (
219: #ifdef __WXGTK__
220: 9
221: #elif defined(TC_MACOSX)
222: 13
223: #else
224: 10
225: #endif
226: * GetCharHeight (window) / 13, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
227: #ifdef __WXGTK__
228: wxFONTWEIGHT_BOLD, false);
229: #elif defined(TC_MACOSX)
230: wxFONTWEIGHT_NORMAL, false);
231: #else
232: wxFONTWEIGHT_BOLD, false, L"Arial");
233: #endif
234: }
235:
236: list <long> GraphicUserInterface::GetListCtrlSelectedItems (wxListCtrl *listCtrl) const
237: {
238: list <long> selectedItems;
239:
240: long item = -1;
241: while ((item = listCtrl->GetNextItem (item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1)
242: selectedItems.push_back (item);
243:
244: return selectedItems;
245: }
246:
247: wxString GraphicUserInterface::GetListCtrlSubItemText (wxListCtrl *listCtrl, long itemIndex, int columnIndex) const
248: {
249: wxListItem item;
250: item.SetId (itemIndex);
251: item.SetColumn (columnIndex);
252: item.SetText (L"");
253:
254: if (!listCtrl->GetItem (item))
255: throw ParameterIncorrect (SRC_POS);
256:
257: return item.GetText();
258: }
259:
260: int GraphicUserInterface::GetScrollbarWidth (wxWindow *window, bool noScrollBar) const
261: {
262: int offset = 0;
263: #ifdef TC_WINDOWS
264: offset = 4;
265: #elif defined (__WXGTK__)
266: offset = 5;
267: #elif defined (TC_MACOSX)
268: offset = 9;
269: #endif
270: if (noScrollBar)
271: return offset;
272:
273: int width = wxSystemSettings::GetMetric (wxSYS_VSCROLL_X, window);
274: if (width == -1)
275: return 24;
276:
277: return width + offset;
278: }
279:
280:
281: void GraphicUserInterface::InsertToListCtrl (wxListCtrl *listCtrl, long itemIndex, const vector <wstring> &itemFields, int imageIndex, void *itemDataPtr) const
282: {
283: wxListItem item;
284: item.SetData (itemDataPtr);
285: item.SetId (itemIndex);
286: item.SetImage (imageIndex);
287: int col = 0;
288: foreach (wxString field, itemFields)
289: {
290: item.SetColumn (col++);
291: item.SetText (field);
292: if (col == 1)
293: {
294: throw_sys_if (listCtrl->InsertItem (item) == -1);
295: item.SetImage (-1);
296: continue;
297: }
298:
299: listCtrl->SetItem (item);
300: }
301: }
302:
303: bool GraphicUserInterface::IsTheOnlyTopLevelWindow (const wxWindow *window) const
304: {
305: foreach (wxWindow *w, wxTopLevelWindows)
306: {
307: if (w != window
308: && (dynamic_cast <const wxFrame *> (w) || dynamic_cast <const wxDialog *> (w))
309: && StringConverter::GetTypeName (typeid (*w)).find ("wxTaskBarIcon") == string::npos)
310: {
311: return false;
312: }
313: }
314: return true;
315: }
316:
317: void GraphicUserInterface::MoveListCtrlItem (wxListCtrl *listCtrl, long itemIndex, long newItemIndex) const
318: {
319: if (itemIndex == newItemIndex || newItemIndex < 0
320: || (newItemIndex > itemIndex && newItemIndex == listCtrl->GetItemCount()))
321: return;
322:
323: wxListItem item;
324: item.SetId (itemIndex);
325: item.SetData ((void *) nullptr);
326: item.SetImage (-1);
327:
328: if (!listCtrl->GetItem (item))
329: throw ParameterIncorrect (SRC_POS);
330:
331: int itemState = listCtrl->GetItemState (itemIndex, wxLIST_STATE_SELECTED);
332:
333: vector <wstring> itemFields (listCtrl->GetColumnCount());
334: for (size_t col = 0; col < itemFields.size(); ++col)
335: {
336: itemFields[col] = GetListCtrlSubItemText (listCtrl, itemIndex, col);
337: }
338:
339: listCtrl->DeleteItem (itemIndex);
340:
341: if (newItemIndex > listCtrl->GetItemCount() - 1)
342: AppendToListCtrl (listCtrl, itemFields, item.GetImage(), (void *) item.GetData());
343: else
344: InsertToListCtrl (listCtrl, newItemIndex, itemFields, item.GetImage(), (void *) item.GetData());
345:
346: item.SetId (newItemIndex);
347: listCtrl->SetItemState (item, itemState, wxLIST_STATE_SELECTED);
348: }
349:
350: VolumeInfoList GraphicUserInterface::MountAllDeviceHostedVolumes (MountOptions &options) const
351: {
352: MountOptionsDialog dialog (GetTopWindow(), options);
353: while (true)
354: {
355: options.Path.reset();
356:
357: if (dialog.ShowModal() != wxID_OK)
358: return VolumeInfoList();
359:
360: VolumeInfoList mountedVolumes = UserInterface::MountAllDeviceHostedVolumes (options);
361:
362: if (!mountedVolumes.empty())
363: return mountedVolumes;
364: }
365: }
366:
367: shared_ptr <VolumeInfo> GraphicUserInterface::MountVolume (MountOptions &options) const
368: {
369: shared_ptr <VolumeInfo> volume;
370:
371: if (Core->IsVolumeMounted (*options.Path))
372: {
373: ShowInfo (StringFormatter (LangString["VOLUME_ALREADY_MOUNTED"], wstring (*options.Path)));
374: return volume;
375: }
376:
377: try
378: {
379: if ((!options.Password || options.Password->IsEmpty())
380: && (!options.Keyfiles || options.Keyfiles->empty())
381: && !Core->IsPasswordCacheEmpty())
382: {
383: // Cached password
384: try
385: {
386: wxBusyCursor busy;
387: return UserInterface::MountVolume (options);
388: }
389: catch (PasswordException&) { }
390: }
391:
392: VolumePassword password;
393: KeyfileList keyfiles;
394:
395: MountOptionsDialog dialog (GetTopWindow(), options);
396: while (!volume)
397: {
398: dialog.Hide();
399: if (dialog.ShowModal() != wxID_OK)
400: return volume;
401:
402: try
403: {
404: wxBusyCursor busy;
405: volume = UserInterface::MountVolume (options);
406: }
407: catch (PasswordException &e)
408: {
409: ShowWarning (e);
410: }
411: }
412: }
413: catch (exception &e)
414: {
415: ShowError (e);
416: }
417:
418: return volume;
419: }
420:
421: void GraphicUserInterface::OnAutoDismountAllEvent ()
422: {
423: VolumeInfoList mountedVolumes = Core->GetMountedVolumes();
424:
425: if (!mountedVolumes.empty())
426: {
427: wxBusyCursor busy;
428: AutoDismountVolumes (mountedVolumes);
429: }
430: }
431:
432: bool GraphicUserInterface::OnInit ()
433: {
434: Gui = this;
435: InterfaceType = UserInterfaceType::Graphic;
436: try
437: {
438: FatalErrorHandler::Register();
439: Init();
440:
441: if (ProcessCommandLine() && !CmdLine->StartBackgroundTask)
442: {
443: Yield();
444: Application::SetExitCode (0);
445: return false;
446: }
447:
448: // Check if another instance is already running and bring its windows to foreground
449: #ifndef TC_MACOSX
450: #ifdef TC_WINDOWS
451: const wxString serverName = Application::GetName() + L"-" + wxGetUserId();
452: class Connection : public wxDDEConnection
453: {
454: public:
455: Connection () { }
456:
457: bool OnExecute (const wxString& topic, wxChar *data, int size, wxIPCFormat format)
458: {
459: if (topic == L"raise")
460: {
461: if (Gui->IsInBackgroundMode())
462: Gui->SetBackgroundMode (false);
463:
464: Gui->mMainFrame->Show (true);
465: Gui->mMainFrame->Raise ();
466: return true;
467: }
468: return false;
469: }
470: };
471: #endif
472:
473: SingleInstanceChecker.reset (new wxSingleInstanceChecker (wxString (L".") + Application::GetName() + L"-lock-" + wxGetUserId()));
474:
475: if (SingleInstanceChecker->IsAnotherRunning())
476: {
477: #ifdef TC_WINDOWS
478: class Client: public wxDDEClient
479: {
480: public:
481: Client() {};
482: wxConnectionBase *OnMakeConnection () { return new Connection; }
483: };
484:
485: auto_ptr <wxDDEClient> client (new Client);
486: auto_ptr <wxConnectionBase> connection (client->MakeConnection (L"localhost", serverName, L"raise"));
487:
488: if (connection.get() && connection->Execute (nullptr))
489: {
490: connection->Disconnect();
491: Application::SetExitCode (0);
492: return false;
493: }
494: #endif
495: wxLog::FlushActive();
496: Application::SetExitCode (1);
497: Gui->ShowInfo (_("TrueCrypt is already running."));
498: return false;
499: }
500:
501: #ifdef TC_WINDOWS
502: class Server : public wxDDEServer
503: {
504: public:
505: wxConnectionBase *OnAcceptConnection (const wxString &topic)
506: {
507: if (topic == L"raise")
508: return new Connection;
509: return nullptr;
510: }
511: };
512:
513: DDEServer.reset (new Server);
514: if (!DDEServer->Create (serverName))
515: wxLog::FlushActive();
516: #endif
517: #endif // !TC_MACOSX
518:
519: Connect (wxEVT_END_SESSION, wxCloseEventHandler (GraphicUserInterface::OnEndSession));
520: #ifdef wxHAS_POWER_EVENTS
521: Gui->Connect (wxEVT_POWER_SUSPENDING, wxPowerEventHandler (GraphicUserInterface::OnPowerSuspending));
522: #endif
523:
524: mMainFrame = new MainFrame (nullptr);
525:
526: if (CmdLine->StartBackgroundTask)
527: {
528: UserPreferences prefs = GetPreferences ();
529: prefs.BackgroundTaskEnabled = true;
530: SetPreferences (prefs);
531: mMainFrame->Close();
532: }
533: else
534: {
535: mMainFrame->Show (true);
536: }
537:
538: SetTopWindow (mMainFrame);
539: }
540: catch (exception &e)
541: {
542: ShowError (e);
543: return false;
544: }
545:
546: return true;
547: }
548:
549: void GraphicUserInterface::OnLogOff ()
550: {
551: VolumeInfoList mountedVolumes = Core->GetMountedVolumes();
552: if (GetPreferences().BackgroundTaskEnabled && GetPreferences().DismountOnLogOff
553: && !mountedVolumes.empty())
554: {
555: wxLongLong startTime = wxGetLocalTimeMillis();
556: bool timeOver = false;
557:
558: wxBusyCursor busy;
559: while (!timeOver && !mountedVolumes.empty())
560: {
561: try
562: {
563: timeOver = (wxGetLocalTimeMillis() - startTime >= 4000);
564:
565: DismountVolumes (mountedVolumes, !timeOver ? false : GetPreferences().ForceAutoDismount, timeOver);
566: OnVolumesAutoDismounted();
567:
568: break;
569: }
570: catch (UserAbort&)
571: {
572: return;
573: }
574: catch (...)
575: {
576: Thread::Sleep (500);
577: }
578:
579: VolumeInfoList mountedVolumes = Core->GetMountedVolumes();
580: }
581:
582: }
583: }
584:
585: #ifdef wxHAS_POWER_EVENTS
586: void GraphicUserInterface::OnPowerSuspending (wxPowerEvent& event)
587: {
588: size_t volumeCount = Core->GetMountedVolumes().size();
589: if (GetPreferences().BackgroundTaskEnabled && GetPreferences().DismountOnPowerSaving && volumeCount > 0)
590: {
591: OnAutoDismountAllEvent();
592:
593: if (Core->GetMountedVolumes().size() < volumeCount)
594: ShowInfoTopMost (LangString["MOUNTED_VOLUMES_AUTO_DISMOUNTED"]);
595: }
596: }
597: #endif
598:
599: void GraphicUserInterface::OnSignal (int signal)
600: {
601: #ifdef TC_UNIX
602: Gui->SingleInstanceChecker.reset();
603: _exit (1);
604: #endif
605: }
606:
607: void GraphicUserInterface::OnVolumesAutoDismounted ()
608: {
609: if (GetPreferences().WipeCacheOnAutoDismount)
610: Core->WipePasswordCache();
611: }
612:
613: void GraphicUserInterface::OpenDocument (wxWindow *parent, const wxFileName &document)
614: {
615:
616: #ifdef TC_WINDOWS
617:
618: if (int (ShellExecute (GetTopWindow() ? static_cast <HWND> (GetTopWindow()->GetHandle()) : nullptr, L"open",
619: document.GetFullPath().c_str(), nullptr, nullptr, SW_SHOWNORMAL)) >= 32)
620: return;
621:
622: #else
623: wxMimeTypesManager mimeMgr;
624: wxFileType *fileType = mimeMgr.GetFileTypeFromExtension (document.GetExt());
625: if (fileType)
626: {
627: try
628: {
629: #ifdef TC_MACOSX
630: if (wxExecute (fileType->GetOpenCommand (document.GetFullPath())) != 0)
631: return;
632: #else
633: if (wxExecute (fileType->GetOpenCommand (L"\"" + document.GetFullPath() + L"\"")) != 0)
634: return;
635: #endif
636: }
637: catch (TimeOut&)
638: {
639: return;
640: }
641: catch (Exception &e)
642: {
643: Gui->ShowError (e);
644: }
645: }
646: #endif
647: if (Gui->AskYesNo (LangString ["HELP_READER_ERROR"]))
648: OpenOnlineHelp (parent);
649: }
650:
651: wxString GraphicUserInterface::GetHomepageLinkURL (const wxString &linkId, const wxString &extraVars) const
652: {
653: wxString url = wxString (L"http://www.truecrypt.org/applink.php?version=4.3a&dest=") + linkId;
654:
655: wxString os, osVersion, architecture;
656:
657: #ifdef TC_WINDOWS
658:
659: os = L"Windows";
660:
661: #elif defined (TC_UNIX)
662: struct utsname unameData;
663: if (uname (&unameData) != -1)
664: {
665: os = StringConverter::ToWide (unameData.sysname);
666: osVersion = StringConverter::ToWide (unameData.release);
667: architecture = StringConverter::ToWide (unameData.machine);
668: //os = L"MacOSX";
669:
670: if (os == L"Darwin")
671: os = L"MacOSX";
672: }
673: else
674: os = L"Unknown";
675: #else
676: os = L"Unknown";
677: #endif
678:
679: os.Replace (L" ", L"-");
680: url += L"&os=";
681: url += os;
682:
683: osVersion.Replace (L" ", L"-");
684: url += L"&osver=";
685: url += osVersion;
686:
687: architecture.Replace (L" ", L"-");
688: url += L"&arch=";
689: url += architecture;
690:
691: if (!extraVars.empty())
692: {
693: url += L"&";
694: url += extraVars;
695: }
696:
697: return url;
698: }
699:
700: void GraphicUserInterface::OpenHomepageLink (wxWindow *parent, const wxString &linkId, const wxString &extraVars)
701: {
702: wxString url;
703:
704: BeginInteractiveBusyState (parent);
705: wxLaunchDefaultBrowser (GetHomepageLinkURL (linkId, extraVars), wxBROWSER_NEW_WINDOW);
706: Thread::Sleep (200);
707: EndInteractiveBusyState (parent);
708: }
709:
710: void GraphicUserInterface::OpenOnlineHelp (wxWindow *parent)
711: {
712: OpenHomepageLink (parent, L"help");
713: }
714:
715: void GraphicUserInterface::OpenUserGuide (wxWindow *parent)
716: {
717: try
718: {
719: wxString docPath = wstring (Application::GetExecutableDirectory());
720:
721: #ifdef TC_WINDOWS
722: docPath += L"\\TrueCrypt User Guide.pdf";
723: #elif defined (TC_MACOSX)
724: docPath += L"/../Resources/TrueCrypt User Guide.pdf";
725: #elif defined (TC_UNIX)
726: docPath = L"/usr/share/truecrypt/doc/TrueCrypt User Guide.pdf";
727: #endif
728:
729: wxFileName docFile = docPath;
730: docFile.Normalize();
731: Gui->OpenDocument (parent, docFile);
732: }
733: catch (Exception &e)
734: {
735: Gui->ShowError (e);
736: }
737: }
738:
739: DevicePath GraphicUserInterface::SelectDevice (wxWindow *parent) const
740: {
741: try
742: {
743: DeviceSelectionDialog dialog (parent);
744: if (dialog.ShowModal() == wxID_OK)
745: {
746: return dialog.SelectedDevice.Path;
747: }
748: }
749: catch (exception &e)
750: {
751: Gui->ShowError (e);
752: }
753:
754: return DevicePath();
755: }
756:
757: DirectoryPath GraphicUserInterface::SelectDirectory (wxWindow *parent, const wxString &message, bool existingOnly) const
758: {
759: return DirectoryPath (::wxDirSelector (!message.empty() ? message :
760: #ifdef __WXGTK__
761: wxDirSelectorPromptStr,
762: #else
763: L"",
764: #endif
765: L"", wxDD_DEFAULT_STYLE | (existingOnly ? wxDD_DIR_MUST_EXIST : 0), wxDefaultPosition, parent));
766: }
767:
768: FilePathList GraphicUserInterface::SelectFiles (wxWindow *parent, const wxString &caption, bool saveMode, bool allowMultiple, const list < pair <wstring, wstring> > &fileExtensions, const DirectoryPath &directory) const
769: {
770: FilePathList files;
771:
772: long style;
773: if (saveMode)
774: style = wxFD_SAVE | wxFD_OVERWRITE_PROMPT;
775: else
776: style = wxFD_OPEN | wxFD_FILE_MUST_EXIST | (allowMultiple ? wxFD_MULTIPLE : 0);
777:
778: wxString wildcards = L"*.*";
779:
780: #ifndef __WXGTK__
781: if (!fileExtensions.empty())
782: #endif
783: {
784: wildcards = LangString["ALL_FILES"] +
785: #ifdef TC_WINDOWS
786: L" (*.*)|*.*";
787: #else
788: L"|*";
789: #endif
790: typedef pair <wstring, wstring> StringPair;
791: foreach (StringPair p, fileExtensions)
792: {
793: wildcards += wxString (L"|") + p.second + L" (*." + p.first + L")|*." + p.first;
794: }
795: }
796:
797: wxFileDialog dialog (parent, caption, wstring (directory), wxString(), wildcards, style);
798:
799: if (dialog.ShowModal() == wxID_OK)
800: {
801: if (!allowMultiple)
802: files.push_back (make_shared <FilePath> (dialog.GetPath()));
803: else
804: {
805: wxArrayString paths;
806: dialog.GetPaths (paths);
807:
808: foreach (const wxString &path, paths)
809: files.push_back (make_shared <FilePath> (path));
810: }
811: }
812:
813: return files;
814: }
815:
816: FilePath GraphicUserInterface::SelectVolumeFile (wxWindow *parent, bool saveMode, const DirectoryPath &directory) const
817: {
818: list < pair <wstring, wstring> > extensions;
819: extensions.push_back (make_pair (L"tc", LangString["TC_VOLUMES"]));
820:
821: FilePathList selFiles = Gui->SelectFiles (parent, LangString[saveMode ? "OPEN_NEW_VOLUME" : "OPEN_VOL_TITLE"], saveMode, false, extensions, directory);
822:
823: if (!selFiles.empty())
824: return *selFiles.front();
825: else
826: return FilePath();
827: }
828:
829: void GraphicUserInterface::SetBackgroundMode (bool state)
830: {
831: #ifdef TC_MACOSX
832: // Hiding an iconized window on OS X apparently cannot be reversed
833: if (state && mMainFrame->IsIconized())
834: mMainFrame->Iconize (false);
835: #endif
836: mMainFrame->Show (!state);
837: if (!state)
838: {
839: if (mMainFrame->IsIconized())
840: mMainFrame->Iconize (false);
841:
842: mMainFrame->Raise();
843: }
844:
845: BackgroundMode = state;
846: }
847:
848: void GraphicUserInterface::SetListCtrlColumnWidths (wxListCtrl *listCtrl, list <int> columnWidthPermilles, bool hasVerticalScrollbar) const
849: {
850: #ifdef TC_MACOSX
851: hasVerticalScrollbar = true;
852: #endif
853: int listWidth = listCtrl->GetSize().GetWidth();
854: int minListWidth = listCtrl->GetMinSize().GetWidth();
855: if (minListWidth > listWidth)
856: listWidth = minListWidth;
857:
858: listWidth -= GetScrollbarWidth (listCtrl, !hasVerticalScrollbar);
859:
860: int col = 0;
861: int totalColWidth = 0;
862: foreach (int colWidth, columnWidthPermilles)
863: {
864: int width = listWidth * colWidth / 1000;
865: totalColWidth += width;
866:
867: if (col == listCtrl->GetColumnCount() - 1)
868: width += listWidth - totalColWidth;
869:
870: listCtrl->SetColumnWidth (col++, width);
871: }
872: }
873:
874: void GraphicUserInterface::SetListCtrlHeight (wxListCtrl *listCtrl, size_t rowCount) const
875: {
876: wxRect itemRect;
877: if (listCtrl->GetItemCount() == 0)
878: {
879: bool addedCols = false;
880: if (listCtrl->GetColumnCount() == 0)
881: {
882: listCtrl->InsertColumn (0, L".", wxLIST_FORMAT_LEFT, 1);
883: addedCols = true;
884: }
885: vector <wstring> f;
886: f.push_back (L".");
887: AppendToListCtrl (listCtrl, f);
888: listCtrl->GetItemRect (0, itemRect);
889:
890: if (addedCols)
891: listCtrl->ClearAll();
892: else
893: listCtrl->DeleteAllItems();
894: }
895: else
896: listCtrl->GetItemRect (0, itemRect);
897:
898: int headerHeight = itemRect.y;
899: #ifdef TC_WINDOWS
900: headerHeight += 4;
901: #elif defined (TC_MACOSX)
902: headerHeight += 7;
903: #elif defined (__WXGTK__)
904: headerHeight += 5;
905: #endif
906: int rowHeight = itemRect.height;
907: #ifdef TC_MACOSX
908: rowHeight += 1;
909: #endif
910: listCtrl->SetMinSize (wxSize (listCtrl->GetMinSize().GetWidth(), rowHeight * rowCount + headerHeight));
911: }
912:
913: void GraphicUserInterface::SetListCtrlWidth (wxListCtrl *listCtrl, size_t charCount, bool hasVerticalScrollbar) const
914: {
915: int width = GetCharWidth (listCtrl) * charCount;
916: #ifdef TC_MACOSX
917: if (!hasVerticalScrollbar)
918: width += GetScrollbarWidth (listCtrl);
919: #endif
920: listCtrl->SetMinSize (wxSize (width, listCtrl->GetMinSize().GetHeight()));
921: }
922:
923: void GraphicUserInterface::ShowErrorTopMost (const wxString &message) const
924: {
925: ShowMessage (message, wxOK | wxICON_ERROR, true);
926: }
927:
928: void GraphicUserInterface::ShowInfoTopMost (const wxString &message) const
929: {
930: ShowMessage (message, wxOK | wxICON_INFORMATION, true);
931: }
932:
933: int GraphicUserInterface::ShowMessage (const wxString &message, long style, bool topMost) const
934: {
935: wxString caption = Application::GetName();
936: wxString subMessage = message;
937:
938: #ifdef TC_MACOSX
939: size_t p = message.find (L"\n");
940: if (p != string::npos)
941: {
942: // Divide message to caption and info message
943: caption = message.substr (0, p);
944:
945: p = message.find_first_not_of (L'\n', p);
946: if (p != string::npos)
947: subMessage = message.substr (p);
948: else
949: subMessage.clear();
950:
951: if (subMessage.EndsWith (L"?"))
952: {
953: // Move question to caption
954: caption += wstring (L" ");
955: p = subMessage.find_last_of (L".\n");
956: if (p != string::npos)
957: {
958: if (caption.EndsWith (L": "))
959: caption[caption.size() - 2] = L'.';
960:
961: caption += subMessage.substr (subMessage.find_first_not_of (L"\n ", p + 1));
962: subMessage = subMessage.substr (0, p + 1);
963: }
964: else
965: {
966: caption += subMessage.substr (subMessage.find_first_not_of (L"\n"));
967: subMessage.clear();
968: }
969: }
970: }
971: else if (message.size() < 160)
972: {
973: caption = message;
974: subMessage.clear();
975: }
976: else
977: {
978: if (style & wxICON_EXCLAMATION)
979: caption = wxString (_("Warning")) + L':';
980: else if (style & wxICON_ERROR || style & wxICON_HAND)
981: caption = wxString (_("Error")) + L':';
982: else
983: caption.clear();
984: }
985: #endif
986: if (topMost)
987: {
988: if (!IsActive())
989: mMainFrame->RequestUserAttention (wxUSER_ATTENTION_ERROR);
990:
991: style |= wxSTAY_ON_TOP;
992: }
993:
994: return wxMessageBox (subMessage, caption, style, GetActiveWindow());
995: }
996:
997: void GraphicUserInterface::ShowWarningTopMost (const wxString &message) const
998: {
999: ShowMessage (message, wxOK
1000: #ifndef TC_MACOSX
1001: | wxICON_EXCLAMATION
1002: #endif
1003: , true);
1004: }
1005:
1006: bool GraphicUserInterface::UpdateListCtrlItem (wxListCtrl *listCtrl, long itemIndex, const vector <wstring> &itemFields) const
1007: {
1008: bool changed = false;
1009: wxListItem item;
1010: item.SetId (itemIndex);
1011: item.SetText (L"");
1012:
1013: int col = 0;
1014: foreach (wxString field, itemFields)
1015: {
1016: item.SetColumn (col++);
1017:
1018: if (!listCtrl->GetItem (item))
1019: throw ParameterIncorrect (SRC_POS);
1020:
1021: if (item.GetText() != field)
1022: {
1023: item.SetText (field);
1024: listCtrl->SetItem (item);
1025: changed = true;
1026: }
1027: }
1028: return changed;
1029: }
1030:
1031: void GraphicUserInterface::Yield () const
1032: {
1033: #ifndef TC_WINDOWS
1034: wxSafeYield (nullptr, true);
1035: #endif
1036: }
1037:
1038: DEFINE_EVENT_TYPE (TC_EVENT_THREAD_EXITING);
1039:
1040: GraphicUserInterface *Gui = nullptr;
1041: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.