]> git.pld-linux.org Git - packages/libreoffice.git/blob - openoffice-recent-files.patch
- up
[packages/libreoffice.git] / openoffice-recent-files.patch
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.152485 seconds and 3 git commands to generate.