merge the formfield patch from ooo-build
[ooovba.git] / shell / source / unix / sysshell / recently_used_file_handler.cxx
blobd468a507a3d919f0a2d60105ef9662aaa82d4eac
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: recently_used_file_handler.cxx,v $
10 * $Revision: 1.12 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_shell.hxx"
34 #include "osl/process.h"
35 #include "rtl/ustring.hxx"
36 #include "rtl/string.hxx"
37 #include "rtl/strbuf.hxx"
39 #include "osl/thread.h"
40 #include "recently_used_file.hxx"
42 #include "internal/xml_parser.hxx"
43 #include "internal/i_xml_parser_event_handler.hxx"
45 #include <map>
46 #include <vector>
47 #include <algorithm>
48 #include <functional>
49 #include <string.h>
51 namespace /* private */ {
53 const rtl::OUString ENVV_UPDATE_RECENTLY_USED =
54 rtl::OUString::createFromAscii("ENABLE_UPDATE_RECENTLY_USED");
56 //########################################
57 inline rtl::OString get_file_extension(const rtl::OString& file_url)
59 sal_Int32 index = file_url.lastIndexOf('.');
60 OSL_ENSURE((index != -1) && ((index + 1) < file_url.getLength()), "Invalid file url");
61 return file_url.copy(index + 1);
64 //########################################
65 typedef std::vector<string_t> string_container_t;
67 #define TAG_RECENT_FILES "RecentFiles"
68 #define TAG_RECENT_ITEM "RecentItem"
69 #define TAG_URI "URI"
70 #define TAG_MIME_TYPE "Mime-Type"
71 #define TAG_TIMESTAMP "Timestamp"
72 #define TAG_PRIVATE "Private"
73 #define TAG_GROUPS "Groups"
74 #define TAG_GROUP "Group"
76 //------------------------------------------------
77 // compare two string_t's case insensitive, may also be done
78 // by specifying special traits for the string type but in this
79 // case it's easier to do it this way
80 struct str_icase_cmp :
81 public std::binary_function<string_t, string_t, bool>
83 bool operator() (const string_t& s1, const string_t& s2) const
84 { return (0 == strcasecmp(s1.c_str(), s2.c_str())); }
87 //------------------------------------------------
88 struct recently_used_item
90 recently_used_item() :
91 is_private_(false)
94 recently_used_item(
95 const string_t& uri,
96 const string_t& mime_type,
97 const string_container_t& groups,
98 bool is_private = false) :
99 uri_(uri),
100 mime_type_(mime_type),
101 is_private_(is_private),
102 groups_(groups)
104 timestamp_ = time(NULL);
107 void set_uri(const string_t& character)
108 { uri_ = character; }
110 void set_mime_type(const string_t& character)
111 { mime_type_ = character; }
113 void set_timestamp(const string_t& character)
115 time_t t;
116 if (sscanf(character.c_str(), "%ld", &t) != 1)
117 timestamp_ = -1;
118 else
119 timestamp_ = t;
122 void set_is_private(const string_t& /*character*/)
123 { is_private_ = true; }
125 void set_groups(const string_t& character)
126 { groups_.push_back(character); }
128 void set_nothing(const string_t& /*character*/)
131 bool has_groups() const
133 return !groups_.empty();
136 bool has_group(const string_t& name) const
138 string_container_t::const_iterator iter_end = groups_.end();
139 return (has_groups() &&
140 iter_end != std::find_if(
141 groups_.begin(), iter_end,
142 std::bind2nd(str_icase_cmp(), name)));
145 void write_xml(const recently_used_file& file) const
147 write_xml_start_tag(TAG_RECENT_ITEM, file, true);
148 write_xml_tag(TAG_URI, uri_, file);
149 write_xml_tag(TAG_MIME_TYPE, mime_type_, file);
151 rtl::OString ts = rtl::OString::valueOf((sal_sSize)timestamp_);
152 write_xml_tag(TAG_TIMESTAMP, ts.getStr(), file);
154 if (is_private_)
155 write_xml_tag(TAG_PRIVATE, file);
157 if (has_groups())
159 write_xml_start_tag(TAG_GROUPS, file, true);
161 string_container_t::const_iterator iter = groups_.begin();
162 string_container_t::const_iterator iter_end = groups_.end();
164 for ( ; iter != iter_end; ++iter)
165 write_xml_tag(TAG_GROUP, (*iter), file);
167 write_xml_end_tag(TAG_GROUPS, file);
169 write_xml_end_tag(TAG_RECENT_ITEM, file);
172 static rtl::OString escape_content(const string_t &text)
174 rtl::OStringBuffer aBuf;
175 for (sal_uInt32 i = 0; i < text.length(); i++)
177 # define MAP(a,b) case a: aBuf.append(b); break
178 switch (text[i])
180 MAP ('&', "&amp;");
181 MAP ('<', "&lt;");
182 MAP ('>', "&gt;");
183 MAP ('\'', "&apos;");
184 MAP ('"', "&quot;");
185 default:
186 aBuf.append(text[i]);
187 break;
189 # undef MAP
191 return aBuf.makeStringAndClear();
194 void write_xml_tag(const string_t& name, const string_t& value, const recently_used_file& file) const
196 write_xml_start_tag(name, file);
197 rtl::OString escaped = escape_content (value);
198 file.write(escaped.getStr(), escaped.getLength());
199 write_xml_end_tag(name, file);
202 void write_xml_tag(const string_t& name, const recently_used_file& file) const
204 file.write("<", 1);
205 file.write(name.c_str(), name.length());
206 file.write("/>\n", 3);
209 void write_xml_start_tag(const string_t& name, const recently_used_file& file, bool linefeed = false) const
211 file.write("<", 1);
212 file.write(name.c_str(), name.length());
213 if (linefeed)
214 file.write(">\n", 2);
215 else
216 file.write(">", 1);
219 void write_xml_end_tag(const string_t& name, const recently_used_file& file) const
221 file.write("</", 2);
222 file.write(name.c_str(), name.length());
223 file.write(">\n", 2);
226 string_t uri_;
227 string_t mime_type_;
228 time_t timestamp_;
229 bool is_private_;
230 string_container_t groups_;
233 typedef std::vector<recently_used_item*> recently_used_item_list_t;
234 typedef void (recently_used_item::* SET_COMMAND)(const string_t&);
236 //########################################
237 // thrown if we encounter xml tags that we do not know
238 class unknown_xml_format_exception {};
240 //########################################
241 class recently_used_file_filter : public i_xml_parser_event_handler
243 public:
244 recently_used_file_filter(recently_used_item_list_t& item_list) :
245 item_(NULL),
246 item_list_(item_list)
248 named_command_map_[TAG_RECENT_FILES] = &recently_used_item::set_nothing;
249 named_command_map_[TAG_RECENT_ITEM] = &recently_used_item::set_nothing;
250 named_command_map_[TAG_URI] = &recently_used_item::set_uri;
251 named_command_map_[TAG_MIME_TYPE] = &recently_used_item::set_mime_type;
252 named_command_map_[TAG_TIMESTAMP] = &recently_used_item::set_timestamp;
253 named_command_map_[TAG_PRIVATE] = &recently_used_item::set_is_private;
254 named_command_map_[TAG_GROUPS] = &recently_used_item::set_nothing;
255 named_command_map_[TAG_GROUP] = &recently_used_item::set_groups;
258 virtual void start_element(
259 const string_t& /*raw_name*/,
260 const string_t& local_name,
261 const xml_tag_attribute_container_t& /*attributes*/)
263 if ((local_name == TAG_RECENT_ITEM) && (NULL == item_))
264 item_ = new recently_used_item;
267 virtual void end_element(const string_t& /*raw_name*/, const string_t& local_name)
269 // check for end tags w/o start tag
270 if( local_name != TAG_RECENT_FILES && NULL == item_ )
271 return; // will result in an XML parser error anyway
273 if (named_command_map_.find(local_name) != named_command_map_.end())
274 (item_->*named_command_map_[local_name])(current_element_);
275 else
277 delete item_;
278 throw unknown_xml_format_exception();
281 if (local_name == TAG_RECENT_ITEM)
283 item_list_.push_back(item_);
284 item_ = NULL;
286 current_element_.clear();
289 virtual void characters(const string_t& character)
291 if (character != "\n")
292 current_element_ += character;
295 virtual void start_document() {}
296 virtual void end_document() {}
298 virtual void ignore_whitespace(const string_t& /*whitespaces*/)
301 virtual void processing_instruction(
302 const string_t& /*target*/, const string_t& /*data*/)
305 virtual void comment(const string_t& /*comment*/)
307 private:
308 recently_used_item* item_;
309 std::map<string_t, SET_COMMAND> named_command_map_;
310 string_t current_element_;
311 recently_used_item_list_t& item_list_;
312 private:
313 recently_used_file_filter(const recently_used_file_filter&);
314 recently_used_file_filter& operator=(const recently_used_file_filter&);
317 //------------------------------------------------
318 void read_recently_used_items(
319 recently_used_file& file,
320 recently_used_item_list_t& item_list)
322 xml_parser xparser;
323 recently_used_file_filter ruff(item_list);
325 xparser.set_document_handler(&ruff);
327 char buff[16384];
328 while (!file.eof())
330 if (size_t length = file.read(buff, sizeof(buff)))
331 xparser.parse(buff, length, file.eof());
335 //------------------------------------------------
336 // The file ~/.recently_used shall not contain more than 500
337 // entries (see www.freedesktop.org)
338 const int MAX_RECENTLY_USED_ITEMS = 500;
340 class recent_item_writer
342 public:
343 recent_item_writer(
344 recently_used_file& file,
345 int max_items_to_write = MAX_RECENTLY_USED_ITEMS) :
346 file_(file),
347 max_items_to_write_(max_items_to_write),
348 items_written_(0)
351 void operator() (const recently_used_item* item)
353 if (items_written_++ < max_items_to_write_)
354 item->write_xml(file_);
356 private:
357 recently_used_file& file_;
358 int max_items_to_write_;
359 int items_written_;
362 //------------------------------------------------
363 const char* XML_HEADER = "<?xml version=\"1.0\"?>\n<RecentFiles>\n";
364 const char* XML_FOOTER = "</RecentFiles>";
366 //------------------------------------------------
367 // assumes that the list is ordered decreasing
368 void write_recently_used_items(
369 recently_used_file& file,
370 recently_used_item_list_t& item_list)
372 if (!item_list.empty())
374 file.truncate();
375 file.reset();
377 file.write(XML_HEADER, strlen(XML_HEADER));
379 std::for_each(
380 item_list.begin(),
381 item_list.end(),
382 recent_item_writer(file));
384 file.write(XML_FOOTER, strlen(XML_FOOTER));
388 //------------------------------------------------
389 struct delete_recently_used_item
391 void operator() (const recently_used_item* item) const
392 { delete item; }
395 //------------------------------------------------
396 void recently_used_item_list_clear(recently_used_item_list_t& item_list)
398 std::for_each(
399 item_list.begin(),
400 item_list.end(),
401 delete_recently_used_item());
402 item_list.clear();
405 //------------------------------------------------
406 class find_item_predicate
408 public:
409 find_item_predicate(const string_t& uri) :
410 uri_(uri)
413 bool operator() (const recently_used_item* item)
414 { return (item->uri_ == uri_); }
415 private:
416 string_t uri_;
419 //------------------------------------------------
420 struct greater_recently_used_item
422 bool operator ()(const recently_used_item* lhs, const recently_used_item* rhs) const
423 { return (lhs->timestamp_ > rhs->timestamp_); }
426 //------------------------------------------------
427 const char* GROUP_OOO = "openoffice.org";
428 const char* GROUP_STAR_OFFICE = "staroffice";
429 const char* GROUP_STAR_SUITE = "starsuite";
431 //------------------------------------------------
432 void recently_used_item_list_add(
433 recently_used_item_list_t& item_list, const rtl::OUString& file_url, const rtl::OUString& mime_type)
435 rtl::OString f = rtl::OUStringToOString(file_url, RTL_TEXTENCODING_UTF8);
437 recently_used_item_list_t::iterator iter =
438 std::find_if(
439 item_list.begin(),
440 item_list.end(),
441 find_item_predicate(f.getStr()));
443 if (iter != item_list.end())
445 (*iter)->timestamp_ = time(NULL);
447 if (!(*iter)->has_group(GROUP_OOO))
448 (*iter)->groups_.push_back(GROUP_OOO);
449 if (!(*iter)->has_group(GROUP_STAR_OFFICE))
450 (*iter)->groups_.push_back(GROUP_STAR_OFFICE);
451 if (!(*iter)->has_group(GROUP_STAR_SUITE))
452 (*iter)->groups_.push_back(GROUP_STAR_SUITE);
454 else
456 string_container_t groups;
457 groups.push_back(GROUP_OOO);
458 groups.push_back(GROUP_STAR_OFFICE);
459 groups.push_back(GROUP_STAR_SUITE);
461 string_t uri(f.getStr());
462 string_t mimetype(rtl::OUStringToOString(mime_type, osl_getThreadTextEncoding()).getStr());
464 if (mimetype.length() == 0)
465 mimetype = "application/octet-stream";
467 item_list.push_back(new recently_used_item(uri, mimetype, groups));
470 // sort decreasing after the timestamp
471 // so that the newest items appear first
472 std::sort(
473 item_list.begin(),
474 item_list.end(),
475 greater_recently_used_item());
478 //##############################
479 bool update_recently_used_enabled()
481 rtl::OUString tmp;
482 osl_getEnvironment(ENVV_UPDATE_RECENTLY_USED.pData, &tmp.pData);
483 return (tmp.getLength() > 0);
486 //------------------------------------------------
487 struct cleanup_guard
489 cleanup_guard(recently_used_item_list_t& item_list) :
490 item_list_(item_list)
492 ~cleanup_guard()
493 { recently_used_item_list_clear(item_list_); }
495 recently_used_item_list_t& item_list_;
498 } // namespace private
500 //###########################################
502 example (see http::www.freedesktop.org):
503 <?xml version="1.0"?>
504 <RecentFiles>
505 <RecentItem>
506 <URI>file:///home/federico/gedit.txt</URI>
507 <Mime-Type>text/plain</Mime-Type>
508 <Timestamp>1046485966</Timestamp>
509 <Groups>
510 <Group>gedit</Group>
511 </Groups>
512 </RecentItem>
513 <RecentItem>
514 <URI>file:///home/federico/gedit-2.2.0.tar.bz2</URI>
515 <Mime-Type>application/x-bzip</Mime-Type>
516 <Timestamp>1046209851</Timestamp>
517 <Private/>
518 <Groups>
519 </Groups>
520 </RecentItem>
521 </RecentFiles>
524 extern "C" void add_to_recently_used_file_list(const rtl::OUString& file_url, const rtl::OUString& mime_type)
528 recently_used_file ruf;
529 recently_used_item_list_t item_list;
530 cleanup_guard guard(item_list);
532 read_recently_used_items(ruf, item_list);
533 recently_used_item_list_add(item_list, file_url, mime_type);
534 write_recently_used_items(ruf, item_list);
536 catch(const char* ex)
538 OSL_ENSURE(false, ex);
540 catch(const xml_parser_exception&)
542 OSL_ENSURE(false, "XML parser error");
544 catch(const unknown_xml_format_exception&)
546 OSL_ENSURE(false, "XML format unknown");