Document domain parameter of wxTranslations::GetTranslatedString().
[wxWidgets.git] / src / common / filehistorycmn.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/filehistorycmn.cpp
3 // Purpose: wxFileHistory class
4 // Author: Julian Smart, Vaclav Slavik, Vadim Zeitlin
5 // Created: 2010-05-03
6 // Copyright: (c) Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // ============================================================================
11 // declarations
12 // ============================================================================
13
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #include "wx/filehistory.h"
26
27 #if wxUSE_FILE_HISTORY
28
29 #include "wx/menu.h"
30 #include "wx/confbase.h"
31 #include "wx/filename.h"
32
33 // ============================================================================
34 // implementation
35 // ============================================================================
36
37 // ----------------------------------------------------------------------------
38 // private helpers
39 // ----------------------------------------------------------------------------
40
41 namespace
42 {
43
44 // return the string used for the MRU list items in the menu
45 //
46 // NB: the index n is 0-based, as usual, but the strings start from 1
47 wxString GetMRUEntryLabel(int n, const wxString& path)
48 {
49 // we need to quote '&' characters which are used for mnemonics
50 wxString pathInMenu(path);
51 pathInMenu.Replace("&", "&&");
52
53 return wxString::Format("&%d %s", n + 1, pathInMenu);
54 }
55
56 } // anonymous namespace
57
58 // ----------------------------------------------------------------------------
59 // File history (a.k.a. MRU, most recently used, files list)
60 // ----------------------------------------------------------------------------
61
62 IMPLEMENT_DYNAMIC_CLASS(wxFileHistory, wxObject)
63
64 wxFileHistoryBase::wxFileHistoryBase(size_t maxFiles, wxWindowID idBase)
65 {
66 m_fileMaxFiles = maxFiles;
67 m_idBase = idBase;
68 }
69
70 /* static */
71 wxString wxFileHistoryBase::NormalizeFileName(const wxFileName& fn)
72 {
73 // We specifically exclude wxPATH_NORM_LONG here as it can take a long time
74 // (several seconds) for network file paths under MSW, resulting in huge
75 // delays when opening a program using wxFileHistory. We also exclude
76 // wxPATH_NORM_ENV_VARS as the file names here are supposed to be "real"
77 // file names and not have any environment variables in them.
78 wxFileName fnNorm(fn);
79 fnNorm.Normalize(wxPATH_NORM_DOTS |
80 wxPATH_NORM_TILDE |
81 wxPATH_NORM_CASE |
82 wxPATH_NORM_ABSOLUTE);
83 return fnNorm.GetFullPath();
84 }
85
86 void wxFileHistoryBase::AddFileToHistory(const wxString& file)
87 {
88 // Check if we don't already have this file. Notice that we avoid
89 // wxFileName::operator==(wxString) here as it converts the string to
90 // wxFileName and then normalizes it using all normalizations which is too
91 // slow (see the comment above), so we use our own quick normalization
92 // functions and a string comparison.
93 const wxFileName fnNew(file);
94 const wxString newFile = NormalizeFileName(fnNew);
95 size_t i,
96 numFiles = m_fileHistory.size();
97 for ( i = 0; i < numFiles; i++ )
98 {
99 if ( newFile == NormalizeFileName(m_fileHistory[i]) )
100 {
101 // we do have it, move it to the top of the history
102 RemoveFileFromHistory(i);
103 numFiles--;
104 break;
105 }
106 }
107
108 // if we already have a full history, delete the one at the end
109 if ( numFiles == m_fileMaxFiles )
110 {
111 RemoveFileFromHistory(--numFiles);
112 }
113
114 // add a new menu item to all file menus (they will be updated below)
115 for ( wxList::compatibility_iterator node = m_fileMenus.GetFirst();
116 node;
117 node = node->GetNext() )
118 {
119 wxMenu * const menu = (wxMenu *)node->GetData();
120
121 if ( !numFiles && menu->GetMenuItemCount() )
122 menu->AppendSeparator();
123
124 // label doesn't matter, it will be set below anyhow, but it can't
125 // be empty (this is supposed to indicate a stock item)
126 menu->Append(m_idBase + numFiles, " ");
127 }
128
129 // insert the new file in the beginning of the file history
130 m_fileHistory.insert(m_fileHistory.begin(), file);
131 numFiles++;
132
133 // update the labels in all menus
134 for ( i = 0; i < numFiles; i++ )
135 {
136 // if in same directory just show the filename; otherwise the full path
137 const wxFileName fnOld(m_fileHistory[i]);
138
139 wxString pathInMenu;
140 if ( fnOld.GetPath() == fnNew.GetPath() )
141 {
142 pathInMenu = fnOld.GetFullName();
143 }
144 else // file in different directory
145 {
146 // absolute path; could also set relative path
147 pathInMenu = m_fileHistory[i];
148 }
149
150 for ( wxList::compatibility_iterator node = m_fileMenus.GetFirst();
151 node;
152 node = node->GetNext() )
153 {
154 wxMenu * const menu = (wxMenu *)node->GetData();
155
156 menu->SetLabel(m_idBase + i, GetMRUEntryLabel(i, pathInMenu));
157 }
158 }
159 }
160
161 void wxFileHistoryBase::RemoveFileFromHistory(size_t i)
162 {
163 size_t numFiles = m_fileHistory.size();
164 wxCHECK_RET( i < numFiles,
165 wxT("invalid index in wxFileHistoryBase::RemoveFileFromHistory") );
166
167 // delete the element from the array
168 m_fileHistory.RemoveAt(i);
169 numFiles--;
170
171 for ( wxList::compatibility_iterator node = m_fileMenus.GetFirst();
172 node;
173 node = node->GetNext() )
174 {
175 wxMenu * const menu = (wxMenu *) node->GetData();
176
177 // shift filenames up
178 for ( size_t j = i; j < numFiles; j++ )
179 {
180 menu->SetLabel(m_idBase + j, GetMRUEntryLabel(j, m_fileHistory[j]));
181 }
182
183 // delete the last menu item which is unused now
184 const wxWindowID lastItemId = m_idBase + numFiles;
185 if ( menu->FindItem(lastItemId) )
186 menu->Delete(lastItemId);
187
188 // delete the last separator too if no more files are left
189 if ( m_fileHistory.empty() )
190 {
191 const wxMenuItemList::compatibility_iterator
192 nodeLast = menu->GetMenuItems().GetLast();
193 if ( nodeLast )
194 {
195 wxMenuItem * const lastMenuItem = nodeLast->GetData();
196 if ( lastMenuItem->IsSeparator() )
197 menu->Delete(lastMenuItem);
198 }
199 //else: menu is empty somehow
200 }
201 }
202 }
203
204 void wxFileHistoryBase::UseMenu(wxMenu *menu)
205 {
206 if ( !m_fileMenus.Member(menu) )
207 m_fileMenus.Append(menu);
208 }
209
210 void wxFileHistoryBase::RemoveMenu(wxMenu *menu)
211 {
212 m_fileMenus.DeleteObject(menu);
213 }
214
215 #if wxUSE_CONFIG
216 void wxFileHistoryBase::Load(const wxConfigBase& config)
217 {
218 m_fileHistory.Clear();
219
220 wxString buf;
221 buf.Printf(wxT("file%d"), 1);
222
223 wxString historyFile;
224 while ((m_fileHistory.GetCount() < m_fileMaxFiles) &&
225 config.Read(buf, &historyFile) && !historyFile.empty())
226 {
227 m_fileHistory.Add(historyFile);
228
229 buf.Printf(wxT("file%d"), (int)m_fileHistory.GetCount()+1);
230 historyFile = wxEmptyString;
231 }
232
233 AddFilesToMenu();
234 }
235
236 void wxFileHistoryBase::Save(wxConfigBase& config)
237 {
238 size_t i;
239 for (i = 0; i < m_fileMaxFiles; i++)
240 {
241 wxString buf;
242 buf.Printf(wxT("file%d"), (int)i+1);
243 if (i < m_fileHistory.GetCount())
244 config.Write(buf, wxString(m_fileHistory[i]));
245 else
246 config.Write(buf, wxEmptyString);
247 }
248 }
249 #endif // wxUSE_CONFIG
250
251 void wxFileHistoryBase::AddFilesToMenu()
252 {
253 if ( m_fileHistory.empty() )
254 return;
255
256 for ( wxList::compatibility_iterator node = m_fileMenus.GetFirst();
257 node;
258 node = node->GetNext() )
259 {
260 AddFilesToMenu((wxMenu *) node->GetData());
261 }
262 }
263
264 void wxFileHistoryBase::AddFilesToMenu(wxMenu* menu)
265 {
266 if ( m_fileHistory.empty() )
267 return;
268
269 if ( menu->GetMenuItemCount() )
270 menu->AppendSeparator();
271
272 for ( size_t i = 0; i < m_fileHistory.GetCount(); i++ )
273 {
274 menu->Append(m_idBase + i, GetMRUEntryLabel(i, m_fileHistory[i]));
275 }
276 }
277
278 #endif // wxUSE_FILE_HISTORY