1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/filehistorycmn.cpp
3 // Purpose: wxFileHistory class
4 // Author: Julian Smart, Vaclav Slavik, Vadim Zeitlin
6 // Copyright: (c) Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // ============================================================================
12 // ============================================================================
14 // ----------------------------------------------------------------------------
16 // ----------------------------------------------------------------------------
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
25 #include "wx/filehistory.h"
27 #if wxUSE_FILE_HISTORY
30 #include "wx/confbase.h"
31 #include "wx/filename.h"
33 // ============================================================================
35 // ============================================================================
37 // ----------------------------------------------------------------------------
39 // ----------------------------------------------------------------------------
44 // return the string used for the MRU list items in the menu
46 // NB: the index n is 0-based, as usual, but the strings start from 1
47 wxString
GetMRUEntryLabel(int n
, const wxString
& path
)
49 // we need to quote '&' characters which are used for mnemonics
50 wxString
pathInMenu(path
);
51 pathInMenu
.Replace("&", "&&");
53 return wxString::Format("&%d %s", n
+ 1, pathInMenu
);
56 } // anonymous namespace
58 // ----------------------------------------------------------------------------
59 // File history (a.k.a. MRU, most recently used, files list)
60 // ----------------------------------------------------------------------------
62 IMPLEMENT_DYNAMIC_CLASS(wxFileHistory
, wxObject
)
64 wxFileHistoryBase::wxFileHistoryBase(size_t maxFiles
, wxWindowID idBase
)
66 m_fileMaxFiles
= maxFiles
;
71 wxString
wxFileHistoryBase::NormalizeFileName(const wxFileName
& fn
)
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
|
82 wxPATH_NORM_ABSOLUTE
);
83 return fnNorm
.GetFullPath();
86 void wxFileHistoryBase::AddFileToHistory(const wxString
& file
)
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
);
96 numFiles
= m_fileHistory
.size();
97 for ( i
= 0; i
< numFiles
; i
++ )
99 if ( newFile
== NormalizeFileName(m_fileHistory
[i
]) )
101 // we do have it, move it to the top of the history
102 RemoveFileFromHistory(i
);
108 // if we already have a full history, delete the one at the end
109 if ( numFiles
== m_fileMaxFiles
)
111 RemoveFileFromHistory(--numFiles
);
114 // add a new menu item to all file menus (they will be updated below)
115 for ( wxList::compatibility_iterator node
= m_fileMenus
.GetFirst();
117 node
= node
->GetNext() )
119 wxMenu
* const menu
= (wxMenu
*)node
->GetData();
121 if ( !numFiles
&& menu
->GetMenuItemCount() )
122 menu
->AppendSeparator();
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
, " ");
129 // insert the new file in the beginning of the file history
130 m_fileHistory
.insert(m_fileHistory
.begin(), file
);
133 // update the labels in all menus
134 for ( i
= 0; i
< numFiles
; i
++ )
136 // if in same directory just show the filename; otherwise the full path
137 const wxFileName
fnOld(m_fileHistory
[i
]);
140 if ( fnOld
.GetPath() == fnNew
.GetPath() )
142 pathInMenu
= fnOld
.GetFullName();
144 else // file in different directory
146 // absolute path; could also set relative path
147 pathInMenu
= m_fileHistory
[i
];
150 for ( wxList::compatibility_iterator node
= m_fileMenus
.GetFirst();
152 node
= node
->GetNext() )
154 wxMenu
* const menu
= (wxMenu
*)node
->GetData();
156 menu
->SetLabel(m_idBase
+ i
, GetMRUEntryLabel(i
, pathInMenu
));
161 void wxFileHistoryBase::RemoveFileFromHistory(size_t i
)
163 size_t numFiles
= m_fileHistory
.size();
164 wxCHECK_RET( i
< numFiles
,
165 wxT("invalid index in wxFileHistoryBase::RemoveFileFromHistory") );
167 // delete the element from the array
168 m_fileHistory
.RemoveAt(i
);
171 for ( wxList::compatibility_iterator node
= m_fileMenus
.GetFirst();
173 node
= node
->GetNext() )
175 wxMenu
* const menu
= (wxMenu
*) node
->GetData();
177 // shift filenames up
178 for ( size_t j
= i
; j
< numFiles
; j
++ )
180 menu
->SetLabel(m_idBase
+ j
, GetMRUEntryLabel(j
, m_fileHistory
[j
]));
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
);
188 // delete the last separator too if no more files are left
189 if ( m_fileHistory
.empty() )
191 const wxMenuItemList::compatibility_iterator
192 nodeLast
= menu
->GetMenuItems().GetLast();
195 wxMenuItem
* const lastMenuItem
= nodeLast
->GetData();
196 if ( lastMenuItem
->IsSeparator() )
197 menu
->Delete(lastMenuItem
);
199 //else: menu is empty somehow
204 void wxFileHistoryBase::UseMenu(wxMenu
*menu
)
206 if ( !m_fileMenus
.Member(menu
) )
207 m_fileMenus
.Append(menu
);
210 void wxFileHistoryBase::RemoveMenu(wxMenu
*menu
)
212 m_fileMenus
.DeleteObject(menu
);
216 void wxFileHistoryBase::Load(const wxConfigBase
& config
)
218 m_fileHistory
.Clear();
221 buf
.Printf(wxT("file%d"), 1);
223 wxString historyFile
;
224 while ((m_fileHistory
.GetCount() < m_fileMaxFiles
) &&
225 config
.Read(buf
, &historyFile
) && !historyFile
.empty())
227 m_fileHistory
.Add(historyFile
);
229 buf
.Printf(wxT("file%d"), (int)m_fileHistory
.GetCount()+1);
230 historyFile
= wxEmptyString
;
236 void wxFileHistoryBase::Save(wxConfigBase
& config
)
239 for (i
= 0; i
< m_fileMaxFiles
; i
++)
242 buf
.Printf(wxT("file%d"), (int)i
+1);
243 if (i
< m_fileHistory
.GetCount())
244 config
.Write(buf
, wxString(m_fileHistory
[i
]));
246 config
.Write(buf
, wxEmptyString
);
249 #endif // wxUSE_CONFIG
251 void wxFileHistoryBase::AddFilesToMenu()
253 if ( m_fileHistory
.empty() )
256 for ( wxList::compatibility_iterator node
= m_fileMenus
.GetFirst();
258 node
= node
->GetNext() )
260 AddFilesToMenu((wxMenu
*) node
->GetData());
264 void wxFileHistoryBase::AddFilesToMenu(wxMenu
* menu
)
266 if ( m_fileHistory
.empty() )
269 if ( menu
->GetMenuItemCount() )
270 menu
->AppendSeparator();
272 for ( size_t i
= 0; i
< m_fileHistory
.GetCount(); i
++ )
274 menu
->Append(m_idBase
+ i
, GetMRUEntryLabel(i
, m_fileHistory
[i
]));
278 #endif // wxUSE_FILE_HISTORY