From f31424dbe95971e8ac8ccf9f11cd79afe19485ea Mon Sep 17 00:00:00 2001 From: David Elliott Date: Mon, 18 Oct 2004 19:15:42 +0000 Subject: [PATCH] wxCocoa: Add native file dialog from Ryan Norton. Modified patch #1039368 https://sourceforge.net/tracker/?func=detail&atid=309863&aid=1039368&group_id=9863 git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@29970 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- Makefile.in | 33 +++--- build/bakefiles/files.bkl | 3 +- include/wx/cocoa/filedlg.h | 51 ++++++++ include/wx/filedlg.h | 2 +- src/cocoa/filedlg.mm | 231 +++++++++++++++++++++++++++++++++++++ 5 files changed, 302 insertions(+), 18 deletions(-) create mode 100644 include/wx/cocoa/filedlg.h create mode 100644 src/cocoa/filedlg.mm diff --git a/Makefile.in b/Makefile.in index 93a979b289..502dc111d8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1350,6 +1350,7 @@ COND_TOOLKIT_COCOA_GUI_HDR = \ wx/cocoa/dialog.h \ wx/cocoa/display.h \ wx/cocoa/drawer.h \ + wx/cocoa/filedlg.h \ wx/cocoa/font.h \ wx/cocoa/fontdlg.h \ wx/cocoa/frame.h \ @@ -2831,6 +2832,7 @@ COND_TOOLKIT_COCOA___GUI_SRC_OBJECTS = \ monodll_display.o \ monodll_drawer.o \ monodll_evtloop.o \ + monodll_filedlg.o \ monodll_font.o \ monodll_fontdlg.o \ monodll_fontenum.o \ @@ -2872,7 +2874,6 @@ COND_TOOLKIT_COCOA___GUI_SRC_OBJECTS = \ monodll_colrdlgg.o \ monodll_dirdlgg.o \ monodll_fdrepdlg.o \ - monodll_filedlgg.o \ monodll_fontdlgg.o \ monodll_imaglist.o \ monodll_listctrl.o \ @@ -3774,6 +3775,7 @@ COND_TOOLKIT_COCOA___GUI_SRC_OBJECTS_1 = \ monolib_display.o \ monolib_drawer.o \ monolib_evtloop.o \ + monolib_filedlg.o \ monolib_font.o \ monolib_fontdlg.o \ monolib_fontenum.o \ @@ -3815,7 +3817,6 @@ COND_TOOLKIT_COCOA___GUI_SRC_OBJECTS_1 = \ monolib_colrdlgg.o \ monolib_dirdlgg.o \ monolib_fdrepdlg.o \ - monolib_filedlgg.o \ monolib_fontdlgg.o \ monolib_imaglist.o \ monolib_listctrl.o \ @@ -4871,6 +4872,7 @@ COND_TOOLKIT_COCOA___GUI_SRC_OBJECTS_2 = \ coredll_display.o \ coredll_drawer.o \ coredll_evtloop.o \ + coredll_filedlg.o \ coredll_font.o \ coredll_fontdlg.o \ coredll_fontenum.o \ @@ -4912,7 +4914,6 @@ COND_TOOLKIT_COCOA___GUI_SRC_OBJECTS_2 = \ coredll_colrdlgg.o \ coredll_dirdlgg.o \ coredll_fdrepdlg.o \ - coredll_filedlgg.o \ coredll_fontdlgg.o \ coredll_imaglist.o \ coredll_listctrl.o \ @@ -5614,6 +5615,7 @@ COND_TOOLKIT_COCOA___GUI_SRC_OBJECTS_3 = \ corelib_display.o \ corelib_drawer.o \ corelib_evtloop.o \ + corelib_filedlg.o \ corelib_font.o \ corelib_fontdlg.o \ corelib_fontenum.o \ @@ -5655,7 +5657,6 @@ COND_TOOLKIT_COCOA___GUI_SRC_OBJECTS_3 = \ corelib_colrdlgg.o \ corelib_dirdlgg.o \ corelib_fdrepdlg.o \ - corelib_filedlgg.o \ corelib_fontdlgg.o \ corelib_imaglist.o \ corelib_listctrl.o \ @@ -10213,9 +10214,6 @@ monodll_sound_sdl.o: $(srcdir)/src/unix/sound_sdl.cpp $(MONODLL_ODEP) @COND_TOOLKIT_GTK_USE_GUI_1_WXUNIV_0@monodll_filedlgg.o: $(srcdir)/src/generic/filedlgg.cpp $(MONODLL_ODEP) @COND_TOOLKIT_GTK_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $< -@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@monodll_filedlgg.o: $(srcdir)/src/generic/filedlgg.cpp $(MONODLL_ODEP) -@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $< - @COND_USE_GUI_1_WXUNIV_1@monodll_filedlgg.o: $(srcdir)/src/generic/filedlgg.cpp $(MONODLL_ODEP) @COND_USE_GUI_1_WXUNIV_1@ $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $< @@ -10993,6 +10991,9 @@ monodll_sound_sdl.o: $(srcdir)/src/unix/sound_sdl.cpp $(MONODLL_ODEP) @COND_TOOLKIT_MAC_USE_GUI_1_WXUNIV_0@monodll_filedlg.o: $(srcdir)/src/mac/carbon/filedlg.cpp $(MONODLL_ODEP) @COND_TOOLKIT_MAC_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $< +@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@monodll_filedlg.o: $(srcdir)/src/cocoa/filedlg.mm $(MONODLL_ODEP) +@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $< + @COND_TOOLKIT_PM_USE_GUI_1_WXUNIV_0@monodll_filedlg.o: $(srcdir)/src/os2/filedlg.cpp $(MONODLL_ODEP) @COND_TOOLKIT_PM_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $< @@ -13387,9 +13388,6 @@ monolib_sound_sdl.o: $(srcdir)/src/unix/sound_sdl.cpp $(MONOLIB_ODEP) @COND_TOOLKIT_GTK_USE_GUI_1_WXUNIV_0@monolib_filedlgg.o: $(srcdir)/src/generic/filedlgg.cpp $(MONOLIB_ODEP) @COND_TOOLKIT_GTK_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $< -@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@monolib_filedlgg.o: $(srcdir)/src/generic/filedlgg.cpp $(MONOLIB_ODEP) -@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $< - @COND_USE_GUI_1_WXUNIV_1@monolib_filedlgg.o: $(srcdir)/src/generic/filedlgg.cpp $(MONOLIB_ODEP) @COND_USE_GUI_1_WXUNIV_1@ $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $< @@ -14167,6 +14165,9 @@ monolib_sound_sdl.o: $(srcdir)/src/unix/sound_sdl.cpp $(MONOLIB_ODEP) @COND_TOOLKIT_MAC_USE_GUI_1_WXUNIV_0@monolib_filedlg.o: $(srcdir)/src/mac/carbon/filedlg.cpp $(MONOLIB_ODEP) @COND_TOOLKIT_MAC_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $< +@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@monolib_filedlg.o: $(srcdir)/src/cocoa/filedlg.mm $(MONOLIB_ODEP) +@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $< + @COND_TOOLKIT_PM_USE_GUI_1_WXUNIV_0@monolib_filedlg.o: $(srcdir)/src/os2/filedlg.cpp $(MONOLIB_ODEP) @COND_TOOLKIT_PM_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $< @@ -16768,9 +16769,6 @@ coredll_win32.o: $(srcdir)/src/univ/themes/win32.cpp $(COREDLL_ODEP) @COND_TOOLKIT_GTK_USE_GUI_1_WXUNIV_0@coredll_filedlgg.o: $(srcdir)/src/generic/filedlgg.cpp $(COREDLL_ODEP) @COND_TOOLKIT_GTK_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(COREDLL_CXXFLAGS) $< -@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@coredll_filedlgg.o: $(srcdir)/src/generic/filedlgg.cpp $(COREDLL_ODEP) -@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(COREDLL_CXXFLAGS) $< - @COND_USE_GUI_1_WXUNIV_1@coredll_filedlgg.o: $(srcdir)/src/generic/filedlgg.cpp $(COREDLL_ODEP) @COND_USE_GUI_1_WXUNIV_1@ $(CXXC) -c -o $@ $(COREDLL_CXXFLAGS) $< @@ -17548,6 +17546,9 @@ coredll_win32.o: $(srcdir)/src/univ/themes/win32.cpp $(COREDLL_ODEP) @COND_TOOLKIT_MAC_USE_GUI_1_WXUNIV_0@coredll_filedlg.o: $(srcdir)/src/mac/carbon/filedlg.cpp $(COREDLL_ODEP) @COND_TOOLKIT_MAC_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(COREDLL_CXXFLAGS) $< +@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@coredll_filedlg.o: $(srcdir)/src/cocoa/filedlg.mm $(COREDLL_ODEP) +@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(COREDLL_CXXFLAGS) $< + @COND_TOOLKIT_PM_USE_GUI_1_WXUNIV_0@coredll_filedlg.o: $(srcdir)/src/os2/filedlg.cpp $(COREDLL_ODEP) @COND_TOOLKIT_PM_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(COREDLL_CXXFLAGS) $< @@ -19246,9 +19247,6 @@ corelib_win32.o: $(srcdir)/src/univ/themes/win32.cpp $(CORELIB_ODEP) @COND_TOOLKIT_GTK_USE_GUI_1_WXUNIV_0@corelib_filedlgg.o: $(srcdir)/src/generic/filedlgg.cpp $(CORELIB_ODEP) @COND_TOOLKIT_GTK_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $< -@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@corelib_filedlgg.o: $(srcdir)/src/generic/filedlgg.cpp $(CORELIB_ODEP) -@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $< - @COND_USE_GUI_1_WXUNIV_1@corelib_filedlgg.o: $(srcdir)/src/generic/filedlgg.cpp $(CORELIB_ODEP) @COND_USE_GUI_1_WXUNIV_1@ $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $< @@ -20026,6 +20024,9 @@ corelib_win32.o: $(srcdir)/src/univ/themes/win32.cpp $(CORELIB_ODEP) @COND_TOOLKIT_MAC_USE_GUI_1_WXUNIV_0@corelib_filedlg.o: $(srcdir)/src/mac/carbon/filedlg.cpp $(CORELIB_ODEP) @COND_TOOLKIT_MAC_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $< +@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@corelib_filedlg.o: $(srcdir)/src/cocoa/filedlg.mm $(CORELIB_ODEP) +@COND_TOOLKIT_COCOA_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $< + @COND_TOOLKIT_PM_USE_GUI_1_WXUNIV_0@corelib_filedlg.o: $(srcdir)/src/os2/filedlg.cpp $(CORELIB_ODEP) @COND_TOOLKIT_PM_USE_GUI_1_WXUNIV_0@ $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $< diff --git a/build/bakefiles/files.bkl b/build/bakefiles/files.bkl index 9c9ebcda16..de37ebea9c 100644 --- a/build/bakefiles/files.bkl +++ b/build/bakefiles/files.bkl @@ -1880,6 +1880,7 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file! src/cocoa/display.mm src/cocoa/drawer.mm src/cocoa/evtloop.mm + src/cocoa/filedlg.mm src/cocoa/font.cpp src/cocoa/fontdlg.mm src/cocoa/fontenum.mm @@ -1922,7 +1923,6 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file! src/generic/colrdlgg.cpp src/generic/dirdlgg.cpp src/generic/fdrepdlg.cpp - src/generic/filedlgg.cpp src/generic/fontdlgg.cpp src/generic/imaglist.cpp src/generic/listctrl.cpp @@ -1975,6 +1975,7 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file! wx/cocoa/dialog.h wx/cocoa/display.h wx/cocoa/drawer.h + wx/cocoa/filedlg.h wx/cocoa/font.h wx/cocoa/fontdlg.h wx/cocoa/frame.h diff --git a/include/wx/cocoa/filedlg.h b/include/wx/cocoa/filedlg.h new file mode 100644 index 0000000000..f91125b38a --- /dev/null +++ b/include/wx/cocoa/filedlg.h @@ -0,0 +1,51 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/cocoa/filedlg.h +// Purpose: wxFileDialog class +// Author: Ryan Norton +// Modified by: +// Created: 2004-10-02 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_COCOA_FILEDLG_H_ +#define _WX_COCOA_FILEDLG_H_ + +DECLARE_WXCOCOA_OBJC_CLASS(NSSavePanel); + +#define wxFileDialog wxCocoaFileDialog +//------------------------------------------------------------------------- +// wxFileDialog +//------------------------------------------------------------------------- + +class WXDLLEXPORT wxFileDialog: public wxFileDialogBase +{ + DECLARE_DYNAMIC_CLASS(wxFileDialog) + DECLARE_NO_COPY_CLASS(wxFileDialog) +public: + wxFileDialog(wxWindow *parent, + const wxString& message = wxFileSelectorPromptStr, + const wxString& defaultDir = wxEmptyString, + const wxString& defaultFile = wxEmptyString, + const wxString& wildCard = wxFileSelectorDefaultWildcardStr, + long style = 0, + const wxPoint& pos = wxDefaultPosition); + ~wxFileDialog(); + + virtual void SetPath(const wxString& path); + virtual void GetPaths(wxArrayString& paths) const; + virtual void GetFilenames(wxArrayString& files) const; + + virtual int ShowModal(); + + inline WX_NSSavePanel GetNSSavePanel() + { return (WX_NSSavePanel)m_cocoaNSWindow; } + +private: + WX_NSMutableArray m_wildcards; + wxArrayString m_fileNames; +}; + +#endif // _WX_FILEDLG_H_ + diff --git a/include/wx/filedlg.h b/include/wx/filedlg.h index 9d7cb7d30a..0b83e13601 100644 --- a/include/wx/filedlg.h +++ b/include/wx/filedlg.h @@ -167,7 +167,7 @@ wxSaveFileSelector(const wxChar *what, #elif defined(__WXMAC__) #include "wx/mac/filedlg.h" #elif defined(__WXCOCOA__) -#include "wx/generic/filedlgg.h" +#include "wx/cocoa/filedlg.h" #elif defined(__WXPM__) #include "wx/os2/filedlg.h" #endif diff --git a/src/cocoa/filedlg.mm b/src/cocoa/filedlg.mm new file mode 100644 index 0000000000..7c74ed9246 --- /dev/null +++ b/src/cocoa/filedlg.mm @@ -0,0 +1,231 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/cocoa/filedlg.mm +// Purpose: wxFileDialog for wxCocoa +// Author: Ryan Norton +// Modified by: +// Created: 2004-10-02 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#if wxUSE_FILEDLG + +#ifndef WX_PRECOMP + #include "wx/msgdlg.h" + #include "wx/filedlg.h" + #include "wx/filename.h" + #include "wx/app.h" +#endif + +#include "wx/cocoa/autorelease.h" +#include "wx/cocoa/string.h" + +#import +#import + +#import +// ============================================================================ +// implementation +// ============================================================================ + +IMPLEMENT_CLASS(wxCocoaFileDialog, wxFileDialogBase) + +// ---------------------------------------------------------------------------- +// wxFileDialog +// ---------------------------------------------------------------------------- + +wxFileDialog::wxFileDialog(wxWindow *parent, const wxString& message, + const wxString& defaultDir, const wxString& defaultFileName, + const wxString& wildCard, long style, const wxPoint& pos) +: wxFileDialogBase(parent, message, defaultDir, defaultFileName, + wildCard, style, pos) +{ + wxTopLevelWindows.Append(this); + + wxASSERT(CreateBase(parent,wxID_ANY,pos,wxDefaultSize,style,wxDefaultValidator,wxDialogNameStr)); + + if ( parent ) + parent->AddChild(this); + + m_cocoaNSWindow = nil; + m_cocoaNSView = nil; + + //Init the wildcard array + m_wildcards = [[NSMutableArray alloc] initWithCapacity:0]; + + //If the user requests to save - use a NSSavePanel + //else use a NSOpenPanel + if (m_dialogStyle & wxSAVE) + { + SetNSPanel([NSSavePanel savePanel]); + + [GetNSSavePanel() setTitle:wxNSStringWithWxString(message)]; + + [GetNSSavePanel() setPrompt:@"Save"]; + [GetNSSavePanel() setTreatsFilePackagesAsDirectories:YES]; + [GetNSSavePanel() setCanSelectHiddenExtension:YES]; + +// Cached as per-app in obj-c +// [GetNSSavePanel() setExtensionHidden:YES]; + + // + // NB: Note that only Panther supports wildcards + // with save dialogs - not that wildcards in save + // dialogs are all that useful, anyway :) + // + } + else //m_dialogStyle & wxOPEN + { + SetNSPanel([NSOpenPanel openPanel]); + [m_cocoaNSWindow setTitle:wxNSStringWithWxString(message)]; + + [(NSOpenPanel*)m_cocoaNSWindow setAllowsMultipleSelection:(m_dialogStyle & wxMULTIPLE)]; + [(NSOpenPanel*)m_cocoaNSWindow setResolvesAliases:YES]; + [(NSOpenPanel*)m_cocoaNSWindow setCanChooseFiles:YES]; + [(NSOpenPanel*)m_cocoaNSWindow setCanChooseDirectories:NO]; + [GetNSSavePanel() setPrompt:@"Open"]; + + //convert wildcards - open panel only takes file extensions - + //no actual wildcards here :) + size_t lastwcpos = 0; + bool bDescription = true; + size_t i; + for(i = wildCard.find('|'); + i != wxString::npos; + i = wildCard.find('|', lastwcpos+1)) + { + size_t oldi = i; + + if(!bDescription) + { + bDescription = !bDescription; + + //work backwards looking for a period + while(i != lastwcpos && wildCard[--i] != '.') {} + + if(i == lastwcpos) + { + //no extension - can't use this wildcard + lastwcpos = oldi; + continue; + } + + [m_wildcards addObject:wxNSStringWithWxString(wildCard.substr(i+1, oldi-i-1))]; + } + else + bDescription = !bDescription; + lastwcpos = oldi; + } + + if (!bDescription) + { + //get last wildcard + size_t oldi = wildCard.length(); + i = oldi; + + //work backwards looking for a period + while(i != lastwcpos && wildCard[--i] != '.') {} + + if(i != lastwcpos) + [m_wildcards addObject:wxNSStringWithWxString(wildCard.substr(i+1, oldi-i-1))]; + } + + if ([m_wildcards count] == 0) + { + [m_wildcards release]; + m_wildcards = nil; + } + } +} + +wxFileDialog::~wxFileDialog() +{ + [m_wildcards release]; +} + +void wxFileDialog::GetPaths(wxArrayString& paths) const +{ + paths.Empty(); + + wxString dir(m_dir); + if ( m_dir.Last() != _T('\\') ) + dir += _T('\\'); + + size_t count = m_fileNames.GetCount(); + for ( size_t n = 0; n < count; n++ ) + { + if (wxFileName(m_fileNames[n]).IsAbsolute()) + paths.Add(m_fileNames[n]); + else + paths.Add(dir + m_fileNames[n]); + } +} + +void wxFileDialog::GetFilenames(wxArrayString& files) const +{ + files = m_fileNames; +} + +void wxFileDialog::SetPath(const wxString& path) +{ + wxString ext; + wxSplitPath(path, &m_dir, &m_fileName, &ext); + if ( !ext.empty() ) + m_fileName << _T('.') << ext; +} + +int wxFileDialog::ShowModal() +{ + wxAutoNSAutoreleasePool thePool; + + m_fileNames.Empty(); + + int nResult; + + if (m_dialogStyle & wxSAVE) + { + nResult = [GetNSSavePanel() + runModalForDirectory:wxNSStringWithWxString(m_dir) + file:wxNSStringWithWxString(m_fileName)]; + + if (nResult == NSOKButton) + { + m_fileNames.Add(wxStringWithNSString([GetNSSavePanel() filename])); + m_path = m_fileNames[0]; + } + } + else //m_dialogStyle & wxOPEN + { + nResult = [(NSOpenPanel*)m_cocoaNSWindow + runModalForDirectory:wxNSStringWithWxString(m_dir) + file:wxNSStringWithWxString(m_fileName) + types:m_wildcards]; + + if (nResult == NSOKButton) + { + for(unsigned i = 0; i < [[(NSOpenPanel*)m_cocoaNSWindow filenames] count]; ++i) + { + m_fileNames.Add(wxStringWithNSString([[(NSOpenPanel*)m_cocoaNSWindow filenames] objectAtIndex:(i)])); + } + + m_path = m_fileNames[0]; + } + } + + return nResult == NSOKButton ? wxID_OK : wxID_CANCEL; +} + +#endif // wxUSE_FILEDLG + -- 2.45.2