shell32: update freedesktop's recently used document list
Damjan Jovanovic
damjan.jov at gmail.com
Sun Apr 25 05:11:40 CDT 2010
Changelog:
* shell32: update freedesktop's recently used document list
Damjan Jovanovic
-------------- next part --------------
diff --git a/dlls/shell32/Makefile.in b/dlls/shell32/Makefile.in
index 5e4a0c8..5b94116 100644
--- a/dlls/shell32/Makefile.in
+++ b/dlls/shell32/Makefile.in
@@ -5,7 +5,7 @@ SRCDIR = @srcdir@
VPATH = @srcdir@
MODULE = shell32.dll
IMPORTLIB = shell32
-IMPORTS = uuid shlwapi comctl32 user32 gdi32 advapi32 kernel32 ntdll
+IMPORTS = uuid shlwapi comctl32 user32 gdi32 advapi32 kernel32 ntdll wininet
DELAYIMPORTS = ole32 oleaut32 shdocvw version
C_SRCS = \
diff --git a/dlls/shell32/shellord.c b/dlls/shell32/shellord.c
index fac7a7a..1deb096 100644
--- a/dlls/shell32/shellord.c
+++ b/dlls/shell32/shellord.c
@@ -21,9 +21,15 @@
*/
#include "config.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
+#include <time.h>
#define COBJMACROS
@@ -47,6 +53,8 @@
#include "shlwapi.h"
#include "commdlg.h"
#include "commoncontrols.h"
+#include "wininet.h"
+#include "xmldom.h"
WINE_DEFAULT_DEBUG_CHANNEL(shell);
WINE_DECLARE_DEBUG_CHANNEL(pidl);
@@ -732,6 +740,483 @@ static INT SHADD_create_add_mru_data(HANDLE mruhandle, LPCSTR doc_name, LPCSTR n
return AddMRUData(mruhandle, buffer, *len);
}
+static WCHAR* get_file_contents(int fd)
+{
+ WCHAR *bufferW = NULL;
+ struct stat fileinfo;
+ if (fstat(fd, &fileinfo) == 0)
+ {
+ char *buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, fileinfo.st_size + 1);
+ if (buffer)
+ {
+ size_t pos = 0;
+ ssize_t ret;
+ do
+ {
+ ret = read(fd, &buffer[pos], fileinfo.st_size - pos);
+ if (ret > 0)
+ pos += ret;
+ } while ((ret > 0 || errno == EINTR) && pos < fileinfo.st_size);
+
+ if (ret >= 0)
+ {
+ INT wsize = MultiByteToWideChar(CP_ACP, 0, buffer, -1, NULL, 0);
+ bufferW = HeapAlloc(GetProcessHeap(), 0, wsize * sizeof(WCHAR));
+ if (bufferW)
+ MultiByteToWideChar(CP_ACP, 0, buffer, -1, bufferW, -1);
+ }
+ HeapFree(GetProcessHeap(), 0, buffer);
+ }
+ }
+ return bufferW;
+}
+
+static BOOL populate_recently_used(IXMLDOMDocument *document)
+{
+ static const WCHAR RecentFilesW[] = {'R','e','c','e','n','t','F','i','l','e','s',0};
+ HRESULT hr;
+ BSTR bstr = NULL;
+ IXMLDOMElement *element = NULL;
+ BOOLEAN succeeded = FALSE;
+
+ bstr = SysAllocString(RecentFilesW);
+ if (bstr == NULL)
+ goto done;
+ hr = IXMLDOMDocument_createElement(document, bstr, &element);
+ if (FAILED(hr))
+ goto done;
+ hr = IXMLDOMDocument_putref_documentElement(document, element);
+ if (FAILED(hr))
+ goto done;
+ succeeded = TRUE;
+
+done:
+ if (bstr)
+ SysFreeString(bstr);
+ IXMLDOMElement_Release(element);
+ return succeeded;
+}
+
+static BOOL match_recent_item(BSTR uri, WCHAR *filename)
+{
+ WCHAR *path;
+ URL_COMPONENTSW components;
+ BOOL matched = FALSE;
+
+ path = HeapAlloc(GetProcessHeap(), 0, (strlenW(uri) + 1) * sizeof(WCHAR));
+ if (path)
+ {
+ memset(&components, 0, sizeof(components));
+ components.dwStructSize = sizeof(components);
+ components.lpszUrlPath = path;
+ components.dwUrlPathLength = strlenW(uri);
+ if (InternetCrackUrlW(uri, 0, ICU_DECODE|ICU_ESCAPE, &components))
+ {
+ char *asciiPath;
+ INT asciiPathLength;
+ TRACE("decoded path is %s\n", debugstr_w(path));
+ asciiPathLength = WideCharToMultiByte(CP_UNIXCP, 0, path, -1, NULL, 0, 0, 0);
+ asciiPath = HeapAlloc(GetProcessHeap(), 0, asciiPathLength);
+ if (asciiPath)
+ {
+ char *unix_filename = wine_get_unix_file_name(filename);
+ if (unix_filename)
+ {
+ matched = (strcmp(asciiPath, unix_filename) == 0);
+ HeapFree(GetProcessHeap(), 0, unix_filename);
+ }
+ HeapFree(GetProcessHeap(), 0, asciiPath);
+ }
+ }
+ HeapFree(GetProcessHeap(), 0, path);
+ }
+ return matched;
+}
+
+static void update_recent_item(IXMLDOMNode *recentItem)
+{
+ IXMLDOMNodeList *subnodes = NULL;
+ HRESULT hr;
+
+ hr = IXMLDOMNode_get_childNodes(recentItem, &subnodes);
+ if (SUCCEEDED(hr))
+ {
+ IXMLDOMNode *node = NULL;
+ for (hr = IXMLDOMNodeList_nextNode(subnodes, &node);
+ SUCCEEDED(hr) && node != NULL;
+ hr = IXMLDOMNodeList_nextNode(subnodes, &node))
+ {
+ static const WCHAR timestampW[] = {'T','i','m','e','s','t','a','m','p',0};
+ BSTR name = NULL;
+ hr = IXMLDOMNode_get_nodeName(node, &name);
+ if (SUCCEEDED(hr))
+ {
+ if (strcmpiW(name, timestampW) == 0)
+ {
+ static const WCHAR fmtW[] = {'%','d',0};
+ WCHAR buffer[50];
+ BSTR text;
+ snprintfW(buffer, sizeof(buffer)/sizeof(WCHAR), fmtW, time(NULL));
+ text = SysAllocString(buffer);
+ if (text)
+ {
+ IXMLDOMNode_put_text(node, text);
+ SysFreeString(text);
+ }
+ }
+ SysFreeString(name);
+ }
+ IXMLDOMNode_Release(node);
+ }
+ IXMLDOMNodeList_Release(subnodes);
+ }
+}
+
+static BOOL check_recent_item(IXMLDOMNode *recentItem, WCHAR *filename)
+{
+ IXMLDOMNodeList *subnodes = NULL;
+ HRESULT hr;
+ BOOL found = FALSE;
+
+ hr = IXMLDOMNode_get_childNodes(recentItem, &subnodes);
+ if (SUCCEEDED(hr))
+ {
+ IXMLDOMNode *node = NULL;
+ for (hr = IXMLDOMNodeList_nextNode(subnodes, &node);
+ SUCCEEDED(hr) && node != NULL && !found;
+ hr = IXMLDOMNodeList_nextNode(subnodes, &node))
+ {
+ static const WCHAR uriW[] = {'U','R','I',0};
+ BSTR name = NULL;
+ hr = IXMLDOMNode_get_nodeName(node, &name);
+ if (SUCCEEDED(hr))
+ {
+ if (strcmpiW(name, uriW) == 0)
+ {
+ BSTR text = NULL;
+ hr = IXMLDOMNode_get_text(node, &text);
+ if (SUCCEEDED(hr))
+ {
+ found = match_recent_item(text, filename);
+ if (found)
+ update_recent_item(recentItem);
+ SysFreeString(text);
+ }
+ }
+ SysFreeString(name);
+ }
+ IXMLDOMNode_Release(node);
+ }
+ IXMLDOMNodeList_Release(subnodes);
+ }
+ return found;
+}
+
+static BOOL add_recent_item_properties(IXMLDOMDocument *document, WCHAR *filename, IXMLDOMElement *recentItem)
+{
+ static const WCHAR UriW[] = {'U','R','I',0};
+ static const WCHAR MimeTypeW[] = {'M','i','m','e','-','T','y','p','e',0};
+ static const WCHAR TimestampW[] = {'T','i','m','e','s','t','a','m','p',0};
+ static const WCHAR ApplicationOctetStreamW[] =
+ {'a','p','p','l','i','c','a','t','i','o','n','/','o','c','t','e','t','-','s','t','r','e','a','m',0};
+ static const WCHAR fileW[] = {'f','i','l','e',0};
+ static const WCHAR fmtW[] = {'%','d',0};
+ HRESULT hr;
+ char *unix_filename = NULL;
+ INT unix_filename_len;
+ WCHAR *unix_filenameW = NULL;
+ BSTR str = NULL;
+ URL_COMPONENTSW urlComponents;
+ DWORD urlSize = 4096;
+ WCHAR *url = NULL;
+ IXMLDOMElement *uriElement = NULL;
+ IXMLDOMElement *mimeTypeElement = NULL;
+ IXMLDOMElement *timestampElement = NULL;
+ IXMLDOMNode *child = NULL;
+ WCHAR buffer[50];
+ BOOL succeeded = FALSE;
+
+ unix_filename = wine_get_unix_file_name(filename);
+ if (unix_filename == NULL)
+ goto done;
+ unix_filename_len = MultiByteToWideChar(CP_ACP, 0, unix_filename, -1, NULL, 0);
+ unix_filenameW = HeapAlloc(GetProcessHeap(), 0, (unix_filename_len + 1) * sizeof(WCHAR));
+ if (unix_filenameW == NULL)
+ goto done;
+ MultiByteToWideChar(CP_ACP, 0, unix_filename, -1, unix_filenameW, -1);
+
+ str = SysAllocString(UriW);
+ if (str == NULL)
+ goto done;
+ hr = IXMLDOMDocument_createElement(document, str, &uriElement);
+ SysFreeString(str);
+ if (FAILED(hr))
+ goto done;
+ memset(&urlComponents, 0, sizeof(urlComponents));
+ urlComponents.dwStructSize = sizeof(urlComponents);
+ urlComponents.lpszScheme = (WCHAR*)fileW;
+ urlComponents.lpszUrlPath = unix_filenameW;
+ urlSize = 0;
+ InternetCreateUrlW(&urlComponents, ICU_ESCAPE, NULL, &urlSize);
+ url = HeapAlloc(GetProcessHeap(), 0, (urlSize + 1) * sizeof(WCHAR));
+ if (url == NULL)
+ goto done;
+ if (!InternetCreateUrlW(&urlComponents, ICU_ESCAPE, url, &urlSize))
+ goto done;
+ str = SysAllocString(url);
+ if (str == NULL)
+ goto done;
+ hr = IXMLDOMNode_put_text((IXMLDOMNode*)uriElement, str);
+ SysFreeString(str);
+ if (FAILED(hr))
+ goto done;
+
+ str = SysAllocString(MimeTypeW);
+ if (str == NULL)
+ goto done;
+ hr = IXMLDOMDocument_createElement(document, str, &mimeTypeElement);
+ SysFreeString(str);
+ if (FAILED(hr))
+ goto done;
+ str = SysAllocString(ApplicationOctetStreamW);
+ if (str == NULL)
+ goto done;
+ hr = IXMLDOMNode_put_text((IXMLDOMNode*)mimeTypeElement, str);
+ SysFreeString(str);
+ if (FAILED(hr))
+ goto done;
+
+ str = SysAllocString(TimestampW);
+ if (str == NULL)
+ goto done;
+ hr = IXMLDOMDocument_createElement(document, str, ×tampElement);
+ SysFreeString(str);
+ if (FAILED(hr))
+ goto done;
+ snprintfW(buffer, sizeof(buffer)/sizeof(WCHAR), fmtW, time(NULL));
+ str = SysAllocString(buffer);
+ if (str == NULL)
+ goto done;
+ hr = IXMLDOMNode_put_text((IXMLDOMNode*)timestampElement, str);
+ SysFreeString(str);
+ if (FAILED(hr))
+ goto done;
+
+ hr = IXMLDOMElement_appendChild(recentItem, (IXMLDOMNode*)uriElement, &child);
+ if (FAILED(hr))
+ goto done;
+ IXMLDOMNode_Release(child);
+ hr = IXMLDOMElement_appendChild(recentItem, (IXMLDOMNode*)mimeTypeElement, &child);
+ if (FAILED(hr))
+ goto done;
+ IXMLDOMNode_Release(child);
+ hr = IXMLDOMElement_appendChild(recentItem, (IXMLDOMNode*)timestampElement, &child);
+ if (FAILED(hr))
+ goto done;
+ IXMLDOMNode_Release(child);
+ succeeded = TRUE;
+
+done:
+ HeapFree(GetProcessHeap(), 0, unix_filename);
+ HeapFree(GetProcessHeap(), 0, unix_filenameW);
+ HeapFree(GetProcessHeap(), 0, url);
+ if (uriElement)
+ IXMLDOMElement_Release(uriElement);
+ if (mimeTypeElement)
+ IXMLDOMElement_Release(mimeTypeElement);
+ if (timestampElement)
+ IXMLDOMElement_Release(timestampElement);
+ return succeeded;
+}
+
+static void add_recent_item(IXMLDOMDocument *document, IXMLDOMNode *recentFilesNode, WCHAR *filename)
+{
+ static const WCHAR recentItemW[] = {'R','e','c','e','n','t','I','t','e','m',0};
+ BSTR recentItemBstr;
+ IXMLDOMNode *child = NULL;
+
+ recentItemBstr = SysAllocString(recentItemW);
+ if (recentItemBstr)
+ {
+ HRESULT hr;
+ IXMLDOMElement *recentItem;
+ hr = IXMLDOMDocument_createElement(document, recentItemBstr, &recentItem);
+ if (SUCCEEDED(hr))
+ {
+ if (add_recent_item_properties(document, filename, recentItem))
+ {
+ hr = IXMLDOMNode_appendChild(recentFilesNode, (IXMLDOMNode*)recentItem, &child);
+ if (SUCCEEDED(hr))
+ IXMLDOMNode_Release(child);
+ }
+ IXMLDOMNode_Release(recentItem);
+ }
+ SysFreeString(recentItemBstr);
+ }
+}
+
+static BOOL add_to_recently_used(IXMLDOMDocument *document, WCHAR *filename)
+{
+ static const WCHAR recentfilesW[] = {'R','e','c','e','n','t','F','i','l','e','s',0};
+ IXMLDOMNode *recentFilesNode = NULL;
+ BSTR recentFilesNodeName = NULL;
+ IXMLDOMNodeList *childNodes = NULL;
+ IXMLDOMNode *recentItem = NULL;
+ BOOL found = FALSE;
+ HRESULT hr;
+ BOOL ret = FALSE;
+
+ hr = IXMLDOMDocument_get_firstChild(document, &recentFilesNode);
+ if (FAILED(hr))
+ {
+ TRACE("error getting first node\n");
+ goto done;
+ }
+ hr = IXMLDOMNode_get_nodeName(recentFilesNode, &recentFilesNodeName);
+ if (FAILED(hr))
+ {
+ TRACE("error getting first node's name\n");
+ goto done;
+ }
+ if (strcmpiW(recentFilesNodeName, recentfilesW))
+ {
+ TRACE("first node isn't RecentFiles but %s\n", debugstr_w(recentFilesNodeName));
+ goto done;
+ }
+ hr = IXMLDOMNode_get_childNodes(recentFilesNode, &childNodes);
+ if (FAILED(hr))
+ {
+ TRACE("getting RecentFiles's children failed\n");
+ goto done;
+ }
+ for (hr = IXMLDOMNodeList_nextNode(childNodes, &recentItem);
+ SUCCEEDED(hr) && recentItem != NULL && !found;
+ hr = IXMLDOMNodeList_nextNode(childNodes, &recentItem))
+ {
+ static const WCHAR recentItemW[] = {'R','e','c','e','n','t','I','t','e','m',0};
+ BSTR name = NULL;
+ hr = IXMLDOMNode_get_nodeName(recentItem, &name);
+ if (SUCCEEDED(hr))
+ {
+ if (strcmpiW(name, recentItemW) == 0)
+ found = check_recent_item(recentItem, filename);
+ SysFreeString(name);
+ }
+ IXMLDOMNode_Release(recentItem);
+ }
+ if (!found)
+ add_recent_item(document, recentFilesNode, filename);
+ ret = TRUE;
+
+done:
+ if (recentFilesNode != NULL)
+ IXMLDOMNode_Release(recentFilesNode);
+ if (recentFilesNodeName != NULL)
+ SysFreeString(recentFilesNodeName);
+ if (childNodes != NULL)
+ IXMLDOMNodeList_Release(childNodes);
+ return ret;
+}
+
+static void save_recently_used(int fd, IXMLDOMDocument *document)
+{
+ BSTR bstr = NULL;
+ HRESULT hr;
+
+ hr = IXMLDOMDocument_get_xml(document, &bstr);
+ if (SUCCEEDED(hr))
+ {
+ INT size;
+ char *xml;
+ size = WideCharToMultiByte(CP_UTF8, 0, bstr, -1, NULL, 0, 0, 0);
+ xml = HeapAlloc(GetProcessHeap(), 0, size);
+ if (xml != NULL)
+ {
+ WideCharToMultiByte(CP_UTF8, 0, bstr, -1, xml, -1, 0, 0);
+ if (lseek(fd, 0, SEEK_SET) == 0)
+ {
+ int position = 0;
+ int ret;
+ do
+ {
+ ret = write(fd, &xml[position], size - position);
+ if (ret >= 0)
+ position += ret;
+ } while ((ret >= 0 || errno == EINTR) && (position < size));
+ if (ret > 0)
+ ftruncate(fd, size - 1);
+ }
+ HeapFree(GetProcessHeap(), 0, xml);
+ }
+ SysFreeString(bstr);
+ }
+ else
+ TRACE("error getting document to save\n");
+}
+
+/* http://standards.freedesktop.org/recent-file-spec/recent-file-spec-0.2.html */
+static void create_freedesktop_recent_document(WCHAR *filename, char *recently_used)
+{
+ int fd = -1;
+ BOOL need_unlock = FALSE;
+ WCHAR *xml = NULL;
+ IXMLDOMDocument *document = NULL;
+ HRESULT hr;
+
+ fd = open(recently_used, O_CREAT | O_RDWR, 0x600);
+ if (fd < 0)
+ goto done;
+ if (lockf(fd, F_LOCK, 0) != 0)
+ goto done;
+ need_unlock = TRUE;
+ xml = get_file_contents(fd);
+ if (xml == NULL)
+ goto done;
+
+ hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IXMLDOMDocument, (LPVOID*)&document);
+ if (FAILED(hr))
+ goto done;
+ if (strlenW(xml) > 0)
+ {
+ BSTR xmlBstr = SysAllocString(xml);
+ if (xmlBstr)
+ {
+ VARIANT_BOOL variantBool = VARIANT_TRUE;
+ hr = IXMLDOMDocument_loadXML(document, xmlBstr, &variantBool);
+ SysFreeString(xmlBstr);
+ if (FAILED(hr) || variantBool == VARIANT_FALSE)
+ {
+ TRACE("couldn't load recently used xml\n");
+ goto done;
+ }
+ }
+ else
+ goto done;
+ }
+ else
+ {
+ if (!populate_recently_used(document))
+ goto done;
+ }
+
+ if (!add_to_recently_used(document, filename))
+ goto done;
+
+ save_recently_used(fd, document);
+
+done:
+ if (fd >= 0)
+ {
+ if (need_unlock)
+ lockf(fd, F_ULOCK, 0);
+ close(fd);
+ }
+ HeapFree(GetProcessHeap(), 0, xml);
+ if (document)
+ IXMLDOMDocument_Release(document);
+}
+
/*************************************************************************
* SHAddToRecentDocs [SHELL32.@]
*
@@ -760,6 +1245,7 @@ void WINAPI SHAddToRecentDocs (UINT uFlags,LPCVOID pv)
UINT olderrormode;
HKEY HCUbasekey;
+ WCHAR *fullpath = NULL;
CHAR doc_name[MAX_PATH];
CHAR link_dir[MAX_PATH];
CHAR new_lnk_filepath[MAX_PATH];
@@ -880,15 +1366,33 @@ void WINAPI SHAddToRecentDocs (UINT uFlags,LPCVOID pv)
switch (uFlags)
{
case SHARD_PIDL:
- SHGetPathFromIDListA(pv, doc_name);
+ fullpath = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
+ if (fullpath == NULL)
+ goto fail;
+ if (!SHGetPathFromIDListA(pv, doc_name))
+ goto fail;
+ if (!SHGetPathFromIDListW(pv, fullpath))
+ goto fail;
break;
case SHARD_PATHA:
+ {
+ int size;
lstrcpynA(doc_name, pv, MAX_PATH);
+ size = MultiByteToWideChar(CP_ACP, 0, doc_name, -1, NULL, 0);
+ fullpath = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
+ if (fullpath == NULL)
+ goto fail;
+ MultiByteToWideChar(CP_ACP, 0, doc_name, -1, fullpath, -1);
break;
+ }
case SHARD_PATHW:
WideCharToMultiByte(CP_ACP, 0, pv, -1, doc_name, MAX_PATH, NULL, NULL);
+ fullpath = HeapAlloc(GetProcessHeap(), 0, (strlenW((WCHAR*)pv) + 1) * sizeof(WCHAR));
+ if (fullpath == NULL)
+ goto fail;
+ strcpyW(fullpath, (WCHAR*)pv);
break;
default:
@@ -1077,10 +1581,29 @@ void WINAPI SHAddToRecentDocs (UINT uFlags,LPCVOID pv)
}
}
+ /* *** JOB 3: Add a shortcut to freedesktop.org's recent documents *** */
+
+ {
+ char *home;
+ char *recently_used;
+
+ home = getenv("HOME");
+ if (home == NULL)
+ goto fail;
+ recently_used = HeapAlloc(GetProcessHeap(), 0, strlen(home) + 1 + 14 + 1);
+ if (recently_used)
+ {
+ sprintf(recently_used, "%s/.recently-used", home);
+ create_freedesktop_recent_document(fullpath, recently_used);
+ HeapFree(GetProcessHeap(), 0, recently_used);
+ }
+ }
+
fail:
CoUninitialize();
/* all done */
+ HeapFree(GetProcessHeap(), 0, fullpath);
RegCloseKey(HCUbasekey);
return;
}
More information about the wine-patches
mailing list