]> git.pld-linux.org Git - packages/libreoffice.git/blame - openoffice-recent-files.patch
- up
[packages/libreoffice.git] / openoffice-recent-files.patch
CommitLineData
31860082 1--- svtools/util/makefile.mk.orig 2003-06-05 22:18:51.000000000 -0500
2+++ svtools/util/makefile.mk 2003-06-05 22:19:38.000000000 -0500
3@@ -233,6 +233,10 @@
4 $(VOSLIB) \
5 $(SALLIB)
6
7+.IF "$(GUI)"=="UNX"
8+SHL2STDLIBS+= $(EXPATASCII3RDLIB) `pkg-config --libs glib-2.0`
9+.ENDIF
10+
11 .IF "$(GUI)"=="WNT"
12 SHL2STDLIBS+= \
13 uwinapi.lib \
14--- svtools/source/config/makefile.mk.orig 2003-06-05 22:19:59.000000000 -0500
15+++ svtools/source/config/makefile.mk 2003-06-05 22:21:02.000000000 -0500
16@@ -107,7 +107,13 @@
17 $(SLO)$/accessibilityoptions.obj \
18 $(SLO)$/cmdoptions.obj \
19 $(SLO)$/extendedsecurityoptions.obj \
20- $(SLO)$/sourceviewconfig.obj
21+ $(SLO)$/sourceviewconfig.obj \
22+ $(SLO)$/recent-files.obj
23+
24+.IF "$(GUI)"=="UNX"
25+CFLAGS+=-Wall
26+CFLAGS+=`pkg-config --cflags glib-2.0`
27+.ENDIF
28
29 EXCEPTIONSFILES = \
30 $(SLO)$/accelcfg.obj \
31--- svtools/prj/build.lst.orig 2003-06-05 22:23:34.000000000 -0500
32+++ svtools/prj/build.lst 2003-06-05 22:23:42.000000000 -0500
33@@ -1,4 +1,4 @@
34-st svtools : offuh toolkit ucbhelper unotools jpeg NULL
35+st svtools : offuh toolkit ucbhelper unotools jpeg expat NULL
36 st svtools usr1 - all st_mkout NULL
37 st svtools\inc get - all st_inc NULL
38 st svtools\inc\sane get - all st_incsa NULL
39--- svtools/inc/historyoptions.hxx.orig 2003-06-05 22:25:11.000000000 -0500
40+++ svtools/inc/historyoptions.hxx 2003-06-05 22:26:05.000000000 -0500
41@@ -239,6 +239,7 @@
42 @param "eHistory" select right history.
43 @param "sURL" URL to save in history
44 @param "sFilter" filter name to save in history
45+ @param "sMimeType" MIME type of the URL; used only for ePICKLIST.
46 @param "sTitle" document title to save in history
47 @param "sPassword" password to save in history
48 @return -
49@@ -249,6 +250,7 @@
50 void AppendItem( EHistoryType eHistory ,
51 const ::rtl::OUString& sURL ,
52 const ::rtl::OUString& sFilter ,
53+ const ::rtl::OUString& sMimeType ,
54 const ::rtl::OUString& sTitle ,
55 const ::rtl::OUString& sPassword );
56
57--- svtools/source/config/historyoptions.cxx.orig 2003-06-05 22:26:20.000000000 -0500
58+++ svtools/source/config/historyoptions.cxx 2003-06-05 23:12:47.000000000 -0500
59@@ -95,6 +95,8 @@
60 #include <algorithm>
61 #endif
62
63+#include "recent-files.hxx"
64+
65 //_________________________________________________________________________________________________________________
66 // namespaces
67 //_________________________________________________________________________________________________________________
68@@ -403,8 +405,10 @@
69 sal_uInt32 nPosition = FIXPROPERTYCOUNT; // step over first three readed size values! but count begins at 0!
70 // Get names/values for picklist.
71 // 4 subkeys for every item!
72+ sal_uInt32 nItem;
73+#if 0
74 OUString sName;
75- for( sal_uInt32 nItem=0; nItem<nPicklistCount; ++nItem )
76+ for( nItem=0; nItem<nPicklistCount; ++nItem )
77 {
78 seqValues[nPosition] >>= aItem.sURL ;
79 ++nPosition;
80@@ -416,6 +420,26 @@
81 ++nPosition;
82 m_aPicklist.push_back( aItem );
83 }
84+#endif
85+ /* The user may already have a pick list from OOo, so we have to skip over it --- we now fetch the list from GNOME */
86+ nPosition += nPicklistCount * 4;
87+
88+ ::svt::RecentFileItem *items;
89+ int nItems;
90+ int i;
91+
92+ ::svt::recentFilesGetList (&items, &nItems);
93+
94+ aItem.sFilter = OUString ("", 0, RTL_TEXTENCODING_ASCII_US);
95+ aItem.sPassword = OUString ("", 0, RTL_TEXTENCODING_ASCII_US);
96+
97+ for (i = 0; i < nItems; i++) {
98+ aItem.sURL = OUString(items[i].uri);
99+ aItem.sTitle = OUString (items[i].uri); /* We don't have a title, so just use the filename */
100+ m_aPicklist.push_back (aItem);
101+ }
102+
103+ ::svt::recentFilesFree (items, nItems);
104
105 // Attention: Don't reset nPosition here!
106
107@@ -506,9 +530,11 @@
108 OUString sNode ;
109 Sequence< PropertyValue > seqPropertyValues( 4 ) ;
110
111+ sal_uInt32 nItem;
112+#if 0
113 // Copy picklist entries to save-list!
114 sal_uInt32 nPicklistCount = m_aPicklist.size();
115- for( sal_uInt32 nItem=0; nItem<nPicklistCount; ++nItem )
116+ for( nItem=0; nItem<nPicklistCount; ++nItem )
117 {
118 aItem = m_aPicklist[nItem];
119 sNode = PROPERTYNAME_PICKLIST + PATHDELIMITER + FIXP + OUString::valueOf( (sal_Int32)nItem ) + PATHDELIMITER;
120@@ -523,7 +549,7 @@
121
122 SetSetProperties( PROPERTYNAME_PICKLIST, seqPropertyValues );
123 }
124-
125+#endif
126 // Copy URL-list entries to save-list!
127 sal_uInt32 nHistoryCount = m_aHistory.size();
128 for( nItem=0; nItem<nHistoryCount; ++nItem )
129@@ -909,11 +935,16 @@
130 void SvtHistoryOptions::AppendItem( EHistoryType eHistory ,
131 const OUString& sURL ,
132 const OUString& sFilter ,
133+ const OUString& sMimeType ,
134 const OUString& sTitle ,
135 const OUString& sPassword )
136 {
137 MutexGuard aGuard( GetOwnStaticMutex() );
138- m_pDataContainer->AppendItem( eHistory, sURL, sFilter, sTitle, sPassword );
139+
140+ if (eHistory == ePICKLIST)
141+ ::svt::recentFilesAddItem (sURL.pData, sMimeType.pData);
142+ else
143+ m_pDataContainer->AppendItem( eHistory, sURL, sFilter, sTitle, sPassword );
144 }
145
146 //*****************************************************************************************************************
147--- sfx2/source/appl/newhelp.cxx.orig 2003-06-05 22:34:06.000000000 -0500
148+++ sfx2/source/appl/newhelp.cxx 2003-06-05 22:35:03.000000000 -0500
149@@ -1383,7 +1383,7 @@
150 {
151 String aTitle = GetEntry(i);
152 String* pURL = (String*)(ULONG)GetEntryData(i);
153- aHistOpt.AppendItem( eHELPBOOKMARKS, rtl::OUString( *pURL ), sEmpty, rtl::OUString( aTitle ), sEmpty );
154+ aHistOpt.AppendItem( eHELPBOOKMARKS, rtl::OUString( *pURL ), sEmpty, sEmpty, rtl::OUString( aTitle ), sEmpty );
155 delete pURL;
156 }
157 }
158--- sfx2/source/appl/sfxpicklist.cxx.orig 2003-04-11 10:54:18.000000000 -0500
159+++ sfx2/source/appl/sfxpicklist.cxx 2003-06-05 22:46:10.000000000 -0500
160@@ -461,6 +461,7 @@
161 SvtHistoryOptions().AppendItem( eHISTORY,
162 aURL.GetURLNoPass( INetURLObject::NO_DECODE ),
163 aFilter,
164+ ::rtl::OUString(),
165 aTitle,
166 SfxStringEncode( aURL.GetPass() ) );
167 }
168@@ -497,17 +498,21 @@
169
170 ::rtl::OUString aTitle = pDocSh->GetTitle(SFX_TITLE_PICKLIST);
171 ::rtl::OUString aFilter;
172+ ::rtl::OUString aMimeType;
173
174 INetURLObject aURL( pMed->GetOrigURL() );
175 const SfxFilter* pFilter = pMed->GetOrigFilter();
176
177- if ( pFilter )
178+ if ( pFilter ) {
179 aFilter = pFilter->GetFilterName();
180+ aMimeType = pFilter->GetMimeType();
181+ }
182
183 // add to svtool history options
184 SvtHistoryOptions().AppendItem( ePICKLIST,
185 aURL.GetURLNoPass( INetURLObject::NO_DECODE ),
186 aFilter,
187+ aMimeType,
188 aTitle,
189 SfxStringEncode( aURL.GetPass() ) );
190
191--- sd/source/ui/dlg/dlgass.cxx.orig 2003-06-05 23:00:04.000000000 -0500
192+++ sd/source/ui/dlg/dlgass.cxx 2003-06-05 23:05:19.000000000 -0500
193@@ -186,6 +186,12 @@
194 #include <sfx2/filedlghelper.hxx>
195 #endif
196
197+#include <com/sun/star/system/XSystemShellExecute.hpp>
198+#include <com/sun/star/document/XTypeDetection.hpp>
199+#include <com/sun/star/util/URL.hpp>
200+#include <com/sun/star/util/XURLTransformer.hpp>
201+#include <sfx2/fcontnr.hxx>
202+
203 #include "sdpage.hxx"
204 #include "helpids.h"
205 #include "assclass.hxx"
206@@ -198,6 +204,8 @@
207
208 using namespace ::com::sun::star;
209 using namespace ::sd;
210+using namespace ::com::sun::star::uno;
211+using namespace ::com::sun::star::system;
212
213
214 void InterpolateFixedBitmap( FixedBitmap * pBitmap )
215@@ -774,7 +782,38 @@
216 m_pWindow = NULL;
217 }
218
219+/* Queries the name of the filter that we can use to open a file */
220+static const String &
221+getFilterNameForFile (rtl_uString *uri)
222+{
223+ Reference< ::com::sun::star::document::XTypeDetection > type_detection (
224+ ::comphelper::getProcessServiceFactory ()->createInstance (
225+ ::rtl::OUString::createFromAscii ("com.sun.star.comp.framework.TypeDetection" )),
226+ UNO_QUERY );
227
228+ if (!type_detection.is())
229+ return;
230+
231+ ::com::sun::star::util::URL url;
232+ ::rtl::OUString type_name;
233+
234+ url.Complete = uri;
235+
236+ Reference < ::com::sun::star::util::XURLTransformer > trans (
237+ ::comphelper::getProcessServiceFactory ()->createInstance (
238+ ::rtl::OUString::createFromAscii ("com.sun.star.util.URLTransformer" )),
239+ UNO_QUERY);
240+
241+ trans->parseStrict (url);
242+
243+ type_name = type_detection->queryTypeByURL (url.Main);
244+ SfxFilterMatcher &filter_matcher = SFX_APP ()->GetFilterMatcher ();
245+ const SfxFilter *filter = filter_matcher.GetFilter4EA (type_name);
246+ if (!filter)
247+ return String ();
248+
249+ return filter->GetFilterName ();
250+}
251
252
253 void AssistentDlgImpl::ScanDocmenu (void)
254@@ -808,6 +847,10 @@
255 else if (aPropertySet[nProperty].Name == HISTORY_PROPERTYNAME_PASSWORD)
256 aPropertySet[nProperty].Value >>= sPassword;
257
258+ sFilter = rtl::OUString (getFilterNameForFile (sURL.pData).GetBuffer ());
259+ if (sFilter.getLength () == 0)
260+ continue;
261+
262 // If the entry is an impress file then insert it into the
263 // history list and the list box.
264 uno::Any aFilterPropSet = xFilterFactory->getByName( sFilter );
265--- svtools/source/config/recent-files.hxx 2003-01-30 04:24:37.000000000 -0600
266+++ svtools/source/config/recent-files.hxx 2003-06-05 23:10:17.000000000 -0500
267@@ -0,0 +1,30 @@
268+#ifndef RECENT_FILES_HXX
269+#define RECENT_FILES_HXX
270+
271+#ifndef _RTL_USTRING_
272+#include <rtl/ustring.h>
273+#endif
274+
275+#include <time.h>
276+
277+namespace svt {
278+
279+/* A recent file item */
280+struct RecentFileItem {
281+ rtl_uString *uri; /* URI of the file */
282+ rtl_uString *mimeType; /* MIME type */
283+ time_t timestamp; /* Timestamp for when the item was added */
284+};
285+
286+/* Queries the list of recent file items for OpenOffice.org */
287+void recentFilesGetList (RecentFileItem **items, int *nItems);
288+
289+/* Frees an array of RecentFileItem structures */
290+void recentFilesFree (RecentFileItem *items, int nItems);
291+
292+/* Adds an item to the list of recent file items */
293+void recentFilesAddItem (rtl_uString *uri, const rtl_uString *mimeType);
294+
295+};
296+
297+#endif
298--- svtools/source/config/recent-files.cxx 2003-01-30 04:24:37.000000000 -0600
299+++ svtools/source/config/recent-files.cxx 2003-06-05 23:28:52.000000000 -0500
300@@ -0,0 +1,802 @@
301+#include <stdio.h>
302+#include <string.h>
303+#include <time.h>
304+#include <unistd.h>
305+#include <sys/file.h>
306+#include <glib.h>
307+#include <tools/string.hxx>
308+#include "expat/xmlparse.h"
309+#include "recent-files.hxx"
310+
311+namespace svt {
312+
313+/*
314+ example:
315+<?xml version="1.0"?>
316+<RecentFiles>
317+ <RecentItem>
318+ <URI>file:///home/federico/gedit.txt</URI>
319+ <Mime-Type>text/plain</Mime-Type>
320+ <Timestamp>1046485966</Timestamp>
321+ <Groups>
322+ <Group>gedit</Group>
323+ </Groups>
324+ </RecentItem>
325+ <RecentItem>
326+ <URI>file:///home/federico/gedit-2.2.0.tar.bz2</URI>
327+ <Mime-Type>application/x-bzip</Mime-Type>
328+ <Timestamp>1046209851</Timestamp>
329+ <Private/>
330+ <Groups>
331+ </Groups>
332+ </RecentItem>
333+</RecentFiles>
334+*/
335+
336+/* Name of the standard ~/.recently-used file */
337+#define RECENT_FILE_NAME ".recently-used"
338+
339+/* Items without a MIME type get assigned this by default */
340+#define APPLICATION_OCTET_STREAM "application/octet-stream"
341+
342+/* Buffer size for reading ~/.recently-used */
343+#define BUFFER_SIZE 16384
344+
345+/* Maximum number of entries to keep before pruning */
346+#define MAX_ITEMS 20
347+
348+/* The group we use for OO.o files */
349+#define GROUP_OPENOFFICE_ORG "OpenOffice.org"
350+
351+/* Tags we understand in the ~/.recently-used XML */
352+#define TAG_RECENT_FILES "RecentFiles"
353+#define TAG_RECENT_ITEM "RecentItem"
354+#define TAG_URI "URI"
355+#define TAG_MIME_TYPE "Mime-Type"
356+#define TAG_TIMESTAMP "Timestamp"
357+#define TAG_PRIVATE "Private"
358+#define TAG_GROUPS "Groups"
359+#define TAG_GROUP "Group"
360+
361+/* Recent item as parsed from the file */
362+struct Item {
363+ char *uri;
364+ char *mimeType;
365+ time_t timestamp;
366+ gboolean isPrivate;
367+ GSList *groups;
368+};
369+
370+/* Parser state */
371+enum State {
372+ STATE_BEGIN, /* No elements read yet */
373+ STATE_RECENT_FILES, /* Inside the toplevel RecentFiles element */
374+ STATE_RECENT_ITEM, /* Inside RecentItem */
375+ STATE_URI, /* Inside URI */
376+ STATE_MIME_TYPE, /* Inside Mime-Type */
377+ STATE_TIMESTAMP, /* Inside Timestamp */
378+ STATE_PRIVATE, /* Inside Private */
379+ STATE_GROUPS, /* Inside Groups */
380+ STATE_GROUP, /* Inside Group */
381+ STATE_END, /* Finished parsing the toplevel element */
382+ STATE_ERROR /* Bad XML */
383+};
384+
385+/* XML parsing context */
386+struct ParseContext {
387+ GSList *items;
388+
389+ State state;
390+};
391+
392+/* Computes the name of the ~/.recent-files file */
393+static char *
394+getRecentFilename (void)
395+{
396+ return g_strdup_printf ("%s/" RECENT_FILE_NAME, g_get_home_dir ());
397+}
398+
399+/* Returns whether a string is null or empty */
400+static gboolean
401+stringIsEmpty (const char *s)
402+{
403+ return (s == NULL || strlen (s) == 0);
404+}
405+
406+/* Frees the contents of str, if any, and g_strdup()s the newStr into it */
407+static void
408+replaceString (char **str, const char *newStr, int newStrLen)
409+{
410+ g_free (*str);
411+ *str = g_strndup (newStr, newStrLen);
412+}
413+
414+/* Creates a new item in the context as a result of entering a RecentItem element */
415+static void
416+startRecentItem (ParseContext *context)
417+{
418+ Item *item;
419+
420+ item = g_new (Item, 1);
421+
422+ item->uri = NULL;
423+ item->mimeType = NULL;
424+ item->timestamp = -1;
425+ item->isPrivate = FALSE;
426+ item->groups = NULL;
427+
428+ context->items = g_slist_prepend (context->items, item);
429+}
430+
431+/* Frees an Item */
432+static void
433+freeItem (Item *item)
434+{
435+ GSList *l;
436+
437+ g_free (item->uri);
438+ g_free (item->mimeType);
439+
440+ for (l = item->groups; l; l = l->next) {
441+ char *group;
442+
443+ group = l->data;
444+ g_free (group);
445+ }
446+ g_slist_free (item->groups);
447+
448+ g_free (item);
449+}
450+
451+/* Terminates a RecentItem element by ensuring that the basic properties of the
452+ * current are fulfilled. If the URI is empty, removes the item from the
453+ * context.
454+ */
455+static void
456+endRecentItem (ParseContext *context)
457+{
458+ Item *item;
459+ GSList *listItem;
460+
461+ g_assert (context->items != NULL);
462+ item = context->items->data;
463+
464+ if (stringIsEmpty (item->uri)) {
465+ freeItem (item);
466+ context->items = g_slist_delete_link (context->items, context->items);
467+ return;
468+ }
469+
470+ if (stringIsEmpty (item->mimeType))
471+ replaceString (&item->mimeType, APPLICATION_OCTET_STREAM, strlen (APPLICATION_OCTET_STREAM));
472+
473+ if (item->timestamp == -1)
474+ item->timestamp = time (NULL);
475+}
476+
477+/* Handles entering a Private element */
478+static void
479+startPrivate (ParseContext *context)
480+{
481+ Item *item;
482+
483+ g_assert (context->items != NULL);
484+ item = context->items->data;
485+
486+ item->isPrivate = TRUE;
487+}
488+
489+/* Start element handler for the parser */
490+static void
491+startElementCb (void *data, const XML_Char *name, const XML_Char **attributes)
492+{
493+ ParseContext *context;
494+
495+ context = data;
496+
497+ if (context->state == STATE_ERROR || context->state == STATE_END)
498+ return;
499+
500+ switch (context->state) {
501+ case STATE_BEGIN:
502+ if (strcmp (name, TAG_RECENT_FILES) == 0)
503+ context->state = STATE_RECENT_FILES;
504+ else
505+ context->state = STATE_ERROR;
506+ break;
507+
508+ case STATE_RECENT_FILES:
509+ if (strcmp (name, TAG_RECENT_ITEM) == 0) {
510+ startRecentItem (context);
511+ context->state = STATE_RECENT_ITEM;
512+ } else
513+ context->state = STATE_ERROR;
514+ break;
515+
516+ case STATE_RECENT_ITEM:
517+ if (strcmp (name, TAG_URI) == 0)
518+ context->state = STATE_URI;
519+ else if (strcmp (name, TAG_MIME_TYPE) == 0)
520+ context->state = STATE_MIME_TYPE;
521+ else if (strcmp (name, TAG_TIMESTAMP) == 0)
522+ context->state = STATE_TIMESTAMP;
523+ else if (strcmp (name, TAG_PRIVATE) == 0) {
524+ startPrivate (context);
525+ context->state = STATE_PRIVATE;
526+ } else if (strcmp (name, TAG_GROUPS) == 0)
527+ context->state = STATE_GROUPS;
528+ else
529+ context->state = STATE_ERROR;
530+ break;
531+
532+ case STATE_URI:
533+ case STATE_MIME_TYPE:
534+ case STATE_TIMESTAMP:
535+ case STATE_PRIVATE:
536+ case STATE_GROUP:
537+ case STATE_END:
538+ case STATE_ERROR:
539+ context->state = STATE_ERROR;
540+ break;
541+
542+ case STATE_GROUPS:
543+ if (strcmp (name, TAG_GROUP) == 0)
544+ context->state = STATE_GROUP;
545+ else
546+ context->state = STATE_ERROR;
547+ break;
548+
549+ default:
550+ g_assert_not_reached ();
551+ }
552+}
553+
554+/* End element handler for the parser */
555+static void
556+endElementCb (void *data, const XML_Char *name)
557+{
558+ ParseContext *context;
559+
560+ context = data;
561+
562+ switch (context->state) {
563+ case STATE_RECENT_FILES:
564+ if (strcmp (name, TAG_RECENT_FILES) == 0)
565+ context->state = STATE_END;
566+ else
567+ context->state = STATE_ERROR;
568+ break;
569+
570+ case STATE_RECENT_ITEM:
571+ if (strcmp (name, TAG_RECENT_ITEM) == 0) {
572+ endRecentItem (context);
573+ context->state = STATE_RECENT_FILES;
574+ } else
575+ context->state = STATE_ERROR;
576+ break;
577+
578+ case STATE_URI:
579+ if (strcmp (name, TAG_URI) == 0)
580+ context->state = STATE_RECENT_ITEM;
581+ else
582+ context->state = STATE_ERROR;
583+ break;
584+
585+ case STATE_MIME_TYPE:
586+ if (strcmp (name, TAG_MIME_TYPE) == 0)
587+ context->state = STATE_RECENT_ITEM;
588+ else
589+ context->state = STATE_ERROR;
590+ break;
591+
592+ case STATE_TIMESTAMP:
593+ if (strcmp (name, TAG_TIMESTAMP) == 0)
594+ context->state = STATE_RECENT_ITEM;
595+ else
596+ context->state = STATE_ERROR;
597+ break;
598+
599+ case STATE_PRIVATE:
600+ if (strcmp (name, TAG_PRIVATE) == 0) {
601+ startPrivate (context); /* I don't know if expat calls the start or end element handler for <foo/> */
602+ context->state = STATE_RECENT_ITEM;
603+ } else
604+ context->state = STATE_ERROR;
605+ break;
606+
607+ case STATE_GROUPS:
608+ if (strcmp (name, TAG_GROUPS) == 0)
609+ context->state = STATE_RECENT_ITEM;
610+ else
611+ context->state = STATE_ERROR;
612+ break;
613+
614+ case STATE_GROUP:
615+ if (strcmp (name, TAG_GROUP) == 0)
616+ context->state = STATE_GROUPS;
617+ else
618+ context->state = STATE_ERROR;
619+ break;
620+
621+ case STATE_BEGIN:
622+ case STATE_END:
623+ case STATE_ERROR:
624+ context->state = STATE_ERROR;
625+ break;
626+
627+ default:
628+ g_assert_not_reached ();
629+ }
630+}
631+
632+/* Sets the timestamp field of an Item */
633+static void
634+setTimestamp (Item *item, const char *s, int len)
635+{
636+ char *dup;
637+ long t;
638+
639+ dup = g_strndup (s, len);
640+ if (sscanf (dup, "%ld", &t) != 1)
641+ item->timestamp = -1;
642+ else
643+ item->timestamp = t;
644+
645+ g_free (dup);
646+}
647+
648+/* Adds a group to an item */
649+static void
650+addGroup (Item *item, const char *s, int len)
651+{
652+ GSList *l;
653+
654+ for (l = item->groups; l; l = l->next) {
655+ const char *group;
656+
657+ group = l->data;
658+ if (strncmp (group, s, len) == 0)
659+ return;
660+ }
661+
662+ item->groups = g_slist_prepend (item->groups, g_strndup (s, len));
663+}
664+
665+/* Handler for character data between tags */
666+static void
667+characterDataCb (void *data, const XML_Char *s, int len)
668+{
669+ ParseContext *context;
670+ Item *item;
671+
672+ context = data;
673+ item = context->items ? context->items->data : NULL;
674+
675+ switch (context->state) {
676+ case STATE_URI:
677+ replaceString (&item->uri, s, len);
678+ break;
679+
680+ case STATE_MIME_TYPE:
681+ replaceString (&item->mimeType, s, len);
682+ break;
683+
684+ case STATE_TIMESTAMP:
685+ setTimestamp (item, s, len);
686+ break;
687+
688+ case STATE_GROUP:
689+ addGroup (item, s, len);
690+ break;
691+
692+ case STATE_BEGIN:
693+ case STATE_RECENT_FILES:
694+ case STATE_RECENT_ITEM:
695+ case STATE_PRIVATE:
696+ case STATE_GROUPS:
697+ case STATE_END:
698+ case STATE_ERROR:
699+ /* Ignore character data that may appear elsewhere */
700+ break;
701+
702+ default:
703+ g_assert_not_reached ();
704+ }
705+}
706+
707+/* Creates an XML parser for .recent-files */
708+static XML_Parser
709+createParser (ParseContext *context)
710+{
711+ XML_Parser parser;
712+
713+ parser = XML_ParserCreate (NULL);
714+ if (!parser)
715+ return NULL;
716+
717+ XML_SetUserData (parser, context);
718+ XML_SetElementHandler (parser, startElementCb, endElementCb);
719+ XML_SetCharacterDataHandler (parser, characterDataCb);
720+
721+ return parser;
722+}
723+
724+/* Compares items by timestamps so as to sort them in DECREASING order */
725+static gint
726+compareTimestampsCb (gconstpointer a, gconstpointer b, gpointer data)
727+{
728+ Item *ia, *ib;
729+ time_t ta, tb;
730+
731+ ia = a;
732+ ta = ia->timestamp;
733+
734+ ib = b;
735+ tb = ib->timestamp;
736+
737+ return (ta < tb) ? 1 : (ta > tb) ? -1 : 0;
738+}
739+
740+/* Returns whether an item has a certain group */
741+static gboolean
742+hasGroup (Item *item, const char *group)
743+{
744+ GSList *l;
745+
746+ for (l = item->groups; l; l = l->next) {
747+ const char *g;
748+
749+ g = l->data;
750+ if (strcmp (g, group) == 0)
751+ return TRUE;
752+ }
753+
754+ return FALSE;
755+}
756+
757+/* Removes the oldest items from the context */
758+static void
759+trimOldItems (ParseContext *context)
760+{
761+ GSList *l, *next;
762+ int i;
763+
764+ i = 0;
765+ l = context->items;
766+
767+ while (l) {
768+ Item *item;
769+
770+ item = l->data;
771+ next = l->next;
772+
773+ if (hasGroup (item, GROUP_OPENOFFICE_ORG)) {
774+ i++;
775+
776+ if (i > MAX_ITEMS) {
777+ freeItem (item);
778+ context->items = g_slist_remove_link (context->items, l);
779+ g_slist_free_1 (l);
780+ }
781+ }
782+
783+ l = next;
784+ }
785+}
786+
787+/* Parses the file and fills in the context */
788+static void
789+parse (FILE *file, ParseContext *context)
790+{
791+ XML_Parser parser;
792+ char buf[BUFFER_SIZE];
793+
794+ context->items = NULL;
795+ context->state = STATE_BEGIN;
796+
797+ parser = createParser (context);
798+
799+ if (!parser)
800+ return FALSE;
801+
802+ while (1) {
803+ int len;
804+ gboolean eof;
805+
806+ len = fread (buf, 1, sizeof (buf), file);
807+ if (ferror (file))
808+ break;
809+
810+ eof = feof (file);
811+
812+ if (!XML_Parse (parser, buf, len, eof)) {
813+ context->state = STATE_ERROR;
814+ break;
815+ }
816+
817+ if (eof)
818+ break;
819+ }
820+
821+ if (context->items)
822+ endRecentItem (context); /* Finish it off for if we terminated prematurely on error */
823+
824+ XML_ParserFree (parser);
825+
826+ context->items = g_slist_sort (context->items, compareTimestampsCb);
827+ trimOldItems (context);
828+}
829+
830+/* Frees the contents of a parse context */
831+static void
832+freeContext(ParseContext *context)
833+{
834+ GSList *l;
835+
836+ for (l = context->items; l; l = l->next) {
837+ Item *item;
838+
839+ item = l->data;
840+ freeItem (item);
841+ }
842+
843+ g_slist_free (context->items);
844+}
845+
846+/* Converts our items into exported structures */
847+static void
848+convertItems (ParseContext *context, RecentFileItem **items, int *nItems)
849+{
850+ GSList *l;
851+ int i;
852+
853+ /* Count items with the OpenOffice.org group */
854+
855+ *nItems = 0;
856+ for (l = context->items; l; l = l->next) {
857+ Item *item;
858+
859+ item = l->data;
860+ if (hasGroup (item, GROUP_OPENOFFICE_ORG))
861+ (*nItems)++;
862+ }
863+
864+ if (*nItems == 0)
865+ return;
866+
867+ /* Add only the items we want */
868+
869+ *items = g_new (RecentFileItem, *nItems);
870+
871+ i = 0;
872+
873+ for (l = context->items; l; l = l->next) {
874+ Item *item;
875+
876+ item = l->data;
877+
878+ if (hasGroup (item, GROUP_OPENOFFICE_ORG)) {
879+ RecentFileItem *rfi;
880+
881+ rfi = (*items) + i;
882+
883+ rfi->uri = NULL;
884+ rtl_string2UString (&rfi->uri, item->uri, strlen (item->uri), RTL_TEXTENCODING_UTF8, 0);
885+ rfi->mimeType = NULL;
886+ rtl_string2UString (&rfi->mimeType, item->mimeType, strlen (item->mimeType), RTL_TEXTENCODING_UTF8, 0);
887+
888+ rfi->timestamp = item->timestamp;
889+
890+ i++;
891+ }
892+ }
893+}
894+
895+/* Opens ~/.recent-files and locks it. If something fails, returns NULL. */
896+static FILE *
897+openAndLock (void)
898+{
899+ char *filename;
900+ FILE *file;
901+ int fd;
902+ ParseContext context;
903+
904+ filename = getRecentFilename ();
905+
906+ file = fopen (filename, "r+");
907+ g_free (filename);
908+
909+ if (!file)
910+ return NULL;
911+
912+ fd = fileno (file);
913+ if (lockf (fd, F_LOCK, 0) != 0) {
914+ fclose (file);
915+ return NULL;
916+ }
917+
918+ return file;
919+}
920+
921+/* Unlocks ~/.recent-files and closes it. */
922+static void
923+unlockAndClose (FILE *file)
924+{
925+ int fd;
926+
927+ fd = fileno (file);
928+ lockf (fd, F_ULOCK, 0);
929+ fclose (file);
930+}
931+
932+
933+/**
934+ * recentFilesGetList:
935+ * @items: Return value; pointer to an array of items. Should be freed with recentFilesFree().
936+ * @nItems: Return value; number of items returned.
937+ *
938+ * Queries the list of GNOME recent file items.
939+ **/
940+void
941+recentFilesGetList (RecentFileItem **items, int *nItems)
942+{
943+ FILE *file;
944+ ParseContext context;
945+
946+ *items = NULL;
947+ *nItems = 0;
948+
949+ file = openAndLock ();
950+ if (!file)
951+ return;
952+
953+ parse (file, &context);
954+
955+ unlockAndClose (file);
956+
957+ convertItems (&context, items, nItems);
958+ freeContext (&context);
959+}
960+
961+/* Writes the ~/.recent-files file */
962+static void
963+writeXml (FILE *file, ParseContext *context)
964+{
965+ GSList *l;
966+
967+ fputs ("<?xml version=\"1.0\"?>\n"
968+ "<" TAG_RECENT_FILES ">\n",
969+ file);
970+
971+ for (l = context->items; l; l = l->next) {
972+ Item *item;
973+ GSList *gl;
974+
975+ item = l->data;
976+
977+ fputs (" <" TAG_RECENT_ITEM ">\n", file);
978+ fprintf (file,
979+ " <" TAG_URI ">%s</" TAG_URI ">\n"
980+ " <" TAG_MIME_TYPE ">%s</" TAG_MIME_TYPE ">\n"
981+ " <" TAG_TIMESTAMP ">%ld</" TAG_TIMESTAMP ">\n"
982+ "%s"
983+ " <" TAG_GROUPS ">\n",
984+ item->uri,
985+ item->mimeType,
986+ (long) item->timestamp,
987+ item->isPrivate ? " <" TAG_PRIVATE "/>\n" : "");
988+
989+ for (gl = item->groups; gl; gl = gl->next) {
990+ const char *group;
991+
992+ group = gl->data;
993+ fprintf (file,
994+ " <" TAG_GROUP ">%s</" TAG_GROUP ">\n",
995+ group);
996+ }
997+
998+ fputs (" </" TAG_GROUPS ">\n"
999+ " </" TAG_RECENT_ITEM ">\n",
1000+ file);
1001+ }
1002+
1003+ fputs ("</" TAG_RECENT_FILES ">\n", file);
1004+}
1005+
1006+/**
1007+ * recentFilesFree:
1008+ * @items: Array of items.
1009+ * @nItems: Number of items in the array.
1010+ *
1011+ * Frees an array of #RecentFileItem structures as returned by recentFilesGetList().
1012+ **/
1013+void
1014+recentFilesFree (RecentFileItem *items, int nItems)
1015+{
1016+ int i;
1017+
1018+ for (i = 0; i < nItems; i++) {
1019+ rtl_uString_release (items[i].uri);
1020+ rtl_uString_release (items[i].mimeType);
1021+ }
1022+
1023+ g_free (items);
1024+}
1025+
1026+/**
1027+ * recentFilesAddItem:
1028+ * @uri: URI to add.
1029+ * @mimeType: MIME type of the item.
1030+ *
1031+ * Adds an item to GNOME's recent files list. Automatically adds the "OpenOffice.org" group.
1032+ **/
1033+void
1034+recentFilesAddItem (rtl_uString *uri, const rtl_uString *mimeType)
1035+{
1036+ FILE *file;
1037+ ParseContext context;
1038+ GSList *l;
1039+ gboolean alreadyExists;
1040+ int fd;
1041+ rtl_String *uriString;
1042+ rtl_String *mimeTypeString;
1043+
1044+ file = openAndLock ();
1045+ if (!file)
1046+ return;
1047+
1048+ parse (file, &context);
1049+
1050+ alreadyExists = FALSE;
1051+
1052+ uriString = NULL;
1053+ mimeTypeString = NULL;
1054+ rtl_uString2String (&uriString, uri->buffer, uri->length, RTL_TEXTENCODING_UTF8, 0);
1055+ rtl_uString2String (&mimeTypeString, mimeType->buffer, mimeType->length, RTL_TEXTENCODING_UTF8, 0);
1056+
1057+ for (l = context.items; l; l = l->next) {
1058+ Item *item;
1059+
1060+ item = l->data;
1061+ if (strcmp (item->uri, uriString->buffer) == 0) {
1062+ g_free (item->mimeType);
1063+ item->mimeType = g_strdup (mimeTypeString->buffer);
1064+ item->timestamp = time (NULL);
1065+ alreadyExists = TRUE;
1066+ break;
1067+ }
1068+ }
1069+
1070+ if (!alreadyExists) {
1071+ Item *item;
1072+
1073+ item = g_new (Item, 1);
1074+ item->uri = g_strdup (uriString->buffer);
1075+ item->mimeType = g_strdup (mimeTypeString->buffer);
1076+ item->timestamp = time (NULL);
1077+ item->isPrivate = FALSE;
1078+ item->groups = g_slist_prepend (NULL, g_strdup (GROUP_OPENOFFICE_ORG));
1079+
1080+ context.items = g_slist_prepend (context.items, item);
1081+ }
1082+
1083+ rtl_string_release (uriString);
1084+ rtl_string_release (mimeTypeString);
1085+
1086+ fd = fileno (file);
1087+
1088+ /* FIXME: perhaps we should do a write to a temporary file, then do an atomic rename() */
1089+
1090+ rewind (file);
1091+ if (ftruncate (fd, 0) != 0)
1092+ goto out;
1093+
1094+ writeXml (file, &context);
1095+
1096+out:
1097+
1098+ unlockAndClose (file);
1099+ freeContext (&context);
1100+}
1101+
1102+};
This page took 0.158732 seconds and 4 git commands to generate.