1 /** @file
2 * @brief Implementation of native file dialogs for Win32
3 */
4 /* Authors:
5 * Joel Holdsworth
6 * The Inkscape Organization
7 *
8 * Copyright (C) 2004-2008 The Inkscape Organization
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 #endif
17 #ifdef WIN32
19 //General includes
20 #include <list>
21 #include <unistd.h>
22 #include <sys/stat.h>
23 #include <errno.h>
24 #include <set>
25 #include <gdk/gdkwin32.h>
26 #include <glib/gstdio.h>
27 #include <glibmm/i18n.h>
28 #include <gtkmm/window.h>
30 //Inkscape includes
31 #include "inkscape.h"
32 #include <dialogs/dialog-events.h>
33 #include <extension/input.h>
34 #include <extension/output.h>
35 #include <extension/db.h>
37 #include <libnr/nr-pixops.h>
38 #include <libnr/nr-translate-scale-ops.h>
39 #include <display/nr-arena-item.h>
40 #include <display/nr-arena.h>
41 #include "sp-item.h"
42 #include "display/canvas-arena.h"
44 #include "filedialog.h"
45 #include "filedialogimpl-win32.h"
47 #include <zlib.h>
48 #include <cairomm/win32_surface.h>
49 #include <cairomm/context.h>
51 using namespace std;
52 using namespace Glib;
53 using namespace Cairo;
54 using namespace Gdk::Cairo;
56 namespace Inkscape
57 {
58 namespace UI
59 {
60 namespace Dialog
61 {
63 const int PreviewWidening = 150;
64 const char PreviewWindowClassName[] = "PreviewWnd";
65 const unsigned long MaxPreviewFileSize = 1344; // kB
67 #define IDC_SHOW_PREVIEW 1000
69 // Windows 2000 version of OPENFILENAMEW
70 struct OPENFILENAMEEXW : public OPENFILENAMEW {
71 void * pvReserved;
72 DWORD dwReserved;
73 DWORD FlagsEx;
74 };
76 struct Filter
77 {
78 gunichar2* name;
79 glong name_length;
80 gunichar2* filter;
81 glong filter_length;
82 Inkscape::Extension::Extension* mod;
83 };
85 ustring utf16_to_ustring(const wchar_t *utf16string, int utf16length = -1)
86 {
87 gchar *utf8string = g_utf16_to_utf8((const gunichar2*)utf16string,
88 utf16length, NULL, NULL, NULL);
89 ustring result(utf8string);
90 g_free(utf8string);
92 return result;
93 }
95 /*#########################################################################
96 ### F I L E D I A L O G B A S E C L A S S
97 #########################################################################*/
99 FileDialogBaseWin32::FileDialogBaseWin32(Gtk::Window &parent,
100 const Glib::ustring &dir, const gchar *title,
101 FileDialogType type, gchar const* /*preferenceBase*/) :
102 dialogType(type),
103 parent(parent),
104 _current_directory(dir)
105 {
106 _main_loop = NULL;
108 _filter_index = 1;
109 _filter_count = 0;
111 _title = (wchar_t*)g_utf8_to_utf16(title, -1, NULL, NULL, NULL);
112 g_assert(_title != NULL);
114 Glib::RefPtr<const Gdk::Window> parentWindow = parent.get_window();
115 g_assert(parentWindow->gobj() != NULL);
116 _ownerHwnd = (HWND)gdk_win32_drawable_get_handle((GdkDrawable*)parentWindow->gobj());
117 }
119 FileDialogBaseWin32::~FileDialogBaseWin32()
120 {
121 g_free(_title);
122 }
124 Inkscape::Extension::Extension *FileDialogBaseWin32::getSelectionType()
125 {
126 return _extension;
127 }
129 Glib::ustring FileDialogBaseWin32::getCurrentDirectory()
130 {
131 return _current_directory;
132 }
134 /*#########################################################################
135 ### F I L E O P E N
136 #########################################################################*/
138 bool FileOpenDialogImplWin32::_show_preview = true;
140 /**
141 * Constructor. Not called directly. Use the factory.
142 */
143 FileOpenDialogImplWin32::FileOpenDialogImplWin32(Gtk::Window &parent,
144 const Glib::ustring &dir,
145 FileDialogType fileTypes,
146 const gchar *title) :
147 FileDialogBaseWin32(parent, dir, title, fileTypes, "dialogs.open")
148 {
149 // Initalize to Autodetect
150 _extension = NULL;
152 // Set our dialog type (open, import, etc...)
153 dialogType = fileTypes;
155 _show_preview_button_bitmap = NULL;
156 _preview_wnd = NULL;
157 _file_dialog_wnd = NULL;
158 _base_window_proc = NULL;
160 _preview_file_size = 0;
161 _preview_bitmap = NULL;
162 _preview_file_icon = NULL;
163 _preview_document_width = 0;
164 _preview_document_height = 0;
165 _preview_image_width = 0;
166 _preview_image_height = 0;
167 _preview_emf_image = false;
169 _mutex = NULL;
171 createFilterMenu();
172 }
175 /**
176 * Destructor
177 */
178 FileOpenDialogImplWin32::~FileOpenDialogImplWin32()
179 {
180 if(_filter != NULL)
181 delete[] _filter;
182 if(_extension_map != NULL)
183 delete[] _extension_map;
184 }
186 void FileOpenDialogImplWin32::createFilterMenu()
187 {
188 list<Filter> filter_list;
190 // Compose the filter string
191 Inkscape::Extension::DB::InputList extension_list;
192 Inkscape::Extension::db.get_input_list(extension_list);
194 ustring all_inkscape_files_filter, all_image_files_filter;
195 Filter all_files, all_inkscape_files, all_image_files;
197 const gchar *all_files_filter_name = N_("All Files");
198 const gchar *all_inkscape_files_filter_name = N_("All Inkscape Files");
199 const gchar *all_image_files_filter_name = N_("All Image Files");
201 // Calculate the amount of memory required
202 int filter_count = 3; // 3 - one for All Files, All Images and All Inkscape Files
203 int filter_length = 1;
205 for (Inkscape::Extension::DB::InputList::iterator current_item = extension_list.begin();
206 current_item != extension_list.end(); current_item++)
207 {
208 Filter filter;
210 Inkscape::Extension::Input *imod = *current_item;
211 if (imod->deactivated()) continue;
213 // Type
214 filter.name = g_utf8_to_utf16(_(imod->get_filetypename()),
215 -1, NULL, &filter.name_length, NULL);
217 // Extension
218 const gchar *file_extension_name = imod->get_extension();
219 filter.filter = g_utf8_to_utf16(file_extension_name,
220 -1, NULL, &filter.filter_length, NULL);
222 filter.mod = imod;
223 filter_list.push_back(filter);
225 filter_length += filter.name_length +
226 filter.filter_length + 3; // Add 3 for two \0s and a *
228 // Add to the "All Inkscape Files" Entry
229 if(all_inkscape_files_filter.length() > 0)
230 all_inkscape_files_filter += ";*";
231 all_inkscape_files_filter += file_extension_name;
232 if( strncmp("image", imod->get_mimetype(), 5) == 0)
233 {
234 // Add to the "All Image Files" Entry
235 if(all_image_files_filter.length() > 0)
236 all_image_files_filter += ";*";
237 all_image_files_filter += file_extension_name;
238 }
240 filter_count++;
241 }
243 int extension_index = 0;
244 _extension_map = new Inkscape::Extension::Extension*[filter_count];
246 // Filter Image Files
247 all_image_files.name = g_utf8_to_utf16(all_image_files_filter_name,
248 -1, NULL, &all_image_files.name_length, NULL);
249 all_image_files.filter = g_utf8_to_utf16(all_image_files_filter.data(),
250 -1, NULL, &all_image_files.filter_length, NULL);
251 all_image_files.mod = NULL;
252 filter_list.push_front(all_image_files);
254 // Filter Inkscape Files
255 all_inkscape_files.name = g_utf8_to_utf16(all_inkscape_files_filter_name,
256 -1, NULL, &all_inkscape_files.name_length, NULL);
257 all_inkscape_files.filter = g_utf8_to_utf16(all_inkscape_files_filter.data(),
258 -1, NULL, &all_inkscape_files.filter_length, NULL);
259 all_inkscape_files.mod = NULL;
260 filter_list.push_front(all_inkscape_files);
262 // Filter All Files
263 all_files.name = g_utf8_to_utf16(all_files_filter_name,
264 -1, NULL, &all_files.name_length, NULL);
265 all_files.filter = NULL;
266 all_files.filter_length = 0;
267 all_files.mod = NULL;
268 filter_list.push_front(all_files);
270 filter_length += all_files.name_length + 3 +
271 all_inkscape_files.filter_length +
272 all_inkscape_files.name_length + 3 +
273 all_image_files.filter_length +
274 all_image_files.name_length + 3 + 1;
275 // Add 3 for 2*2 \0s and a *, and 1 for a trailing \0
277 _filter = new wchar_t[filter_length];
278 wchar_t *filterptr = _filter;
280 for(list<Filter>::iterator filter_iterator = filter_list.begin();
281 filter_iterator != filter_list.end(); filter_iterator++)
282 {
283 const Filter &filter = *filter_iterator;
285 wcsncpy(filterptr, (wchar_t*)filter.name, filter.name_length);
286 filterptr += filter.name_length;
287 g_free(filter.name);
289 *(filterptr++) = L'\0';
290 *(filterptr++) = L'*';
292 if(filter.filter != NULL)
293 {
294 wcsncpy(filterptr, (wchar_t*)filter.filter, filter.filter_length);
295 filterptr += filter.filter_length;
296 g_free(filter.filter);
297 }
299 *(filterptr++) = L'\0';
301 // Associate this input extension with the file type name
302 _extension_map[extension_index++] = filter.mod;
303 }
304 *(filterptr++) = L'\0';
306 _filter_count = extension_index;
307 _filter_index = 2; // Select the 2nd filter in the list - 2 is NOT the 3rd
308 }
310 void FileOpenDialogImplWin32::GetOpenFileName_thread()
311 {
312 OPENFILENAMEEXW ofn;
314 g_assert(this != NULL);
315 g_assert(_mutex != NULL);
317 WCHAR* current_directory_string = (WCHAR*)g_utf8_to_utf16(
318 _current_directory.data(), _current_directory.length(),
319 NULL, NULL, NULL);
321 memset(&ofn, 0, sizeof(ofn));
323 // Copy the selected file name, converting from UTF-8 to UTF-16
324 memset(_path_string, 0, sizeof(_path_string));
325 gunichar2* utf16_path_string = g_utf8_to_utf16(
326 myFilename.data(), -1, NULL, NULL, NULL);
327 wcsncpy(_path_string, (wchar_t*)utf16_path_string, _MAX_PATH);
328 g_free(utf16_path_string);
330 ofn.lStructSize = sizeof(ofn);
331 ofn.hwndOwner = _ownerHwnd;
332 ofn.lpstrFile = _path_string;
333 ofn.nMaxFile = _MAX_PATH;
334 ofn.lpstrFileTitle = NULL;
335 ofn.nMaxFileTitle = 0;
336 ofn.lpstrInitialDir = current_directory_string;
337 ofn.lpstrTitle = _title;
338 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_ENABLEHOOK | OFN_HIDEREADONLY | OFN_ENABLESIZING;
339 ofn.lpstrFilter = _filter;
340 ofn.nFilterIndex = _filter_index;
341 ofn.lpfnHook = GetOpenFileName_hookproc;
342 ofn.lCustData = (LPARAM)this;
344 _result = GetOpenFileNameW(&ofn) != 0;
346 g_assert(ofn.nFilterIndex >= 1 && ofn.nFilterIndex <= _filter_count);
347 _filter_index = ofn.nFilterIndex;
348 _extension = _extension_map[ofn.nFilterIndex - 1];
350 // Copy the selected file name, converting from UTF-16 to UTF-8
351 myFilename = utf16_to_ustring(_path_string, _MAX_PATH);
353 // Tidy up
354 g_free(current_directory_string);
356 _mutex->lock();
357 _finished = true;
358 _mutex->unlock();
359 }
361 void FileOpenDialogImplWin32::register_preview_wnd_class()
362 {
363 HINSTANCE hInstance = GetModuleHandle(NULL);
364 const WNDCLASSA PreviewWndClass =
365 {
366 CS_HREDRAW | CS_VREDRAW,
367 preview_wnd_proc,
368 0,
369 0,
370 hInstance,
371 NULL,
372 LoadCursor(hInstance, IDC_ARROW),
373 (HBRUSH)(COLOR_BTNFACE + 1),
374 NULL,
375 PreviewWindowClassName
376 };
378 RegisterClassA(&PreviewWndClass);
379 }
381 UINT_PTR CALLBACK FileOpenDialogImplWin32::GetOpenFileName_hookproc(
382 HWND hdlg, UINT uiMsg, WPARAM, LPARAM lParam)
383 {
384 FileOpenDialogImplWin32 *pImpl = (FileOpenDialogImplWin32*)
385 GetWindowLongPtr(hdlg, GWLP_USERDATA);
387 switch(uiMsg)
388 {
389 case WM_INITDIALOG:
390 {
391 HWND hParentWnd = GetParent(hdlg);
392 HINSTANCE hInstance = GetModuleHandle(NULL);
394 // Make the window a bit wider
395 RECT rcRect;
396 GetWindowRect(hParentWnd, &rcRect);
397 MoveWindow(hParentWnd, rcRect.left, rcRect.top,
398 rcRect.right - rcRect.left + PreviewWidening,
399 rcRect.bottom - rcRect.top,
400 FALSE);
402 // Set the pointer to the object
403 OPENFILENAMEW *ofn = (OPENFILENAMEW*)lParam;
404 SetWindowLongPtr(hdlg, GWLP_USERDATA, ofn->lCustData);
405 SetWindowLongPtr(hParentWnd, GWLP_USERDATA, ofn->lCustData);
406 pImpl = (FileOpenDialogImplWin32*)ofn->lCustData;
408 // Subclass the parent
409 pImpl->_base_window_proc = (WNDPROC)GetWindowLongPtr(hParentWnd, GWL_WNDPROC);
410 SetWindowLongPtr(hParentWnd, GWL_WNDPROC, (LONG_PTR)file_dialog_subclass_proc);
412 // Add a button to the toolbar
413 pImpl->_toolbar_wnd = FindWindowEx(hParentWnd, NULL, "ToolbarWindow32", NULL);
415 pImpl->_show_preview_button_bitmap = LoadBitmap(
416 hInstance, MAKEINTRESOURCE(IDC_SHOW_PREVIEW));
417 TBADDBITMAP tbAddBitmap = {NULL, (UINT)pImpl->_show_preview_button_bitmap};
418 const int iBitmapIndex = SendMessage(pImpl->_toolbar_wnd,
419 TB_ADDBITMAP, 1, (LPARAM)&tbAddBitmap);
421 TBBUTTON tbButton;
422 memset(&tbButton, 0, sizeof(TBBUTTON));
423 tbButton.iBitmap = iBitmapIndex;
424 tbButton.idCommand = IDC_SHOW_PREVIEW;
425 tbButton.fsState = (pImpl->_show_preview ? TBSTATE_CHECKED : 0)
426 | TBSTATE_ENABLED;
427 tbButton.fsStyle = TBSTYLE_CHECK;
428 tbButton.iString = (INT_PTR)_("Show Preview");
429 SendMessage(pImpl->_toolbar_wnd, TB_ADDBUTTONS, 1, (LPARAM)&tbButton);
431 // Create preview pane
432 register_preview_wnd_class();
434 pImpl->_mutex->lock();
436 pImpl->_file_dialog_wnd = hParentWnd;
438 pImpl->_preview_wnd =
439 CreateWindowA(PreviewWindowClassName, "",
440 WS_CHILD | WS_VISIBLE,
441 0, 0, 100, 100, hParentWnd, NULL, hInstance, NULL);
442 SetWindowLongPtr(pImpl->_preview_wnd, GWLP_USERDATA, ofn->lCustData);
444 pImpl->_mutex->unlock();
446 pImpl->layout_dialog();
447 }
448 break;
450 case WM_NOTIFY:
451 {
453 OFNOTIFY *pOFNotify = reinterpret_cast<OFNOTIFY*>(lParam);
454 switch(pOFNotify->hdr.code)
455 {
456 case CDN_SELCHANGE:
457 {
458 if(pImpl != NULL)
459 {
460 // Get the file name
461 pImpl->_mutex->lock();
463 SendMessage(pOFNotify->hdr.hwndFrom, CDM_GETFILEPATH,
464 sizeof(pImpl->_path_string) / sizeof(wchar_t),
465 (LPARAM)pImpl->_path_string);
467 pImpl->_file_selected = true;
469 pImpl->_mutex->unlock();
470 }
471 }
472 break;
473 }
474 }
475 break;
477 case WM_CLOSE:
478 pImpl->_mutex->lock();
479 pImpl->_preview_file_size = 0;
481 pImpl->_file_dialog_wnd = NULL;
482 DestroyWindow(pImpl->_preview_wnd);
483 pImpl->_preview_wnd = NULL;
484 DeleteObject(pImpl->_show_preview_button_bitmap);
485 pImpl->_show_preview_button_bitmap = NULL;
486 pImpl->_mutex->unlock();
488 break;
489 }
491 // Use default dialog behaviour
492 return 0;
493 }
495 LRESULT CALLBACK FileOpenDialogImplWin32::file_dialog_subclass_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
496 {
497 FileOpenDialogImplWin32 *pImpl = (FileOpenDialogImplWin32*)
498 GetWindowLongPtr(hwnd, GWLP_USERDATA);
500 LRESULT lResult = CallWindowProc(pImpl->_base_window_proc, hwnd, uMsg, wParam, lParam);
502 switch(uMsg)
503 {
504 case WM_SHOWWINDOW:
505 if(wParam != 0)
506 pImpl->layout_dialog();
507 break;
509 case WM_SIZE:
510 pImpl->layout_dialog();
511 break;
513 case WM_COMMAND:
514 if(wParam == IDC_SHOW_PREVIEW)
515 {
516 const bool enable = SendMessage(pImpl->_toolbar_wnd,
517 TB_ISBUTTONCHECKED, IDC_SHOW_PREVIEW, 0) != 0;
518 pImpl->enable_preview(enable);
519 }
520 break;
521 }
523 return lResult;
524 }
526 LRESULT CALLBACK FileOpenDialogImplWin32::preview_wnd_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
527 {
528 const int CaptionPadding = 4;
529 const int IconSize = 32;
531 FileOpenDialogImplWin32 *pImpl = (FileOpenDialogImplWin32*)
532 GetWindowLongPtr(hwnd, GWLP_USERDATA);
534 LRESULT lResult = 0;
536 switch(uMsg)
537 {
538 case WM_ERASEBKGND:
539 // Do nothing to erase the background
540 // - otherwise there'll be flicker
541 lResult = 1;
542 break;
544 case WM_PAINT:
545 {
546 // Get the client rect
547 RECT rcClient;
548 GetClientRect(hwnd, &rcClient);
550 // Prepare to paint
551 PAINTSTRUCT paint_struct;
552 HDC dc = BeginPaint(hwnd, &paint_struct);
554 HFONT hCaptionFont = (HFONT)SendMessage(GetParent(hwnd),
555 WM_GETFONT, 0, 0);
556 HFONT hOldFont = (HFONT)SelectObject(dc, hCaptionFont);
557 SetBkMode(dc, TRANSPARENT);
559 pImpl->_mutex->lock();
561 if(pImpl->_path_string[0] == 0)
562 {
563 FillRect(dc, &rcClient, (HBRUSH)(COLOR_3DFACE + 1));
564 DrawText(dc, _("No file selected"), -1, &rcClient,
565 DT_CENTER | DT_VCENTER | DT_NOPREFIX);
566 }
567 else if(pImpl->_preview_bitmap != NULL)
568 {
569 BITMAP bitmap;
570 GetObject(pImpl->_preview_bitmap, sizeof(bitmap), &bitmap);
571 const int destX = (rcClient.right - bitmap.bmWidth) / 2;
573 // Render the image
574 HDC hSrcDC = CreateCompatibleDC(dc);
575 HBITMAP hOldBitmap = (HBITMAP)SelectObject(hSrcDC, pImpl->_preview_bitmap);
577 BitBlt(dc, destX, 0, bitmap.bmWidth, bitmap.bmHeight,
578 hSrcDC, 0, 0, SRCCOPY);
580 SelectObject(hSrcDC, hOldBitmap);
581 DeleteDC(hSrcDC);
583 // Fill in the background area
584 HRGN hEraseRgn = CreateRectRgn(rcClient.left, rcClient.top,
585 rcClient.right, rcClient.bottom);
586 HRGN hImageRgn = CreateRectRgn(destX, 0,
587 destX + bitmap.bmWidth, bitmap.bmHeight);
588 CombineRgn(hEraseRgn, hEraseRgn, hImageRgn, RGN_DIFF);
590 FillRgn(dc, hEraseRgn, GetSysColorBrush(COLOR_3DFACE));
592 DeleteObject(hImageRgn);
593 DeleteObject(hEraseRgn);
595 // Draw the caption on
596 RECT rcCaptionRect = {rcClient.left,
597 rcClient.top + bitmap.bmHeight + CaptionPadding,
598 rcClient.right, rcClient.bottom};
600 WCHAR szCaption[_MAX_FNAME + 32];
601 const int iLength = pImpl->format_caption(
602 szCaption, sizeof(szCaption) / sizeof(WCHAR));
604 DrawTextW(dc, szCaption, iLength, &rcCaptionRect,
605 DT_CENTER | DT_TOP | DT_NOPREFIX | DT_PATH_ELLIPSIS);
606 }
607 else if(pImpl->_preview_file_icon != NULL)
608 {
609 FillRect(dc, &rcClient, (HBRUSH)(COLOR_3DFACE + 1));
611 // Draw the files icon
612 const int destX = (rcClient.right - IconSize) / 2;
613 DrawIconEx(dc, destX, 0, pImpl->_preview_file_icon,
614 IconSize, IconSize, 0, NULL,
615 DI_NORMAL | DI_COMPAT);
617 // Draw the caption on
618 RECT rcCaptionRect = {rcClient.left,
619 rcClient.top + IconSize + CaptionPadding,
620 rcClient.right, rcClient.bottom};
622 WCHAR szFileName[_MAX_FNAME], szCaption[_MAX_FNAME + 32];
623 _wsplitpath(pImpl->_path_string, NULL, NULL, szFileName, NULL);
625 const int iLength = snwprintf(szCaption,
626 sizeof(szCaption), L"%s\n%d kB",
627 szFileName, pImpl->_preview_file_size);
629 DrawTextW(dc, szCaption, iLength, &rcCaptionRect,
630 DT_CENTER | DT_TOP | DT_NOPREFIX | DT_PATH_ELLIPSIS);
631 }
632 else
633 {
634 // Can't show anything!
635 FillRect(dc, &rcClient, (HBRUSH)(COLOR_3DFACE + 1));
636 }
638 pImpl->_mutex->unlock();
640 // Finish painting
641 SelectObject(dc, hOldFont);
642 EndPaint(hwnd, &paint_struct);
643 }
645 break;
647 case WM_DESTROY:
648 pImpl->free_preview();
649 break;
651 default:
652 lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
653 break;
654 }
656 return lResult;
657 }
659 void FileOpenDialogImplWin32::enable_preview(bool enable)
660 {
661 _show_preview = enable;
663 // Relayout the dialog
664 ShowWindow(_preview_wnd, enable ? SW_SHOW : SW_HIDE);
665 layout_dialog();
667 // Load or unload the preview
668 if(enable)
669 {
670 _mutex->lock();
671 _file_selected = true;
672 _mutex->unlock();
673 }
674 else free_preview();
675 }
677 void FileOpenDialogImplWin32::layout_dialog()
678 {
679 union RECTPOINTS
680 {
681 RECT r;
682 POINT p[2];
683 };
685 const float MaxExtentScale = 2.0f / 3.0f;
687 RECT rcClient;
688 GetClientRect(_file_dialog_wnd, &rcClient);
690 // Re-layout the dialog
691 HWND hFileListWnd = GetDlgItem(_file_dialog_wnd, lst2);
692 HWND hFolderComboWnd = GetDlgItem(_file_dialog_wnd, cmb2);
695 RECT rcFolderComboRect;
696 RECTPOINTS rcFileList;
697 GetWindowRect(hFileListWnd, &rcFileList.r);
698 GetWindowRect(hFolderComboWnd, &rcFolderComboRect);
699 const int iPadding = rcFileList.r.top - rcFolderComboRect.bottom;
700 MapWindowPoints(NULL, _file_dialog_wnd, rcFileList.p, 2);
702 RECT rcPreview;
703 RECT rcBody = {rcFileList.r.left, rcFileList.r.top,
704 rcClient.right - iPadding, rcFileList.r.bottom};
705 rcFileList.r.right = rcBody.right;
707 if(_show_preview)
708 {
709 rcPreview.top = rcBody.top;
710 rcPreview.left = rcClient.right - (rcBody.bottom - rcBody.top);
711 const int iMaxExtent = (int)(MaxExtentScale * (float)(rcBody.left + rcBody.right)) + iPadding / 2;
712 if(rcPreview.left < iMaxExtent) rcPreview.left = iMaxExtent;
713 rcPreview.bottom = rcBody.bottom;
714 rcPreview.right = rcBody.right;
716 // Re-layout the preview box
717 _mutex->lock();
719 _preview_width = rcPreview.right - rcPreview.left;
720 _preview_height = rcPreview.bottom - rcPreview.top;
722 _mutex->unlock();
724 render_preview();
726 MoveWindow(_preview_wnd, rcPreview.left, rcPreview.top,
727 _preview_width, _preview_height, TRUE);
729 rcFileList.r.right = rcPreview.left - iPadding;
730 }
732 // Re-layout the file list box
733 MoveWindow(hFileListWnd, rcFileList.r.left, rcFileList.r.top,
734 rcFileList.r.right - rcFileList.r.left,
735 rcFileList.r.bottom - rcFileList.r.top, TRUE);
737 // Re-layout the toolbar
738 RECTPOINTS rcToolBar;
739 GetWindowRect(_toolbar_wnd, &rcToolBar.r);
740 MapWindowPoints(NULL, _file_dialog_wnd, rcToolBar.p, 2);
741 MoveWindow(_toolbar_wnd, rcToolBar.r.left, rcToolBar.r.top,
742 rcToolBar.r.right - rcToolBar.r.left, rcToolBar.r.bottom - rcToolBar.r.top, TRUE);
743 }
745 void FileOpenDialogImplWin32::file_selected()
746 {
747 // Destroy any previous previews
748 free_preview();
751 // Determine if the file exists
752 DWORD attributes = GetFileAttributesW(_path_string);
753 if(attributes == 0xFFFFFFFF ||
754 attributes == FILE_ATTRIBUTE_DIRECTORY)
755 {
756 InvalidateRect(_preview_wnd, NULL, FALSE);
757 return;
758 }
760 // Check the file exists and get the file size
761 HANDLE file_handle = CreateFileW(_path_string, GENERIC_READ,
762 FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
763 if(file_handle == INVALID_HANDLE_VALUE) return;
764 const DWORD file_size = GetFileSize(file_handle, NULL);
765 if (file_size == INVALID_FILE_SIZE) return;
766 _preview_file_size = file_size / 1024;
767 CloseHandle(file_handle);
769 if(_show_preview) load_preview();
770 }
772 void FileOpenDialogImplWin32::load_preview()
773 {
774 // Destroy any previous previews
775 free_preview();
777 // Try to get the file icon
778 SHFILEINFOW fileInfo;
779 if(SUCCEEDED(SHGetFileInfoW(_path_string, 0, &fileInfo,
780 sizeof(fileInfo), SHGFI_ICON | SHGFI_LARGEICON)))
781 _preview_file_icon = fileInfo.hIcon;
783 // Will this file be too big?
784 if(_preview_file_size > MaxPreviewFileSize)
785 {
786 InvalidateRect(_preview_wnd, NULL, FALSE);
787 return;
788 }
790 // Prepare to render a preview
791 const Glib::ustring svg = ".svg";
792 const Glib::ustring svgz = ".svgz";
793 const Glib::ustring emf = ".emf";
794 const Glib::ustring wmf = ".wmf";
795 const Glib::ustring path = utf16_to_ustring(_path_string);
797 bool success = false;
799 _preview_document_width = _preview_document_height = 0;
801 if ((dialogType == SVG_TYPES || dialogType == IMPORT_TYPES) &&
802 (hasSuffix(path, svg) || hasSuffix(path, svgz)))
803 success = set_svg_preview();
804 else if (hasSuffix(path, emf) || hasSuffix(path, wmf))
805 success = set_emf_preview();
806 else if (isValidImageFile(path))
807 success = set_image_preview();
808 else {
809 // Show no preview
810 }
812 if(success) render_preview();
814 InvalidateRect(_preview_wnd, NULL, FALSE);
815 }
817 void FileOpenDialogImplWin32::free_preview()
818 {
819 _mutex->lock();
820 if(_preview_bitmap != NULL)
821 DeleteObject(_preview_bitmap);
822 _preview_bitmap = NULL;
824 if(_preview_file_icon != NULL)
825 DestroyIcon(_preview_file_icon);
826 _preview_file_icon = NULL;
828 _preview_bitmap_image.clear();
829 _preview_emf_image = false;
830 _mutex->unlock();
831 }
833 bool FileOpenDialogImplWin32::set_svg_preview()
834 {
835 const int PreviewSize = 512;
837 gchar *utf8string = g_utf16_to_utf8((const gunichar2*)_path_string,
838 _MAX_PATH, NULL, NULL, NULL);
839 SPDocument *svgDoc = sp_document_new (utf8string, true);
840 g_free(utf8string);
842 // Check the document loaded properly
843 if(svgDoc == NULL) return false;
844 if(svgDoc->root == NULL)
845 {
846 sp_document_unref(svgDoc);
847 return false;
848 }
850 // Get the size of the document
851 const double svgWidth = sp_document_width(svgDoc);
852 const double svgHeight = sp_document_height(svgDoc);
854 // Find the minimum scale to fit the image inside the preview area
855 const double scaleFactorX = PreviewSize / svgWidth;
856 const double scaleFactorY = PreviewSize / svgHeight;
857 const double scaleFactor = (scaleFactorX > scaleFactorY) ? scaleFactorY : scaleFactorX;
859 // Now get the resized values
860 const double scaledSvgWidth = scaleFactor * svgWidth;
861 const double scaledSvgHeight = scaleFactor * svgHeight;
863 Geom::Rect area(Geom::Point(0, 0), Geom::Point(scaledSvgWidth, scaledSvgHeight));
864 NRRectL areaL = {0, 0, scaledSvgWidth, scaledSvgHeight};
865 NRRectL bbox = {0, 0, scaledSvgWidth, scaledSvgHeight};
867 // write object bbox to area
868 boost::optional<Geom::Rect> maybeArea(area);
869 sp_document_ensure_up_to_date (svgDoc);
870 sp_item_invoke_bbox((SPItem *) svgDoc->root, maybeArea,
871 sp_item_i2r_affine((SPItem *)(svgDoc->root)), TRUE);
873 NRArena *const arena = NRArena::create();
875 unsigned const key = sp_item_display_key_new(1);
877 NRArenaItem *root = sp_item_invoke_show((SPItem*)(svgDoc->root),
878 arena, key, SP_ITEM_SHOW_DISPLAY);
880 NRGC gc(NULL);
881 gc.transform = Geom::Matrix(Geom::Scale(scaleFactor, scaleFactor));
883 nr_arena_item_invoke_update (root, NULL, &gc,
884 NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE);
886 // Prepare a GDI compatible NRPixBlock
887 NRPixBlock pixBlock;
888 pixBlock.size = NR_PIXBLOCK_SIZE_BIG;
889 pixBlock.mode = NR_PIXBLOCK_MODE_R8G8B8;
890 pixBlock.empty = 1;
891 pixBlock.visible_area.x0 = pixBlock.area.x0 = 0;
892 pixBlock.visible_area.y0 = pixBlock.area.y0 = 0;
893 pixBlock.visible_area.x1 = pixBlock.area.x1 = scaledSvgWidth;
894 pixBlock.visible_area.y1 = pixBlock.area.y1 = scaledSvgHeight;
895 pixBlock.rs = 4 * ((3 * (int)scaledSvgWidth + 3) / 4);
896 pixBlock.data.px = g_try_new (unsigned char, pixBlock.rs * scaledSvgHeight);
898 // Fail if the pixblock failed to allocate
899 if(pixBlock.data.px == NULL)
900 {
901 sp_document_unref(svgDoc);
902 return false;
903 }
905 memset(pixBlock.data.px, 0xFF, pixBlock.rs * scaledSvgHeight);
907 memcpy(&root->bbox, &areaL, sizeof(areaL));
909 // Render the image
910 nr_arena_item_invoke_render(NULL, root, &bbox, &pixBlock, /*0*/NR_ARENA_ITEM_RENDER_NO_CACHE);
912 // Tidy up
913 sp_document_unref(svgDoc);
914 sp_item_invoke_hide((SPItem*)(svgDoc->root), key);
915 nr_object_unref((NRObject *) arena);
917 // Create the GDK pixbuf
918 _mutex->lock();
920 _preview_bitmap_image = Gdk::Pixbuf::create_from_data(
921 pixBlock.data.px, Gdk::COLORSPACE_RGB, false, 8,
922 (int)scaledSvgWidth, (int)scaledSvgHeight, pixBlock.rs,
923 sigc::ptr_fun(destroy_svg_rendering));
925 _preview_document_width = scaledSvgWidth;
926 _preview_document_height = scaledSvgHeight;
927 _preview_image_width = svgWidth;
928 _preview_image_height = svgHeight;
930 _mutex->unlock();
932 return true;
933 }
935 void FileOpenDialogImplWin32::destroy_svg_rendering(const guint8 *buffer)
936 {
937 g_assert(buffer != NULL);
938 g_free((void*)buffer);
939 }
941 bool FileOpenDialogImplWin32::set_image_preview()
942 {
943 const Glib::ustring path = utf16_to_ustring(_path_string, _MAX_PATH);
945 bool successful = false;
947 _mutex->lock();
949 try {
950 _preview_bitmap_image = Gdk::Pixbuf::create_from_file(path);
951 if (_preview_bitmap_image) {
952 _preview_image_width = _preview_bitmap_image->get_width();
953 _preview_document_width = _preview_image_width;
954 _preview_image_height = _preview_bitmap_image->get_height();
955 _preview_document_height = _preview_image_height;
956 successful = true;
957 }
958 }
959 catch (const Gdk::PixbufError&) {}
960 catch (const Glib::FileError&) {}
962 _mutex->unlock();
964 return successful;
965 }
967 // Aldus Placeable Header ===================================================
968 // Since we are a 32bit app, we have to be sure this structure compiles to
969 // be identical to a 16 bit app's version. To do this, we use the #pragma
970 // to adjust packing, we use a WORD for the hmf handle, and a SMALL_RECT
971 // for the bbox rectangle.
972 #pragma pack( push )
973 #pragma pack( 2 )
974 typedef struct
975 {
976 DWORD dwKey;
977 WORD hmf;
978 SMALL_RECT bbox;
979 WORD wInch;
980 DWORD dwReserved;
981 WORD wCheckSum;
982 } APMHEADER, *PAPMHEADER;
983 #pragma pack( pop )
986 static HENHMETAFILE
987 MyGetEnhMetaFileW( const WCHAR *filename )
988 {
989 // Try open as Enhanced Metafile
990 HENHMETAFILE hemf = GetEnhMetaFileW(filename);
992 if (!hemf) {
993 // Try open as Windows Metafile
994 HMETAFILE hmf = GetMetaFileW(filename);
996 METAFILEPICT mp;
997 HDC hDC;
999 if (!hmf) {
1000 WCHAR szTemp[MAX_PATH];
1002 DWORD dw = GetShortPathNameW( filename, szTemp, MAX_PATH );
1003 if (dw) {
1004 hmf = GetMetaFileW( szTemp );
1005 }
1006 }
1008 if (hmf) {
1009 // Convert Windows Metafile to Enhanced Metafile
1010 DWORD nSize = GetMetaFileBitsEx( hmf, 0, NULL );
1012 if (nSize) {
1013 BYTE *lpvData = new BYTE[nSize];
1014 if (lpvData) {
1015 DWORD dw = GetMetaFileBitsEx( hmf, nSize, lpvData );
1016 if (dw) {
1017 // Fill out a METAFILEPICT structure
1018 mp.mm = MM_ANISOTROPIC;
1019 mp.xExt = 1000;
1020 mp.yExt = 1000;
1021 mp.hMF = NULL;
1022 // Get a reference DC
1023 hDC = GetDC( NULL );
1024 // Make an enhanced metafile from the windows metafile
1025 hemf = SetWinMetaFileBits( nSize, lpvData, hDC, &mp );
1026 // Clean up
1027 ReleaseDC( NULL, hDC );
1028 DeleteMetaFile( hmf );
1029 }
1030 delete[] lpvData;
1031 }
1032 else {
1033 DeleteMetaFile( hmf );
1034 }
1035 }
1036 else {
1037 DeleteMetaFile( hmf );
1038 }
1039 }
1040 else {
1041 // Try open as Aldus Placeable Metafile
1042 HANDLE hFile;
1043 hFile = CreateFileW( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
1045 if (hFile != INVALID_HANDLE_VALUE) {
1046 DWORD nSize = GetFileSize( hFile, NULL );
1047 if (nSize) {
1048 BYTE *lpvData = new BYTE[nSize];
1049 if (lpvData) {
1050 DWORD dw = ReadFile( hFile, lpvData, nSize, &nSize, NULL );
1051 if (dw) {
1052 if ( ((PAPMHEADER)lpvData)->dwKey == 0x9ac6cdd7l ) {
1053 // Fill out a METAFILEPICT structure
1054 mp.mm = MM_ANISOTROPIC;
1055 mp.xExt = ((PAPMHEADER)lpvData)->bbox.Right - ((PAPMHEADER)lpvData)->bbox.Left;
1056 mp.xExt = ( mp.xExt * 2540l ) / (DWORD)(((PAPMHEADER)lpvData)->wInch);
1057 mp.yExt = ((PAPMHEADER)lpvData)->bbox.Bottom - ((PAPMHEADER)lpvData)->bbox.Top;
1058 mp.yExt = ( mp.yExt * 2540l ) / (DWORD)(((PAPMHEADER)lpvData)->wInch);
1059 mp.hMF = NULL;
1060 // Get a reference DC
1061 hDC = GetDC( NULL );
1062 // Create an enhanced metafile from the bits
1063 hemf = SetWinMetaFileBits( nSize, lpvData+sizeof(APMHEADER), hDC, &mp );
1064 // Clean up
1065 ReleaseDC( NULL, hDC );
1066 }
1067 }
1068 delete[] lpvData;
1069 }
1070 }
1071 CloseHandle( hFile );
1072 }
1073 }
1074 }
1076 return hemf;
1077 }
1080 bool FileOpenDialogImplWin32::set_emf_preview()
1081 {
1082 _mutex->lock();
1084 BOOL ok = FALSE;
1086 DWORD w = 0;
1087 DWORD h = 0;
1089 HENHMETAFILE hemf = MyGetEnhMetaFileW( _path_string );
1091 if (hemf)
1092 {
1093 ENHMETAHEADER emh;
1094 ZeroMemory(&emh, sizeof(emh));
1095 ok = GetEnhMetaFileHeader(hemf, sizeof(emh), &emh) != 0;
1097 w = (emh.rclFrame.right - emh.rclFrame.left);
1098 h = (emh.rclFrame.bottom - emh.rclFrame.top);
1100 DeleteEnhMetaFile(hemf);
1101 }
1103 if (ok)
1104 {
1105 const int PreviewSize = 512;
1107 // Get the size of the document
1108 const double emfWidth = w;
1109 const double emfHeight = h;
1111 // Find the minimum scale to fit the image inside the preview area
1112 const double scaleFactorX = PreviewSize / emfWidth;
1113 const double scaleFactorY = PreviewSize / emfHeight;
1114 const double scaleFactor = (scaleFactorX > scaleFactorY) ? scaleFactorY : scaleFactorX;
1116 // Now get the resized values
1117 const double scaledEmfWidth = scaleFactor * emfWidth;
1118 const double scaledEmfHeight = scaleFactor * emfHeight;
1120 _preview_document_width = scaledEmfWidth;
1121 _preview_document_height = scaledEmfHeight;
1122 _preview_image_width = emfWidth;
1123 _preview_image_height = emfHeight;
1125 _preview_emf_image = true;
1126 }
1128 _mutex->unlock();
1130 return ok;
1131 }
1133 void FileOpenDialogImplWin32::render_preview()
1134 {
1135 double x, y;
1136 const double blurRadius = 8;
1137 const double halfBlurRadius = blurRadius / 2;
1138 const int shaddowOffsetX = 0;
1139 const int shaddowOffsetY = 2;
1140 const int pagePadding = 5;
1141 const double shaddowAlpha = 0.75;
1143 // Is the preview showing?
1144 if(!_show_preview)
1145 return;
1147 // Do we have anything to render?
1148 _mutex->lock();
1150 if(!_preview_bitmap_image && !_preview_emf_image)
1151 {
1152 _mutex->unlock();
1153 return;
1154 }
1156 // Tidy up any previous bitmap renderings
1157 if(_preview_bitmap != NULL)
1158 DeleteObject(_preview_bitmap);
1159 _preview_bitmap = NULL;
1161 // Calculate the size of the caption
1162 int captionHeight = 0;
1164 if(_preview_wnd != NULL)
1165 {
1166 RECT rcCaptionRect;
1167 WCHAR szCaption[_MAX_FNAME + 32];
1168 const int iLength = format_caption(szCaption,
1169 sizeof(szCaption) / sizeof(WCHAR));
1171 HDC dc = GetDC(_preview_wnd);
1172 DrawTextW(dc, szCaption, iLength, &rcCaptionRect,
1173 DT_CENTER | DT_TOP | DT_NOPREFIX | DT_PATH_ELLIPSIS | DT_CALCRECT);
1174 ReleaseDC(_preview_wnd, dc);
1176 captionHeight = rcCaptionRect.bottom - rcCaptionRect.top;
1177 }
1179 // Find the minimum scale to fit the image inside the preview area
1180 const double scaleFactorX =
1181 ((double)_preview_width - pagePadding * 2 - blurRadius) / _preview_document_width;
1182 const double scaleFactorY =
1183 ((double)_preview_height - pagePadding * 2
1184 - shaddowOffsetY - halfBlurRadius - captionHeight) / _preview_document_height;
1185 double scaleFactor = (scaleFactorX > scaleFactorY) ? scaleFactorY : scaleFactorX;
1186 scaleFactor = (scaleFactor > 1.0) ? 1.0 : scaleFactor;
1188 // Now get the resized values
1189 const double scaledSvgWidth = scaleFactor * _preview_document_width;
1190 const double scaledSvgHeight = scaleFactor * _preview_document_height;
1192 const int svgX = pagePadding + halfBlurRadius;
1193 const int svgY = pagePadding;
1195 const int frameX = svgX - pagePadding;
1196 const int frameY = svgY - pagePadding;
1197 const int frameWidth = scaledSvgWidth + pagePadding * 2;
1198 const int frameHeight = scaledSvgHeight + pagePadding * 2;
1200 const int totalWidth = (int)ceil(frameWidth + blurRadius);
1201 const int totalHeight = (int)ceil(frameHeight + blurRadius);
1203 // Prepare the drawing surface
1204 HDC hDC = GetDC(_preview_wnd);
1205 HDC hMemDC = CreateCompatibleDC(hDC);
1206 _preview_bitmap = CreateCompatibleBitmap(hDC, totalWidth, totalHeight);
1207 HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, _preview_bitmap);
1208 Cairo::RefPtr<Win32Surface> surface = Win32Surface::create(hMemDC);
1209 Cairo::RefPtr<Context> context = Context::create(surface);
1211 // Paint the background to match the dialog colour
1212 const COLORREF background = GetSysColor(COLOR_3DFACE);
1213 context->set_source_rgb(
1214 GetRValue(background) / 255.0,
1215 GetGValue(background) / 255.0,
1216 GetBValue(background) / 255.0);
1217 context->paint();
1219 //----- Draw the drop shaddow -----//
1221 // Left Edge
1222 x = frameX + shaddowOffsetX - halfBlurRadius;
1223 Cairo::RefPtr<LinearGradient> leftEdgeFade = LinearGradient::create(
1224 x, 0.0, x + blurRadius, 0.0);
1225 leftEdgeFade->add_color_stop_rgba (0, 0, 0, 0, 0);
1226 leftEdgeFade->add_color_stop_rgba (1, 0, 0, 0, shaddowAlpha);
1227 context->set_source(leftEdgeFade);
1228 context->rectangle (x, frameY + shaddowOffsetY + halfBlurRadius,
1229 blurRadius, frameHeight - blurRadius);
1230 context->fill();
1232 // Right Edge
1233 x = frameX + frameWidth + shaddowOffsetX - halfBlurRadius;
1234 Cairo::RefPtr<LinearGradient> rightEdgeFade = LinearGradient::create(
1235 x, 0.0, x + blurRadius, 0.0);
1236 rightEdgeFade->add_color_stop_rgba (0, 0, 0, 0, shaddowAlpha);
1237 rightEdgeFade->add_color_stop_rgba (1, 0, 0, 0, 0);
1238 context->set_source(rightEdgeFade);
1239 context->rectangle (frameX + frameWidth + shaddowOffsetX - halfBlurRadius,
1240 frameY + shaddowOffsetY + halfBlurRadius,
1241 blurRadius, frameHeight - blurRadius);
1242 context->fill();
1244 // Top Edge
1245 y = frameY + shaddowOffsetY - halfBlurRadius;
1246 Cairo::RefPtr<LinearGradient> topEdgeFade = LinearGradient::create(
1247 0.0, y, 0.0, y + blurRadius);
1248 topEdgeFade->add_color_stop_rgba (0, 0, 0, 0, 0);
1249 topEdgeFade->add_color_stop_rgba (1, 0, 0, 0, shaddowAlpha);
1250 context->set_source(topEdgeFade);
1251 context->rectangle (frameX + shaddowOffsetX + halfBlurRadius, y,
1252 frameWidth - blurRadius, blurRadius);
1253 context->fill();
1255 // Bottom Edge
1256 y = frameY + frameHeight + shaddowOffsetY - halfBlurRadius;
1257 Cairo::RefPtr<LinearGradient> bottomEdgeFade = LinearGradient::create(
1258 0.0, y, 0.0, y + blurRadius);
1259 bottomEdgeFade->add_color_stop_rgba (0, 0, 0, 0, shaddowAlpha);
1260 bottomEdgeFade->add_color_stop_rgba (1, 0, 0, 0, 0);
1261 context->set_source(bottomEdgeFade);
1262 context->rectangle (frameX + shaddowOffsetX + halfBlurRadius, y,
1263 frameWidth - blurRadius, blurRadius);
1264 context->fill();
1266 // Top Left Corner
1267 x = frameX + shaddowOffsetX - halfBlurRadius;
1268 y = frameY + shaddowOffsetY - halfBlurRadius;
1269 Cairo::RefPtr<RadialGradient> topLeftCornerFade = RadialGradient::create(
1270 x + blurRadius, y + blurRadius, 0, x + blurRadius, y + blurRadius, blurRadius);
1271 topLeftCornerFade->add_color_stop_rgba (0, 0, 0, 0, shaddowAlpha);
1272 topLeftCornerFade->add_color_stop_rgba (1, 0, 0, 0, 0);
1273 context->set_source(topLeftCornerFade);
1274 context->rectangle (x, y, blurRadius, blurRadius);
1275 context->fill();
1277 // Top Right Corner
1278 x = frameX + frameWidth + shaddowOffsetX - halfBlurRadius;
1279 y = frameY + shaddowOffsetY - halfBlurRadius;
1280 Cairo::RefPtr<RadialGradient> topRightCornerFade = RadialGradient::create(
1281 x, y + blurRadius, 0, x, y + blurRadius, blurRadius);
1282 topRightCornerFade->add_color_stop_rgba (0, 0, 0, 0, shaddowAlpha);
1283 topRightCornerFade->add_color_stop_rgba (1, 0, 0, 0, 0);
1284 context->set_source(topRightCornerFade);
1285 context->rectangle (x, y, blurRadius, blurRadius);
1286 context->fill();
1288 // Bottom Left Corner
1289 x = frameX + shaddowOffsetX - halfBlurRadius;
1290 y = frameY + frameHeight + shaddowOffsetY - halfBlurRadius;
1291 Cairo::RefPtr<RadialGradient> bottomLeftCornerFade = RadialGradient::create(
1292 x + blurRadius, y, 0, x + blurRadius, y, blurRadius);
1293 bottomLeftCornerFade->add_color_stop_rgba (0, 0, 0, 0, shaddowAlpha);
1294 bottomLeftCornerFade->add_color_stop_rgba (1, 0, 0, 0, 0);
1295 context->set_source(bottomLeftCornerFade);
1296 context->rectangle (x, y, blurRadius, blurRadius);
1297 context->fill();
1299 // Bottom Right Corner
1300 x = frameX + frameWidth + shaddowOffsetX - halfBlurRadius;
1301 y = frameY + frameHeight + shaddowOffsetY - halfBlurRadius;
1302 Cairo::RefPtr<RadialGradient> bottomRightCornerFade = RadialGradient::create(
1303 x, y, 0, x, y, blurRadius);
1304 bottomRightCornerFade->add_color_stop_rgba (0, 0, 0, 0, shaddowAlpha);
1305 bottomRightCornerFade->add_color_stop_rgba (1, 0, 0, 0, 0);
1306 context->set_source(bottomRightCornerFade);
1307 context->rectangle (frameX + frameWidth + shaddowOffsetX - halfBlurRadius,
1308 frameY + frameHeight + shaddowOffsetY - halfBlurRadius,
1309 blurRadius, blurRadius);
1310 context->fill();
1312 // Draw the frame
1313 context->set_line_width(1);
1314 context->rectangle (frameX, frameY, frameWidth, frameHeight);
1316 context->set_source_rgb(1.0, 1.0, 1.0);
1317 context->fill_preserve();
1318 context->set_source_rgb(0.25, 0.25, 0.25);
1319 context->stroke_preserve();
1321 // Draw the image
1322 if(_preview_bitmap_image) // Is the image a pixbuf?
1323 {
1324 // Set the transformation
1325 const Matrix matrix = {
1326 scaleFactor, 0,
1327 0, scaleFactor,
1328 svgX, svgY };
1329 context->set_matrix (matrix);
1331 // Render the image
1332 set_source_pixbuf (context, _preview_bitmap_image, 0, 0);
1333 context->paint();
1335 // Reset the transformation
1336 context->set_identity_matrix();
1337 }
1339 // Draw the inner frame
1340 context->set_source_rgb(0.75, 0.75, 0.75);
1341 context->rectangle (svgX, svgY, scaledSvgWidth, scaledSvgHeight);
1342 context->stroke();
1344 _mutex->unlock();
1346 // Finish drawing
1347 surface->finish();
1349 if (_preview_emf_image) {
1350 HENHMETAFILE hemf = MyGetEnhMetaFileW(_path_string);
1351 if (hemf) {
1352 RECT rc;
1353 rc.top = svgY+2;
1354 rc.left = svgX+2;
1355 rc.bottom = scaledSvgHeight-2;
1356 rc.right = scaledSvgWidth-2;
1357 PlayEnhMetaFile(hMemDC, hemf, &rc);
1358 DeleteEnhMetaFile(hemf);
1359 }
1360 }
1362 SelectObject(hMemDC, hOldBitmap) ;
1363 DeleteDC(hMemDC);
1365 // Refresh the preview pane
1366 InvalidateRect(_preview_wnd, NULL, FALSE);
1367 }
1369 int FileOpenDialogImplWin32::format_caption(wchar_t *caption, int caption_size)
1370 {
1371 wchar_t szFileName[_MAX_FNAME];
1372 _wsplitpath(_path_string, NULL, NULL, szFileName, NULL);
1374 return snwprintf(caption, caption_size,
1375 L"%s\n%d kB\n%d \xD7 %d", szFileName, _preview_file_size,
1376 (int)_preview_document_width, (int)_preview_document_height);
1377 }
1379 /**
1380 * Show this dialog modally. Return true if user hits [OK]
1381 */
1382 bool
1383 FileOpenDialogImplWin32::show()
1384 {
1385 // We can only run one worker thread at a time
1386 if(_mutex != NULL) return false;
1388 if(!Glib::thread_supported())
1389 Glib::thread_init();
1391 _result = false;
1392 _finished = false;
1393 _file_selected = false;
1394 _mutex = new Glib::Mutex();
1395 _main_loop = g_main_loop_new(g_main_context_default(), FALSE);
1397 if(Glib::Thread::create(sigc::mem_fun(*this, &FileOpenDialogImplWin32::GetOpenFileName_thread), true))
1398 {
1399 while(1)
1400 {
1401 g_main_context_iteration(g_main_context_default(), FALSE);
1403 if(_mutex->trylock())
1404 {
1405 // Read mutexed data
1406 const bool finished = _finished;
1407 const bool is_file_selected = _file_selected;
1408 _file_selected = false;
1409 _mutex->unlock();
1411 if(finished) break;
1412 if(is_file_selected) file_selected();
1413 }
1415 Sleep(10);
1416 }
1417 }
1419 // Tidy up
1420 delete _mutex;
1421 _mutex = NULL;
1423 return _result;
1424 }
1426 /**
1427 * To Get Multiple filenames selected at-once.
1428 */
1429 std::vector<Glib::ustring>FileOpenDialogImplWin32::getFilenames()
1430 {
1431 std::vector<Glib::ustring> result;
1432 result.push_back(getFilename());
1433 return result;
1434 }
1437 /*#########################################################################
1438 ### F I L E S A V E
1439 #########################################################################*/
1441 /**
1442 * Constructor
1443 */
1444 FileSaveDialogImplWin32::FileSaveDialogImplWin32(Gtk::Window &parent,
1445 const Glib::ustring &dir,
1446 FileDialogType fileTypes,
1447 const char *title,
1448 const Glib::ustring &/*default_key*/,
1449 const char *docTitle) :
1450 FileDialogBaseWin32(parent, dir, title, fileTypes, "dialogs.save_as"),
1451 _title_label(NULL),
1452 _title_edit(NULL)
1453 {
1454 FileSaveDialog::myDocTitle = docTitle;
1455 createFilterMenu();
1456 }
1458 FileSaveDialogImplWin32::~FileSaveDialogImplWin32()
1459 {
1460 }
1462 void FileSaveDialogImplWin32::createFilterMenu()
1463 {
1464 list<Filter> filter_list;
1466 knownExtensions.clear();
1468 // Compose the filter string
1469 Glib::ustring all_inkscape_files_filter, all_image_files_filter;
1470 Inkscape::Extension::DB::OutputList extension_list;
1471 Inkscape::Extension::db.get_output_list(extension_list);
1473 int filter_count = 0;
1474 int filter_length = 1;
1476 for (Inkscape::Extension::DB::OutputList::iterator current_item = extension_list.begin();
1477 current_item != extension_list.end(); current_item++)
1478 {
1479 Inkscape::Extension::Output *omod = *current_item;
1480 if (omod->deactivated()) continue;
1482 filter_count++;
1484 Filter filter;
1486 // Extension
1487 const gchar *filter_extension = omod->get_extension();
1488 filter.filter = g_utf8_to_utf16(
1489 filter_extension, -1, NULL, &filter.filter_length, NULL);
1490 knownExtensions.insert( Glib::ustring(filter_extension).casefold() );
1492 // Type
1493 filter.name = g_utf8_to_utf16(
1494 _(omod->get_filetypename()), -1, NULL, &filter.name_length, NULL);
1496 filter.mod = omod;
1498 filter_length += filter.name_length +
1499 filter.filter_length + 3; // Add 3 for two \0s and a *
1501 filter_list.push_back(filter);
1502 }
1504 int extension_index = 0;
1505 _extension_map = new Inkscape::Extension::Extension*[filter_count];
1507 _filter = new wchar_t[filter_length];
1508 wchar_t *filterptr = _filter;
1510 for(list<Filter>::iterator filter_iterator = filter_list.begin();
1511 filter_iterator != filter_list.end(); filter_iterator++)
1512 {
1513 const Filter &filter = *filter_iterator;
1515 wcsncpy(filterptr, (wchar_t*)filter.name, filter.name_length);
1516 filterptr += filter.name_length;
1517 g_free(filter.name);
1519 *(filterptr++) = L'\0';
1520 *(filterptr++) = L'*';
1522 wcsncpy(filterptr, (wchar_t*)filter.filter, filter.filter_length);
1523 filterptr += filter.filter_length;
1524 g_free(filter.filter);
1526 *(filterptr++) = L'\0';
1528 // Associate this input extension with the file type name
1529 _extension_map[extension_index++] = filter.mod;
1530 }
1531 *(filterptr++) = 0;
1533 _filter_count = extension_index;
1534 _filter_index = 1; // A value of 1 selects the 1st filter - NOT the 2nd
1535 }
1537 void FileSaveDialogImplWin32::GetSaveFileName_thread()
1538 {
1539 OPENFILENAMEEXW ofn;
1541 g_assert(this != NULL);
1542 g_assert(_main_loop != NULL);
1544 WCHAR* current_directory_string = (WCHAR*)g_utf8_to_utf16(
1545 _current_directory.data(), _current_directory.length(),
1546 NULL, NULL, NULL);
1548 // Copy the selected file name, converting from UTF-8 to UTF-16
1549 memset(_path_string, 0, sizeof(_path_string));
1550 gunichar2* utf16_path_string = g_utf8_to_utf16(
1551 myFilename.data(), -1, NULL, NULL, NULL);
1552 wcsncpy(_path_string, (wchar_t*)utf16_path_string, _MAX_PATH);
1553 g_free(utf16_path_string);
1555 ZeroMemory(&ofn, sizeof(ofn));
1556 ofn.lStructSize = sizeof(ofn);
1557 ofn.hwndOwner = _ownerHwnd;
1558 ofn.lpstrFile = _path_string;
1559 ofn.nMaxFile = _MAX_PATH;
1560 ofn.nFilterIndex = _filter_index;
1561 ofn.lpstrFileTitle = NULL;
1562 ofn.nMaxFileTitle = 0;
1563 ofn.lpstrInitialDir = current_directory_string;
1564 ofn.lpstrTitle = _title;
1565 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_ENABLEHOOK;
1566 ofn.lpstrFilter = _filter;
1567 ofn.nFilterIndex = _filter_index;
1568 ofn.lpfnHook = GetSaveFileName_hookproc;
1569 ofn.lCustData = (LPARAM)this;
1571 _result = GetSaveFileNameW(&ofn) != 0;
1573 g_assert(ofn.nFilterIndex >= 1 && ofn.nFilterIndex <= _filter_count);
1574 _filter_index = ofn.nFilterIndex;
1575 _extension = _extension_map[ofn.nFilterIndex - 1];
1577 // Copy the selected file name, converting from UTF-16 to UTF-8
1578 myFilename = utf16_to_ustring(_path_string, _MAX_PATH);
1580 // Tidy up
1581 g_free(current_directory_string);
1583 g_main_loop_quit(_main_loop);
1584 }
1586 /**
1587 * Show this dialog modally. Return true if user hits [OK]
1588 */
1589 bool
1590 FileSaveDialogImplWin32::show()
1591 {
1592 if(!Glib::thread_supported())
1593 Glib::thread_init();
1595 _result = false;
1596 _main_loop = g_main_loop_new(g_main_context_default(), FALSE);
1598 if(_main_loop != NULL)
1599 {
1600 if(Glib::Thread::create(sigc::mem_fun(*this, &FileSaveDialogImplWin32::GetSaveFileName_thread), true))
1601 g_main_loop_run(_main_loop);
1603 if(_result)
1604 appendExtension(myFilename, (Inkscape::Extension::Output*)_extension);
1605 }
1607 return _result;
1608 }
1610 void FileSaveDialogImplWin32::setSelectionType( Inkscape::Extension::Extension * /*key*/ )
1611 {
1612 // If no pointer to extension is passed in, look up based on filename extension.
1614 }
1616 UINT_PTR CALLBACK FileSaveDialogImplWin32::GetSaveFileName_hookproc(
1617 HWND hdlg, UINT uiMsg, WPARAM, LPARAM lParam)
1618 {
1619 FileSaveDialogImplWin32 *pImpl = (FileSaveDialogImplWin32*)
1620 GetWindowLongPtr(hdlg, GWLP_USERDATA);
1622 switch(uiMsg)
1623 {
1624 case WM_INITDIALOG:
1625 {
1626 HWND hParentWnd = GetParent(hdlg);
1627 HINSTANCE hInstance = GetModuleHandle(NULL);
1629 // get size/pos of typical combo box
1630 RECT rEDT1, rCB1, rROOT, rST;
1631 GetWindowRect(GetDlgItem(hParentWnd, cmb1), &rCB1);
1632 GetWindowRect(GetDlgItem(hParentWnd, cmb13), &rEDT1);
1633 GetWindowRect(GetDlgItem(hParentWnd, stc2), &rST);
1634 GetWindowRect(hdlg, &rROOT);
1635 int ydelta = rCB1.top - rEDT1.top;
1637 // Make the window a bit longer
1638 RECT rcRect;
1639 GetWindowRect(hParentWnd, &rcRect);
1640 MoveWindow(hParentWnd, rcRect.left, rcRect.top, rcRect.right - rcRect.left,
1641 rcRect.bottom - rcRect.top + ydelta, FALSE);
1643 // It is not necessary to delete stock objects by calling DeleteObject
1644 HGDIOBJ dlgFont = GetStockObject(DEFAULT_GUI_FONT);
1646 // Set the pointer to the object
1647 OPENFILENAMEW *ofn = (OPENFILENAMEW*)lParam;
1648 SetWindowLongPtr(hdlg, GWLP_USERDATA, ofn->lCustData);
1649 SetWindowLongPtr(hParentWnd, GWLP_USERDATA, ofn->lCustData);
1650 pImpl = (FileSaveDialogImplWin32*)ofn->lCustData;
1652 // Create the Title label and edit control
1653 pImpl->_title_label = CreateWindowEx(NULL, "STATIC", "Title:",
1654 WS_VISIBLE|WS_CHILD,
1655 CW_USEDEFAULT, CW_USEDEFAULT, rCB1.left-rST.left, rST.bottom-rST.top,
1656 hParentWnd, NULL, hInstance, NULL);
1657 if(pImpl->_title_label) {
1658 if(dlgFont) SendMessage(pImpl->_title_label, WM_SETFONT, (WPARAM)dlgFont, MAKELPARAM(FALSE, 0));
1659 SetWindowPos(pImpl->_title_label, NULL, rST.left-rROOT.left, rST.top+ydelta-rROOT.top,
1660 rCB1.left-rST.left, rST.bottom-rST.top, SWP_SHOWWINDOW|SWP_NOZORDER);
1661 }
1663 pImpl->_title_edit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
1664 WS_VISIBLE|WS_CHILD|WS_TABSTOP|ES_AUTOHSCROLL,
1665 CW_USEDEFAULT, CW_USEDEFAULT, rCB1.right-rCB1.left, rCB1.bottom-rCB1.top,
1666 hParentWnd, NULL, hInstance, NULL);
1667 if(pImpl->_title_edit) {
1668 if(dlgFont) SendMessage(pImpl->_title_edit, WM_SETFONT, (WPARAM)dlgFont, MAKELPARAM(FALSE, 0));
1669 SetWindowPos(pImpl->_title_edit, NULL, rCB1.left-rROOT.left, rCB1.top+ydelta-rROOT.top,
1670 rCB1.right-rCB1.left, rCB1.bottom-rCB1.top, SWP_SHOWWINDOW|SWP_NOZORDER);
1671 // TODO: make sure this works for Unicode
1672 SetWindowText(pImpl->_title_edit, pImpl->myDocTitle.c_str());
1673 }
1674 }
1675 break;
1676 case WM_DESTROY:
1677 {
1678 if(pImpl->_title_edit) {
1679 int length = GetWindowTextLength(pImpl->_title_edit)+1;
1680 char* temp_title = new char[length];
1681 GetWindowText(pImpl->_title_edit, temp_title, length);
1682 pImpl->myDocTitle = temp_title;
1683 delete[] temp_title;
1684 DestroyWindow(pImpl->_title_label);
1685 pImpl->_title_label = NULL;
1686 DestroyWindow(pImpl->_title_edit);
1687 pImpl->_title_edit = NULL;
1688 }
1689 }
1690 break;
1691 }
1693 // Use default dialog behaviour
1694 return 0;
1695 }
1697 }
1698 }
1699 }
1701 #endif
1703 /*
1704 Local Variables:
1705 mode:c++
1706 c-file-style:"stroustrup"
1707 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1708 indent-tabs-mode:nil
1709 fill-column:99
1710 End:
1711 */
1712 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :