1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /**
3  * @file
4  * Implementation of native file dialogs for Win32.
5  */
6 /* Authors:
7  *   Joel Holdsworth
8  *   The Inkscape Organization
9  *   Abhishek Sharma
10  *
11  * Copyright (C) 2004-2008 The Inkscape Organization
12  *
13  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14  */
15 #ifdef _WIN32
16 
17 #include "filedialogimpl-win32.h"
18 // General includes
19 #include <cairomm/win32_surface.h>
20 #include <gdk/gdkwin32.h>
21 #include <gdkmm/general.h>
22 #include <glibmm/fileutils.h>
23 #include <glibmm/i18n.h>
24 #include <list>
25 #include <vector>
26 
27 //Inkscape includes
28 #include "display/cairo-utils.h"
29 #include "document.h"
30 #include "extension/db.h"
31 #include "extension/input.h"
32 #include "extension/output.h"
33 #include "filedialog.h"
34 #include "helper/pixbuf-ops.h"
35 #include "preferences.h"
36 #include "util/units.h"
37 
38 
39 using namespace Glib;
40 using namespace Cairo;
41 using namespace Gdk::Cairo;
42 
43 namespace Inkscape
44 {
45 namespace UI
46 {
47 namespace Dialog
48 {
49 
50 const int PREVIEW_WIDENING = 150;
51 const int WINDOW_WIDTH_MINIMUM = 32;
52 const int WINDOW_WIDTH_FALLBACK = 450;
53 const int WINDOW_HEIGHT_MINIMUM = 32;
54 const int WINDOW_HEIGHT_FALLBACK = 360;
55 const char PreviewWindowClassName[] = "PreviewWnd";
56 const unsigned long MaxPreviewFileSize = 10240; // kB
57 
58 #define IDC_SHOW_PREVIEW    1000
59 
60 struct Filter
61 {
62     gunichar2* name;
63     glong name_length;
64     gunichar2* filter;
65     glong filter_length;
66     Inkscape::Extension::Extension* mod;
67 };
68 
utf16_to_ustring(const wchar_t * utf16string,int utf16length=-1)69 ustring utf16_to_ustring(const wchar_t *utf16string, int utf16length = -1)
70 {
71     gchar *utf8string = g_utf16_to_utf8((const gunichar2*)utf16string,
72         utf16length, NULL, NULL, NULL);
73     ustring result(utf8string);
74     g_free(utf8string);
75 
76     return result;
77 }
78 
79 namespace {
80 
sanitizeWindowSizeParam(int size,int delta,int minimum,int fallback)81 int sanitizeWindowSizeParam( int size, int delta, int minimum, int fallback )
82 {
83     int result = size;
84     if ( size < minimum ) {
85         g_warning( "Window size %d is less than cutoff.", size );
86         result = fallback - delta;
87     }
88     result += delta;
89     return result;
90 }
91 
92 } // namespace
93 
94 /*#########################################################################
95 ### F I L E     D I A L O G    B A S E    C L A S S
96 #########################################################################*/
97 
FileDialogBaseWin32(Gtk::Window & parent,const Glib::ustring & dir,const gchar * title,FileDialogType type,gchar const *)98 FileDialogBaseWin32::FileDialogBaseWin32(Gtk::Window &parent,
99         const Glib::ustring &dir, const gchar *title,
100         FileDialogType type, gchar const* /*preferenceBase*/) :
101         dialogType(type),
102         parent(parent),
103         _current_directory(dir)
104 {
105     _main_loop = NULL;
106 
107     _filter_index = 1;
108     _filter_count = 0;
109 
110     _title = (wchar_t*)g_utf8_to_utf16(title, -1, NULL, NULL, NULL);
111     g_assert(_title != NULL);
112 
113     Glib::RefPtr<const Gdk::Window> parentWindow = parent.get_window();
114     g_assert(parentWindow->gobj() != NULL);
115     _ownerHwnd = (HWND)gdk_win32_window_get_handle((GdkWindow*)parentWindow->gobj());
116 }
117 
~FileDialogBaseWin32()118 FileDialogBaseWin32::~FileDialogBaseWin32()
119 {
120     g_free(_title);
121 }
122 
getSelectionType()123 Inkscape::Extension::Extension *FileDialogBaseWin32::getSelectionType()
124 {
125     return _extension;
126 }
127 
getCurrentDirectory()128 Glib::ustring FileDialogBaseWin32::getCurrentDirectory()
129 {
130     return _current_directory;
131 }
132 
133 /*#########################################################################
134 ### F I L E    O P E N
135 #########################################################################*/
136 
137 bool FileOpenDialogImplWin32::_show_preview = true;
138 
139 /**
140  * Constructor.  Not called directly.  Use the factory.
141  */
FileOpenDialogImplWin32(Gtk::Window & parent,const Glib::ustring & dir,FileDialogType fileTypes,const gchar * title)142 FileOpenDialogImplWin32::FileOpenDialogImplWin32(Gtk::Window &parent,
143                                        const Glib::ustring &dir,
144                                        FileDialogType fileTypes,
145                                        const gchar *title) :
146     FileDialogBaseWin32(parent, dir, title, fileTypes, "dialogs.open")
147 {
148     // Initialize to Autodetect
149     _extension = NULL;
150 
151     // Set our dialog type (open, import, etc...)
152     dialogType = fileTypes;
153 
154     _show_preview_button_bitmap = NULL;
155     _preview_wnd = NULL;
156     _file_dialog_wnd = NULL;
157     _base_window_proc = NULL;
158 
159     _preview_file_size = 0;
160     _preview_bitmap = NULL;
161     _preview_file_icon = NULL;
162     _preview_document_width = 0;
163     _preview_document_height = 0;
164     _preview_image_width = 0;
165     _preview_image_height = 0;
166     _preview_emf_image = false;
167 
168     _mutex = NULL;
169 
170     if (dialogType != CUSTOM_TYPE)
171     	createFilterMenu();
172 }
173 
174 
175 /**
176  * Destructor
177  */
~FileOpenDialogImplWin32()178 FileOpenDialogImplWin32::~FileOpenDialogImplWin32()
179 {
180     if(_filter != NULL)
181         delete[] _filter;
182     if(_extension_map != NULL)
183         delete[] _extension_map;
184 }
185 
addFilterMenu(Glib::ustring name,Glib::ustring pattern)186 void FileOpenDialogImplWin32::addFilterMenu(Glib::ustring name, Glib::ustring pattern)
187 {
188     std::list<Filter> filter_list;
189 
190     Filter all_exe_files;
191 
192     const gchar *all_exe_files_filter_name = name.data();
193     const gchar *all_exe_files_filter = pattern.data();
194 
195     // Calculate the amount of memory required
196     int filter_count = 1;
197     int filter_length = 1;
198 
199     int extension_index = 0;
200     _extension_map = new Inkscape::Extension::Extension*[filter_count];
201 
202     // Filter Executable Files
203     all_exe_files.name = g_utf8_to_utf16(all_exe_files_filter_name,
204         -1, NULL, &all_exe_files.name_length, NULL);
205     all_exe_files.filter = g_utf8_to_utf16(all_exe_files_filter,
206             -1, NULL, &all_exe_files.filter_length, NULL);
207     all_exe_files.mod = NULL;
208     filter_list.push_front(all_exe_files);
209 
210     filter_length = all_exe_files.name_length + all_exe_files.filter_length + 3; // Add 3 for two \0s and a *
211 
212     _filter = new wchar_t[filter_length];
213     wchar_t *filterptr = _filter;
214 
215     for(std::list<Filter>::iterator filter_iterator = filter_list.begin();
216         filter_iterator != filter_list.end(); ++filter_iterator)
217     {
218         const Filter &filter = *filter_iterator;
219 
220         wcsncpy(filterptr, (wchar_t*)filter.name, filter.name_length);
221         filterptr += filter.name_length;
222         g_free(filter.name);
223 
224         *(filterptr++) = L'\0';
225         *(filterptr++) = L'*';
226 
227         if(filter.filter != NULL)
228         {
229             wcsncpy(filterptr, (wchar_t*)filter.filter, filter.filter_length);
230             filterptr += filter.filter_length;
231             g_free(filter.filter);
232         }
233 
234         *(filterptr++) = L'\0';
235 
236         // Associate this input extension with the file type name
237         _extension_map[extension_index++] = filter.mod;
238     }
239     *(filterptr++) = L'\0';
240 
241     _filter_count = extension_index;
242     _filter_index = 1;  // Select the 1st filter in the list
243 }
244 
createFilterMenu()245 void FileOpenDialogImplWin32::createFilterMenu()
246 {
247     std::list<Filter> filter_list;
248 
249     int extension_index = 0;
250     int filter_length = 1;
251 
252     if (dialogType == CUSTOM_TYPE) {
253         return;
254     }
255 
256     if (dialogType != EXE_TYPES) {
257         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
258         _show_preview = prefs->getBool("/dialogs/open/enable_preview", true);
259 
260         // Compose the filter string
261         Inkscape::Extension::DB::InputList extension_list;
262         Inkscape::Extension::db.get_input_list(extension_list);
263 
264         ustring all_inkscape_files_filter, all_image_files_filter, all_vectors_filter, all_bitmaps_filter;
265         Filter all_files, all_inkscape_files, all_image_files, all_vectors, all_bitmaps;
266 
267         const gchar *all_files_filter_name = _("All Files");
268         const gchar *all_inkscape_files_filter_name = _("All Inkscape Files");
269         const gchar *all_image_files_filter_name = _("All Images");
270         const gchar *all_vectors_filter_name = _("All Vectors");
271         const gchar *all_bitmaps_filter_name = _("All Bitmaps");
272 
273         // Calculate the amount of memory required
274         int filter_count = 5;       // 5 - one for each filter type
275 
276         for (Inkscape::Extension::DB::InputList::iterator current_item = extension_list.begin();
277              current_item != extension_list.end(); ++current_item)
278         {
279             Filter filter;
280 
281             Inkscape::Extension::Input *imod = *current_item;
282             if (imod->deactivated()) continue;
283 
284             // Type
285             filter.name = g_utf8_to_utf16(imod->get_filetypename(true), -1, NULL, &filter.name_length, NULL);
286 
287             // Extension
288             const gchar *file_extension_name = imod->get_extension();
289             filter.filter = g_utf8_to_utf16(file_extension_name, -1, NULL, &filter.filter_length, NULL);
290 
291             filter.mod = imod;
292             filter_list.push_back(filter);
293 
294             filter_length += filter.name_length + filter.filter_length + 3;   // Add 3 for two \0s and a *
295 
296             // Add to the "All Inkscape Files" Entry
297             if(all_inkscape_files_filter.length() > 0)
298                 all_inkscape_files_filter += ";*";
299             all_inkscape_files_filter += file_extension_name;
300             if( strncmp("image", imod->get_mimetype(), 5) == 0)
301             {
302                 // Add to the "All Image Files" Entry
303                 if(all_image_files_filter.length() > 0)
304                     all_image_files_filter += ";*";
305                 all_image_files_filter += file_extension_name;
306             }
307 
308             // I don't know of any other way to define "bitmap" formats other than by listing them
309             // if you change it here, do the same change in filedialogimpl-gtkmm
310             if (
311                 strncmp("image/png", imod->get_mimetype(), 9)==0 ||
312                 strncmp("image/jpeg", imod->get_mimetype(), 10)==0 ||
313                 strncmp("image/gif", imod->get_mimetype(), 9)==0 ||
314                 strncmp("image/x-icon", imod->get_mimetype(), 12)==0 ||
315                 strncmp("image/x-navi-animation", imod->get_mimetype(), 22)==0 ||
316                 strncmp("image/x-cmu-raster", imod->get_mimetype(), 18)==0 ||
317                 strncmp("image/x-xpixmap", imod->get_mimetype(), 15)==0 ||
318                 strncmp("image/bmp", imod->get_mimetype(), 9)==0 ||
319                 strncmp("image/vnd.wap.wbmp", imod->get_mimetype(), 18)==0 ||
320                 strncmp("image/tiff", imod->get_mimetype(), 10)==0 ||
321                 strncmp("image/x-xbitmap", imod->get_mimetype(), 15)==0 ||
322                 strncmp("image/x-tga", imod->get_mimetype(), 11)==0 ||
323                 strncmp("image/x-pcx", imod->get_mimetype(), 11)==0
324                 ) {
325                 if(all_bitmaps_filter.length() > 0)
326                     all_bitmaps_filter += ";*";
327                 all_bitmaps_filter += file_extension_name;
328             } else {
329                 if(all_vectors_filter.length() > 0)
330                     all_vectors_filter += ";*";
331                 all_vectors_filter += file_extension_name;
332             }
333 
334             filter_count++;
335         }
336 
337         _extension_map = new Inkscape::Extension::Extension*[filter_count];
338 
339         // Filter bitmap files
340         all_bitmaps.name = g_utf8_to_utf16(all_bitmaps_filter_name,
341             -1, NULL, &all_bitmaps.name_length, NULL);
342         all_bitmaps.filter = g_utf8_to_utf16(all_bitmaps_filter.data(),
343                 -1, NULL, &all_bitmaps.filter_length, NULL);
344         all_bitmaps.mod = NULL;
345         filter_list.push_front(all_bitmaps);
346 
347         // Filter vector files
348         all_vectors.name = g_utf8_to_utf16(all_vectors_filter_name,
349             -1, NULL, &all_vectors.name_length, NULL);
350         all_vectors.filter = g_utf8_to_utf16(all_vectors_filter.data(),
351                 -1, NULL, &all_vectors.filter_length, NULL);
352         all_vectors.mod = NULL;
353         filter_list.push_front(all_vectors);
354 
355         // Filter Image Files
356         all_image_files.name = g_utf8_to_utf16(all_image_files_filter_name,
357             -1, NULL, &all_image_files.name_length, NULL);
358         all_image_files.filter = g_utf8_to_utf16(all_image_files_filter.data(),
359                 -1, NULL, &all_image_files.filter_length, NULL);
360         all_image_files.mod = NULL;
361         filter_list.push_front(all_image_files);
362 
363         // Filter Inkscape Files
364         all_inkscape_files.name = g_utf8_to_utf16(all_inkscape_files_filter_name,
365             -1, NULL, &all_inkscape_files.name_length, NULL);
366         all_inkscape_files.filter = g_utf8_to_utf16(all_inkscape_files_filter.data(),
367                 -1, NULL, &all_inkscape_files.filter_length, NULL);
368         all_inkscape_files.mod = NULL;
369         filter_list.push_front(all_inkscape_files);
370 
371         // Filter All Files
372         all_files.name = g_utf8_to_utf16(all_files_filter_name,
373             -1, NULL, &all_files.name_length, NULL);
374         all_files.filter = NULL;
375         all_files.filter_length = 0;
376         all_files.mod = NULL;
377         filter_list.push_front(all_files);
378 
379         filter_length += all_files.name_length + 3 +
380                         all_inkscape_files.filter_length +
381                         all_inkscape_files.name_length + 3 +
382                         all_image_files.filter_length +
383                         all_image_files.name_length + 3 +
384                         all_vectors.filter_length +
385                         all_vectors.name_length + 3 +
386                         all_bitmaps.filter_length +
387                         all_bitmaps.name_length + 3 +
388                                                       1;
389          // Add 3 for 2*2 \0s and a *, and 1 for a trailing \0
390     } else {
391         // Executables only
392         ustring all_exe_files_filter = "*.exe;*.bat;*.com";
393         Filter all_exe_files, all_files;
394 
395         const gchar *all_files_filter_name = _("All Files");
396         const gchar *all_exe_files_filter_name = _("All Executable Files");
397 
398         // Calculate the amount of memory required
399         int filter_count = 2;       // 2 - All Files and All Executable Files
400 
401         _extension_map = new Inkscape::Extension::Extension*[filter_count];
402 
403         // Filter Executable Files
404         all_exe_files.name = g_utf8_to_utf16(all_exe_files_filter_name,
405             -1, NULL, &all_exe_files.name_length, NULL);
406         all_exe_files.filter = g_utf8_to_utf16(all_exe_files_filter.data(),
407                 -1, NULL, &all_exe_files.filter_length, NULL);
408         all_exe_files.mod = NULL;
409         filter_list.push_front(all_exe_files);
410 
411         // Filter All Files
412         all_files.name = g_utf8_to_utf16(all_files_filter_name,
413             -1, NULL, &all_files.name_length, NULL);
414         all_files.filter = NULL;
415         all_files.filter_length = 0;
416         all_files.mod = NULL;
417         filter_list.push_front(all_files);
418 
419         filter_length += all_files.name_length + 3 +
420                         all_exe_files.filter_length +
421                         all_exe_files.name_length + 3 +
422                                                       1;
423          // Add 3 for 2*2 \0s and a *, and 1 for a trailing \0
424     }
425 
426     _filter = new wchar_t[filter_length];
427     wchar_t *filterptr = _filter;
428 
429     for(std::list<Filter>::iterator filter_iterator = filter_list.begin();
430         filter_iterator != filter_list.end(); ++filter_iterator)
431     {
432         const Filter &filter = *filter_iterator;
433 
434         wcsncpy(filterptr, (wchar_t*)filter.name, filter.name_length);
435         filterptr += filter.name_length;
436         g_free(filter.name);
437 
438         *(filterptr++) = L'\0';
439         *(filterptr++) = L'*';
440 
441         if(filter.filter != NULL)
442         {
443             wcsncpy(filterptr, (wchar_t*)filter.filter, filter.filter_length);
444             filterptr += filter.filter_length;
445             g_free(filter.filter);
446         }
447 
448         *(filterptr++) = L'\0';
449 
450         // Associate this input extension with the file type name
451         _extension_map[extension_index++] = filter.mod;
452     }
453     *(filterptr++) = L'\0';
454 
455     _filter_count = extension_index;
456     _filter_index = 2;  // Select the 2nd filter in the list - 2 is NOT the 3rd
457 }
458 
GetOpenFileName_thread()459 void FileOpenDialogImplWin32::GetOpenFileName_thread()
460 {
461     OPENFILENAMEW ofn;
462 
463     g_assert(_mutex != NULL);
464 
465     WCHAR* current_directory_string = (WCHAR*)g_utf8_to_utf16(
466         _current_directory.data(), _current_directory.length(),
467         NULL, NULL, NULL);
468 
469     memset(&ofn, 0, sizeof(ofn));
470 
471     // Copy the selected file name, converting from UTF-8 to UTF-16
472     memset(_path_string, 0, sizeof(_path_string));
473     gunichar2* utf16_path_string = g_utf8_to_utf16(
474         myFilename.data(), -1, NULL, NULL, NULL);
475     wcsncpy(_path_string, (wchar_t*)utf16_path_string, _MAX_PATH);
476     g_free(utf16_path_string);
477 
478     ofn.lStructSize = sizeof(ofn);
479     ofn.hwndOwner = _ownerHwnd;
480     ofn.lpstrFile = _path_string;
481     ofn.nMaxFile = _MAX_PATH;
482     ofn.lpstrFileTitle = NULL;
483     ofn.nMaxFileTitle = 0;
484     ofn.lpstrInitialDir = current_directory_string;
485     ofn.lpstrTitle = _title;
486     ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_ENABLEHOOK | OFN_HIDEREADONLY | OFN_ENABLESIZING;
487     ofn.lpstrFilter = _filter;
488     ofn.nFilterIndex = _filter_index;
489     ofn.lpfnHook = GetOpenFileName_hookproc;
490     ofn.lCustData = (LPARAM)this;
491 
492     _result = GetOpenFileNameW(&ofn) != 0;
493 
494     g_assert(ofn.nFilterIndex >= 1 && ofn.nFilterIndex <= _filter_count);
495     _filter_index = ofn.nFilterIndex;
496     _extension = _extension_map[ofn.nFilterIndex - 1];
497 
498     // Copy the selected file name, converting from UTF-16 to UTF-8
499     myFilename = utf16_to_ustring(_path_string, _MAX_PATH);
500 
501     // Tidy up
502     g_free(current_directory_string);
503 
504     _mutex->lock();
505     _finished = true;
506     _mutex->unlock();
507 }
508 
register_preview_wnd_class()509 void FileOpenDialogImplWin32::register_preview_wnd_class()
510 {
511     HINSTANCE hInstance = GetModuleHandle(NULL);
512     const WNDCLASSA PreviewWndClass =
513     {
514         CS_HREDRAW | CS_VREDRAW,
515         preview_wnd_proc,
516         0,
517         0,
518         hInstance,
519         NULL,
520         LoadCursor(hInstance, IDC_ARROW),
521         (HBRUSH)(COLOR_BTNFACE + 1),
522         NULL,
523         PreviewWindowClassName
524     };
525 
526     RegisterClassA(&PreviewWndClass);
527 }
528 
GetOpenFileName_hookproc(HWND hdlg,UINT uiMsg,WPARAM,LPARAM lParam)529 UINT_PTR CALLBACK FileOpenDialogImplWin32::GetOpenFileName_hookproc(
530     HWND hdlg, UINT uiMsg, WPARAM, LPARAM lParam)
531 {
532     FileOpenDialogImplWin32 *pImpl = reinterpret_cast<FileOpenDialogImplWin32*>
533         (GetWindowLongPtr(hdlg, GWLP_USERDATA));
534 
535     switch(uiMsg)
536     {
537     case WM_INITDIALOG:
538         {
539             HWND hParentWnd = GetParent(hdlg);
540             HINSTANCE hInstance = GetModuleHandle(NULL);
541 
542             // Set the pointer to the object
543             OPENFILENAMEW *ofn = reinterpret_cast<OPENFILENAMEW*>(lParam);
544             SetWindowLongPtr(hdlg, GWLP_USERDATA, ofn->lCustData);
545             SetWindowLongPtr(hParentWnd, GWLP_USERDATA, ofn->lCustData);
546             pImpl = reinterpret_cast<FileOpenDialogImplWin32*>(ofn->lCustData);
547 
548             // Make the window a bit wider
549             RECT rcRect;
550             GetWindowRect(hParentWnd, &rcRect);
551 
552             // Don't show the preview when opening executable files
553             if ( pImpl->dialogType == EXE_TYPES) {
554                 MoveWindow(hParentWnd, rcRect.left, rcRect.top,
555                            rcRect.right - rcRect.left,
556                            rcRect.bottom - rcRect.top,
557                            FALSE);
558             } else {
559                 MoveWindow(hParentWnd, rcRect.left, rcRect.top,
560                            rcRect.right - rcRect.left + PREVIEW_WIDENING,
561                            rcRect.bottom - rcRect.top,
562                            FALSE);
563             }
564 
565             // Subclass the parent
566             pImpl->_base_window_proc = (WNDPROC)GetWindowLongPtr(hParentWnd, GWLP_WNDPROC);
567             SetWindowLongPtr(hParentWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(file_dialog_subclass_proc));
568 
569             if ( pImpl->dialogType != EXE_TYPES) {
570                 // Add a button to the toolbar
571                 pImpl->_toolbar_wnd = FindWindowEx(hParentWnd, NULL, "ToolbarWindow32", NULL);
572 
573                 pImpl->_show_preview_button_bitmap = LoadBitmap(
574                     hInstance, MAKEINTRESOURCE(IDC_SHOW_PREVIEW));
575                 TBADDBITMAP tbAddBitmap = {NULL, reinterpret_cast<UINT_PTR>(pImpl->_show_preview_button_bitmap)};
576                 const int iBitmapIndex = SendMessage(pImpl->_toolbar_wnd,
577                     TB_ADDBITMAP, 1, (LPARAM)&tbAddBitmap);
578 
579 
580                 TBBUTTON tbButton;
581                 memset(&tbButton, 0, sizeof(TBBUTTON));
582                 tbButton.iBitmap = iBitmapIndex;
583                 tbButton.idCommand = IDC_SHOW_PREVIEW;
584                 tbButton.fsState = (pImpl->_show_preview ? TBSTATE_CHECKED : 0)
585                     | TBSTATE_ENABLED;
586                 tbButton.fsStyle = TBSTYLE_CHECK;
587                 tbButton.iString = (INT_PTR)_("Show Preview");
588                 SendMessage(pImpl->_toolbar_wnd, TB_ADDBUTTONS, 1, (LPARAM)&tbButton);
589 
590                 // Create preview pane
591                 register_preview_wnd_class();
592             }
593 
594             pImpl->_mutex->lock();
595 
596             pImpl->_file_dialog_wnd = hParentWnd;
597 
598             if ( pImpl->dialogType != EXE_TYPES) {
599                 pImpl->_preview_wnd =
600                     CreateWindowA(PreviewWindowClassName, "",
601                         WS_CHILD | WS_VISIBLE,
602                         0, 0, 100, 100, hParentWnd, NULL, hInstance, NULL);
603                 SetWindowLongPtr(pImpl->_preview_wnd, GWLP_USERDATA, ofn->lCustData);
604             }
605 
606             pImpl->_mutex->unlock();
607 
608             pImpl->layout_dialog();
609         }
610         break;
611 
612     case WM_NOTIFY:
613         {
614 
615         OFNOTIFY *pOFNotify = reinterpret_cast<OFNOTIFY*>(lParam);
616         switch(pOFNotify->hdr.code)
617         {
618         case CDN_SELCHANGE:
619             {
620                 if(pImpl != NULL)
621                 {
622                     // Get the file name
623                     pImpl->_mutex->lock();
624 
625                     SendMessage(pOFNotify->hdr.hwndFrom, CDM_GETFILEPATH,
626                         sizeof(pImpl->_path_string) / sizeof(wchar_t),
627                         (LPARAM)pImpl->_path_string);
628 
629                     pImpl->_file_selected = true;
630 
631                     pImpl->_mutex->unlock();
632                 }
633             }
634             break;
635         }
636         }
637         break;
638 
639     case WM_CLOSE:
640         pImpl->_mutex->lock();
641         pImpl->_preview_file_size = 0;
642 
643         pImpl->_file_dialog_wnd = NULL;
644         DestroyWindow(pImpl->_preview_wnd);
645         pImpl->_preview_wnd = NULL;
646         DeleteObject(pImpl->_show_preview_button_bitmap);
647         pImpl->_show_preview_button_bitmap = NULL;
648         pImpl->_mutex->unlock();
649 
650         break;
651     }
652 
653     // Use default dialog behaviour
654     return 0;
655 }
656 
file_dialog_subclass_proc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)657 LRESULT CALLBACK FileOpenDialogImplWin32::file_dialog_subclass_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
658 {
659     FileOpenDialogImplWin32 *pImpl = reinterpret_cast<FileOpenDialogImplWin32*>
660         (GetWindowLongPtr(hwnd, GWLP_USERDATA));
661 
662     LRESULT lResult = CallWindowProc(pImpl->_base_window_proc, hwnd, uMsg, wParam, lParam);
663 
664     switch(uMsg)
665     {
666     case WM_SHOWWINDOW:
667         if(wParam != 0)
668             pImpl->layout_dialog();
669         break;
670 
671     case WM_SIZE:
672         pImpl->layout_dialog();
673         break;
674 
675     case WM_COMMAND:
676         if(wParam == IDC_SHOW_PREVIEW)
677         {
678             const bool enable = SendMessage(pImpl->_toolbar_wnd,
679                 TB_ISBUTTONCHECKED, IDC_SHOW_PREVIEW, 0) != 0;
680             pImpl->enable_preview(enable);
681         }
682         break;
683     }
684 
685     return lResult;
686 }
687 
preview_wnd_proc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)688 LRESULT CALLBACK FileOpenDialogImplWin32::preview_wnd_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
689 {
690     const int CaptionPadding = 4;
691     const int IconSize = 32;
692 
693     FileOpenDialogImplWin32 *pImpl = reinterpret_cast<FileOpenDialogImplWin32*>
694         (GetWindowLongPtr(hwnd, GWLP_USERDATA));
695 
696     LRESULT lResult = 0;
697 
698     switch(uMsg)
699     {
700     case WM_ERASEBKGND:
701         // Do nothing to erase the background
702         //  - otherwise there'll be flicker
703         lResult = 1;
704         break;
705 
706     case WM_PAINT:
707         {
708             // Get the client rect
709             RECT rcClient;
710             GetClientRect(hwnd, &rcClient);
711 
712             // Prepare to paint
713             PAINTSTRUCT paint_struct;
714             HDC dc = BeginPaint(hwnd, &paint_struct);
715 
716             HFONT hCaptionFont = reinterpret_cast<HFONT>(SendMessage(GetParent(hwnd),
717                     WM_GETFONT, 0, 0));
718             HFONT hOldFont = static_cast<HFONT>(SelectObject(dc, hCaptionFont));
719             SetBkMode(dc, TRANSPARENT);
720 
721             pImpl->_mutex->lock();
722 
723             if(pImpl->_path_string[0] == 0)
724             {
725                 WCHAR* noFileText=(WCHAR*)g_utf8_to_utf16(_("No file selected"),
726                     -1, NULL, NULL, NULL);
727                 FillRect(dc, &rcClient, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
728                 DrawTextW(dc,  noFileText, -1, &rcClient,
729                     DT_CENTER | DT_VCENTER | DT_NOPREFIX);
730                 g_free(noFileText);
731             }
732             else if(pImpl->_preview_bitmap != NULL)
733             {
734                 BITMAP bitmap;
735                 GetObject(pImpl->_preview_bitmap, sizeof(bitmap), &bitmap);
736                 const int destX = (rcClient.right - bitmap.bmWidth) / 2;
737 
738                 // Render the image
739                 HDC hSrcDC = CreateCompatibleDC(dc);
740                 HBITMAP hOldBitmap = (HBITMAP)SelectObject(hSrcDC, pImpl->_preview_bitmap);
741 
742                 BitBlt(dc, destX, 0, bitmap.bmWidth, bitmap.bmHeight,
743                     hSrcDC, 0, 0, SRCCOPY);
744 
745                 SelectObject(hSrcDC, hOldBitmap);
746                 DeleteDC(hSrcDC);
747 
748                 // Fill in the background area
749                 HRGN hEraseRgn = CreateRectRgn(rcClient.left, rcClient.top,
750                     rcClient.right, rcClient.bottom);
751                 HRGN hImageRgn = CreateRectRgn(destX, 0,
752                     destX + bitmap.bmWidth, bitmap.bmHeight);
753                 CombineRgn(hEraseRgn, hEraseRgn, hImageRgn, RGN_DIFF);
754 
755                 FillRgn(dc, hEraseRgn, GetSysColorBrush(COLOR_3DFACE));
756 
757                 DeleteObject(hImageRgn);
758                 DeleteObject(hEraseRgn);
759 
760                 // Draw the caption on
761                 RECT rcCaptionRect = {rcClient.left,
762                     rcClient.top + bitmap.bmHeight + CaptionPadding,
763                     rcClient.right, rcClient.bottom};
764 
765                 WCHAR szCaption[_MAX_FNAME + 32];
766                 const int iLength = pImpl->format_caption(
767                     szCaption, sizeof(szCaption) / sizeof(WCHAR));
768 
769                 DrawTextW(dc, szCaption, iLength, &rcCaptionRect,
770                     DT_CENTER | DT_TOP | DT_NOPREFIX | DT_PATH_ELLIPSIS);
771             }
772             else if(pImpl->_preview_file_icon != NULL)
773             {
774                 FillRect(dc, &rcClient, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
775 
776                 // Draw the files icon
777                 const int destX = (rcClient.right - IconSize) / 2;
778                 DrawIconEx(dc, destX, 0, pImpl->_preview_file_icon,
779                     IconSize, IconSize, 0, NULL,
780                     DI_NORMAL | DI_COMPAT);
781 
782                 // Draw the caption on
783                 RECT rcCaptionRect = {rcClient.left,
784                     rcClient.top + IconSize + CaptionPadding,
785                     rcClient.right, rcClient.bottom};
786 
787                 WCHAR szFileName[_MAX_FNAME], szCaption[_MAX_FNAME + 32];
788                 _wsplitpath(pImpl->_path_string, NULL, NULL, szFileName, NULL);
789 
790                 const int iLength = snwprintf(szCaption,
791                     sizeof(szCaption), L"%ls\n%d kB",
792                     szFileName, pImpl->_preview_file_size);
793 
794                 DrawTextW(dc, szCaption, iLength, &rcCaptionRect,
795                     DT_CENTER | DT_TOP | DT_NOPREFIX | DT_PATH_ELLIPSIS);
796             }
797             else
798             {
799                 // Can't show anything!
800                 FillRect(dc, &rcClient, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
801             }
802 
803             pImpl->_mutex->unlock();
804 
805             // Finish painting
806             SelectObject(dc, hOldFont);
807             EndPaint(hwnd, &paint_struct);
808         }
809 
810         break;
811 
812     case WM_DESTROY:
813         pImpl->free_preview();
814         break;
815 
816     default:
817         lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
818         break;
819     }
820 
821     return lResult;
822 }
823 
enable_preview(bool enable)824 void FileOpenDialogImplWin32::enable_preview(bool enable)
825 {
826     if (_show_preview != enable) {
827         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
828         prefs->setBool("/dialogs/open/enable_preview", enable);
829     }
830     _show_preview = enable;
831 
832     // Relayout the dialog
833     ShowWindow(_preview_wnd, enable ? SW_SHOW : SW_HIDE);
834     layout_dialog();
835 
836     // Load or unload the preview
837     if(enable)
838     {
839         _mutex->lock();
840         _file_selected = true;
841         _mutex->unlock();
842     }
843     else free_preview();
844 }
845 
layout_dialog()846 void FileOpenDialogImplWin32::layout_dialog()
847 {
848     union RECTPOINTS
849     {
850         RECT r;
851         POINT p[2];
852     };
853 
854     const float MaxExtentScale = 2.0f / 3.0f;
855 
856     RECT rcClient;
857     GetClientRect(_file_dialog_wnd, &rcClient);
858 
859     // Re-layout the dialog
860     HWND hFileListWnd = GetDlgItem(_file_dialog_wnd, lst2);
861     HWND hFolderComboWnd = GetDlgItem(_file_dialog_wnd, cmb2);
862 
863 
864     RECT rcFolderComboRect;
865     RECTPOINTS rcFileList;
866     GetWindowRect(hFileListWnd, &rcFileList.r);
867     GetWindowRect(hFolderComboWnd, &rcFolderComboRect);
868     const int iPadding = rcFileList.r.top - rcFolderComboRect.bottom;
869     MapWindowPoints(NULL, _file_dialog_wnd, rcFileList.p, 2);
870 
871     RECT rcPreview;
872     RECT rcBody = {rcFileList.r.left, rcFileList.r.top,
873         rcClient.right - iPadding, rcFileList.r.bottom};
874     rcFileList.r.right = rcBody.right;
875 
876     if(_show_preview && dialogType != EXE_TYPES)
877     {
878         rcPreview.top = rcBody.top;
879         rcPreview.left = rcClient.right - (rcBody.bottom - rcBody.top);
880         const int iMaxExtent = (int)(MaxExtentScale * (float)(rcBody.left + rcBody.right)) + iPadding / 2;
881         if(rcPreview.left < iMaxExtent) rcPreview.left = iMaxExtent;
882         rcPreview.bottom = rcBody.bottom;
883         rcPreview.right = rcBody.right;
884 
885         // Re-layout the preview box
886         _mutex->lock();
887 
888             _preview_width = rcPreview.right - rcPreview.left;
889             _preview_height = rcPreview.bottom - rcPreview.top;
890 
891         _mutex->unlock();
892 
893         render_preview();
894 
895         MoveWindow(_preview_wnd, rcPreview.left, rcPreview.top,
896             _preview_width, _preview_height, TRUE);
897 
898         rcFileList.r.right = rcPreview.left - iPadding;
899     }
900 
901     // Re-layout the file list box
902     MoveWindow(hFileListWnd, rcFileList.r.left, rcFileList.r.top,
903         rcFileList.r.right - rcFileList.r.left,
904         rcFileList.r.bottom - rcFileList.r.top, TRUE);
905 
906     // Re-layout the toolbar
907     RECTPOINTS rcToolBar;
908     GetWindowRect(_toolbar_wnd, &rcToolBar.r);
909     MapWindowPoints(NULL, _file_dialog_wnd, rcToolBar.p, 2);
910     MoveWindow(_toolbar_wnd, rcToolBar.r.left, rcToolBar.r.top,
911         rcToolBar.r.right - rcToolBar.r.left, rcToolBar.r.bottom - rcToolBar.r.top, TRUE);
912 }
913 
file_selected()914 void FileOpenDialogImplWin32::file_selected()
915 {
916     // Destroy any previous previews
917     free_preview();
918 
919 
920     // Determine if the file exists
921     DWORD attributes = GetFileAttributesW(_path_string);
922     if(attributes == 0xFFFFFFFF ||
923         attributes == FILE_ATTRIBUTE_DIRECTORY)
924     {
925         InvalidateRect(_preview_wnd, NULL, FALSE);
926         return;
927     }
928 
929     // Check the file exists and get the file size
930     HANDLE file_handle = CreateFileW(_path_string, GENERIC_READ,
931         FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
932     if(file_handle == INVALID_HANDLE_VALUE) return;
933     const DWORD file_size = GetFileSize(file_handle, NULL);
934     if (file_size == INVALID_FILE_SIZE) return;
935     _preview_file_size = file_size / 1024;
936     CloseHandle(file_handle);
937 
938     if(_show_preview) load_preview();
939 }
940 
load_preview()941 void FileOpenDialogImplWin32::load_preview()
942 {
943     // Destroy any previous previews
944     free_preview();
945 
946     // Try to get the file icon
947     SHFILEINFOW fileInfo;
948     if(SUCCEEDED(SHGetFileInfoW(_path_string, 0, &fileInfo,
949         sizeof(fileInfo), SHGFI_ICON | SHGFI_LARGEICON)))
950         _preview_file_icon = fileInfo.hIcon;
951 
952     // Will this file be too big?
953     if(_preview_file_size > MaxPreviewFileSize)
954     {
955         InvalidateRect(_preview_wnd, NULL, FALSE);
956         return;
957     }
958 
959     // Prepare to render a preview
960     const Glib::ustring svg = ".svg";
961     const Glib::ustring svgz = ".svgz";
962     const Glib::ustring emf = ".emf";
963     const Glib::ustring wmf = ".wmf";
964     const Glib::ustring path = utf16_to_ustring(_path_string);
965 
966     bool success = false;
967 
968     _preview_document_width = _preview_document_height = 0;
969 
970     if ((dialogType == SVG_TYPES || dialogType == IMPORT_TYPES) &&
971             (hasSuffix(path, svg) || hasSuffix(path, svgz)))
972         success = set_svg_preview();
973     else if (hasSuffix(path, emf) || hasSuffix(path, wmf))
974         success = set_emf_preview();
975     else if (isValidImageFile(path))
976         success = set_image_preview();
977     else {
978         // Show no preview
979     }
980 
981     if(success) render_preview();
982 
983     InvalidateRect(_preview_wnd, NULL, FALSE);
984 }
985 
free_preview()986 void FileOpenDialogImplWin32::free_preview()
987 {
988     _mutex->lock();
989     if(_preview_bitmap != NULL)
990         DeleteObject(_preview_bitmap);
991     _preview_bitmap = NULL;
992 
993     if(_preview_file_icon != NULL)
994         DestroyIcon(_preview_file_icon);
995     _preview_file_icon = NULL;
996 
997     _preview_bitmap_image.reset();
998     _preview_emf_image = false;
999     _mutex->unlock();
1000 }
1001 
set_svg_preview()1002 bool FileOpenDialogImplWin32::set_svg_preview()
1003 {
1004     const int PreviewSize = 512;
1005 
1006     gchar *utf8string = g_utf16_to_utf8((const gunichar2*)_path_string,
1007         _MAX_PATH, NULL, NULL, NULL);
1008     std::unique_ptr<SPDocument> svgDoc(SPDocument::createNewDoc(utf8string, 0));
1009     g_free(utf8string);
1010 
1011     // Check the document loaded properly
1012     if (svgDoc == NULL) {
1013         return false;
1014     }
1015     if (svgDoc->getRoot() == NULL)
1016     {
1017         return false;
1018     }
1019 
1020     // Get the size of the document
1021     Inkscape::Util::Quantity svgWidth = svgDoc->getWidth();
1022     Inkscape::Util::Quantity svgHeight = svgDoc->getHeight();
1023     const double svgWidth_px = svgWidth.value("px");
1024     const double svgHeight_px = svgHeight.value("px");
1025 
1026     // Find the minimum scale to fit the image inside the preview area
1027     const double scaleFactorX = PreviewSize / svgWidth_px;
1028     const double scaleFactorY = PreviewSize / svgHeight_px;
1029     const double scaleFactor = (scaleFactorX > scaleFactorY) ? scaleFactorY : scaleFactorX;
1030 
1031     // Now get the resized values
1032     const int scaledSvgWidth  = round(scaleFactor * svgWidth_px);
1033     const int scaledSvgHeight = round(scaleFactor * svgHeight_px);
1034 
1035     const double dpi = 96*scaleFactor;
1036     Inkscape::Pixbuf *pixbuf =
1037         sp_generate_internal_bitmap(svgDoc.get(), NULL, 0, 0, svgWidth_px, svgHeight_px, scaledSvgWidth,
1038                                     scaledSvgHeight, dpi, dpi, (guint32)0xffffff00, NULL);
1039 
1040     // Tidy up
1041     if (pixbuf == NULL) {
1042         return false;
1043     }
1044 
1045     // Create the GDK pixbuf
1046     _mutex->lock();
1047     _preview_bitmap_image = Glib::wrap(pixbuf->getPixbufRaw(), /* ref */ true);
1048     delete pixbuf;
1049     _preview_document_width = svgWidth_px;
1050     _preview_document_height = svgHeight_px;
1051     _preview_image_width = scaledSvgWidth;
1052     _preview_image_height = scaledSvgHeight;
1053     _mutex->unlock();
1054 
1055     return true;
1056 }
1057 
destroy_svg_rendering(const guint8 * buffer)1058 void FileOpenDialogImplWin32::destroy_svg_rendering(const guint8 *buffer)
1059 {
1060     g_assert(buffer != NULL);
1061     g_free((void*)buffer);
1062 }
1063 
set_image_preview()1064 bool FileOpenDialogImplWin32::set_image_preview()
1065 {
1066     const Glib::ustring path = utf16_to_ustring(_path_string, _MAX_PATH);
1067 
1068     bool successful = false;
1069 
1070     _mutex->lock();
1071 
1072     try {
1073         _preview_bitmap_image = Gdk::Pixbuf::create_from_file(path);
1074         if (_preview_bitmap_image) {
1075             _preview_image_width = _preview_bitmap_image->get_width();
1076             _preview_document_width = _preview_image_width;
1077             _preview_image_height = _preview_bitmap_image->get_height();
1078             _preview_document_height = _preview_image_height;
1079             successful = true;
1080         }
1081     }
1082     catch (const Gdk::PixbufError&) {}
1083     catch (const Glib::FileError&) {}
1084 
1085     _mutex->unlock();
1086 
1087     return successful;
1088 }
1089 
1090 // Aldus Placeable Header ===================================================
1091 // Since we are a 32bit app, we have to be sure this structure compiles to
1092 // be identical to a 16 bit app's version. To do this, we use the #pragma
1093 // to adjust packing, we use a WORD for the hmf handle, and a SMALL_RECT
1094 // for the bbox rectangle.
1095 #pragma pack( push )
1096 #pragma pack( 2 )
1097 struct APMHEADER
1098 {
1099     DWORD       dwKey;
1100     WORD        hmf;
1101     SMALL_RECT  bbox;
1102     WORD        wInch;
1103     DWORD       dwReserved;
1104     WORD        wCheckSum;
1105 };
1106 using PAPMHEADER = APMHEADER *;
1107 #pragma pack( pop )
1108 
1109 
1110 static HENHMETAFILE
MyGetEnhMetaFileW(const WCHAR * filename)1111 MyGetEnhMetaFileW( const WCHAR *filename )
1112 {
1113     // Try open as Enhanced Metafile
1114     HENHMETAFILE hemf = GetEnhMetaFileW(filename);
1115 
1116     if (!hemf) {
1117         // Try open as Windows Metafile
1118         HMETAFILE hmf = GetMetaFileW(filename);
1119 
1120         METAFILEPICT mp;
1121         HDC hDC;
1122 
1123         if (!hmf) {
1124             WCHAR szTemp[MAX_PATH];
1125 
1126             DWORD dw = GetShortPathNameW( filename, szTemp, MAX_PATH );
1127             if (dw) {
1128                 hmf = GetMetaFileW( szTemp );
1129             }
1130         }
1131 
1132         if (hmf) {
1133             // Convert Windows Metafile to Enhanced Metafile
1134             DWORD nSize = GetMetaFileBitsEx( hmf, 0, NULL );
1135 
1136             if (nSize) {
1137                 BYTE *lpvData = new BYTE[nSize];
1138                 if (lpvData) {
1139                     DWORD dw = GetMetaFileBitsEx( hmf, nSize, lpvData );
1140                     if (dw) {
1141                         // Fill out a METAFILEPICT structure
1142                         mp.mm = MM_ANISOTROPIC;
1143                         mp.xExt = 1000;
1144                         mp.yExt = 1000;
1145                         mp.hMF = NULL;
1146                         // Get a reference DC
1147                         hDC = GetDC( NULL );
1148                         // Make an enhanced metafile from the windows metafile
1149                         hemf = SetWinMetaFileBits( nSize, lpvData, hDC, &mp );
1150                         // Clean up
1151                         ReleaseDC( NULL, hDC );
1152                         DeleteMetaFile( hmf );
1153                     }
1154                     delete[] lpvData;
1155                 }
1156                 else {
1157                     DeleteMetaFile( hmf );
1158                 }
1159             }
1160             else {
1161                 DeleteMetaFile( hmf );
1162             }
1163         }
1164         else {
1165             // Try open as Aldus Placeable Metafile
1166             HANDLE hFile;
1167             hFile = CreateFileW( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
1168 
1169             if (hFile != INVALID_HANDLE_VALUE) {
1170                 DWORD nSize = GetFileSize( hFile, NULL );
1171                 if (nSize) {
1172                     BYTE *lpvData = new BYTE[nSize];
1173                     if (lpvData) {
1174                         DWORD dw = ReadFile( hFile, lpvData, nSize, &nSize, NULL );
1175                         if (dw) {
1176                             if ( ((PAPMHEADER)lpvData)->dwKey == 0x9ac6cdd7l ) {
1177                                 // Fill out a METAFILEPICT structure
1178                                 mp.mm = MM_ANISOTROPIC;
1179                                 mp.xExt = ((PAPMHEADER)lpvData)->bbox.Right - ((PAPMHEADER)lpvData)->bbox.Left;
1180                                 mp.xExt = ( mp.xExt * 2540l ) / (DWORD)(((PAPMHEADER)lpvData)->wInch);
1181                                 mp.yExt = ((PAPMHEADER)lpvData)->bbox.Bottom - ((PAPMHEADER)lpvData)->bbox.Top;
1182                                 mp.yExt = ( mp.yExt * 2540l ) / (DWORD)(((PAPMHEADER)lpvData)->wInch);
1183                                 mp.hMF = NULL;
1184                                 // Get a reference DC
1185                                 hDC = GetDC( NULL );
1186                                 // Create an enhanced metafile from the bits
1187                                 hemf = SetWinMetaFileBits( nSize, lpvData+sizeof(APMHEADER), hDC, &mp );
1188                                 // Clean up
1189                                 ReleaseDC( NULL, hDC );
1190                             }
1191                         }
1192                         delete[] lpvData;
1193                     }
1194                 }
1195                 CloseHandle( hFile );
1196             }
1197         }
1198     }
1199 
1200     return hemf;
1201 }
1202 
1203 
set_emf_preview()1204 bool FileOpenDialogImplWin32::set_emf_preview()
1205 {
1206     _mutex->lock();
1207 
1208     BOOL ok = FALSE;
1209 
1210     DWORD w = 0;
1211     DWORD h = 0;
1212 
1213     HENHMETAFILE hemf = MyGetEnhMetaFileW( _path_string );
1214 
1215     if (hemf)
1216     {
1217         ENHMETAHEADER emh;
1218         ZeroMemory(&emh, sizeof(emh));
1219         ok = GetEnhMetaFileHeader(hemf, sizeof(emh), &emh) != 0;
1220 
1221         w = (emh.rclFrame.right - emh.rclFrame.left);
1222         h = (emh.rclFrame.bottom - emh.rclFrame.top);
1223 
1224         DeleteEnhMetaFile(hemf);
1225     }
1226 
1227     if (ok)
1228     {
1229         const int PreviewSize = 512;
1230 
1231         // Get the size of the document
1232         const double emfWidth = w;
1233         const double emfHeight = h;
1234 
1235         _preview_document_width = emfWidth / 2540 * 96; // width is in units of 0.01 mm
1236         _preview_document_height = emfHeight / 2540 * 96; // height is in units of 0.01 mm
1237         _preview_image_width = emfWidth;
1238         _preview_image_height = emfHeight;
1239 
1240         _preview_emf_image = true;
1241     }
1242 
1243     _mutex->unlock();
1244 
1245     return ok;
1246 }
1247 
render_preview()1248 void FileOpenDialogImplWin32::render_preview()
1249 {
1250     double x, y;
1251     const double blurRadius = 8;
1252     const double halfBlurRadius = blurRadius / 2;
1253     const int shaddowOffsetX = 0;
1254     const int shaddowOffsetY = 2;
1255     const int pagePadding = 5;
1256     const double shaddowAlpha = 0.75;
1257 
1258     // Is the preview showing?
1259     if(!_show_preview)
1260         return;
1261 
1262     // Do we have anything to render?
1263     _mutex->lock();
1264 
1265     if(!_preview_bitmap_image && !_preview_emf_image)
1266     {
1267         _mutex->unlock();
1268         return;
1269     }
1270 
1271     // Tidy up any previous bitmap renderings
1272     if(_preview_bitmap != NULL)
1273         DeleteObject(_preview_bitmap);
1274     _preview_bitmap = NULL;
1275 
1276     // Calculate the size of the caption
1277     int captionHeight = 0;
1278 
1279     if(_preview_wnd != NULL)
1280     {
1281         RECT rcCaptionRect;
1282         WCHAR szCaption[_MAX_FNAME + 32];
1283         const int iLength = format_caption(szCaption,
1284             sizeof(szCaption) / sizeof(WCHAR));
1285 
1286         HDC dc = GetDC(_preview_wnd);
1287         DrawTextW(dc, szCaption, iLength, &rcCaptionRect,
1288             DT_CENTER | DT_TOP | DT_NOPREFIX | DT_PATH_ELLIPSIS | DT_CALCRECT);
1289         ReleaseDC(_preview_wnd, dc);
1290 
1291         captionHeight = rcCaptionRect.bottom - rcCaptionRect.top;
1292     }
1293 
1294     // Find the minimum scale to fit the image inside the preview area
1295     const double scaleFactorX = ((double)_preview_width - pagePadding * 2 - blurRadius)  / _preview_image_width;
1296     const double scaleFactorY = ((double)_preview_height - pagePadding * 2 - shaddowOffsetY - halfBlurRadius - captionHeight)  / _preview_image_height;
1297     const double scaleFactor = (scaleFactorX > scaleFactorY) ? scaleFactorY : scaleFactorX;
1298 
1299     // Now get the resized values
1300     const double scaledSvgWidth  = scaleFactor * _preview_image_width;
1301     const double scaledSvgHeight = scaleFactor * _preview_image_height;
1302 
1303     const int svgX = pagePadding + halfBlurRadius;
1304     const int svgY = pagePadding;
1305 
1306     const int frameX = svgX - pagePadding;
1307     const int frameY = svgY - pagePadding;
1308     const int frameWidth = scaledSvgWidth + pagePadding * 2;
1309     const int frameHeight = scaledSvgHeight + pagePadding * 2;
1310 
1311     const int totalWidth = (int)ceil(frameWidth + blurRadius);
1312     const int totalHeight = (int)ceil(frameHeight + blurRadius);
1313 
1314     // Prepare the drawing surface
1315     HDC hDC = GetDC(_preview_wnd);
1316     HDC hMemDC = CreateCompatibleDC(hDC);
1317     _preview_bitmap = CreateCompatibleBitmap(hDC, totalWidth, totalHeight);
1318     HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, _preview_bitmap);
1319     Cairo::RefPtr<Win32Surface> surface = Win32Surface::create(hMemDC);
1320     Cairo::RefPtr<Context> context = Context::create(surface);
1321 
1322     // Paint the background to match the dialog colour
1323     const COLORREF background = GetSysColor(COLOR_3DFACE);
1324     context->set_source_rgb(
1325         GetRValue(background) / 255.0,
1326         GetGValue(background) / 255.0,
1327         GetBValue(background) / 255.0);
1328     context->paint();
1329 
1330     //----- Draw the drop shadow -----//
1331 
1332     // Left Edge
1333     x = frameX + shaddowOffsetX - halfBlurRadius;
1334     Cairo::RefPtr<LinearGradient> leftEdgeFade = LinearGradient::create(
1335         x, 0.0, x + blurRadius, 0.0);
1336     leftEdgeFade->add_color_stop_rgba (0, 0, 0, 0, 0);
1337     leftEdgeFade->add_color_stop_rgba (1, 0, 0, 0, shaddowAlpha);
1338     context->set_source(leftEdgeFade);
1339     context->rectangle (x, frameY + shaddowOffsetY + halfBlurRadius,
1340         blurRadius, frameHeight - blurRadius);
1341     context->fill();
1342 
1343     // Right Edge
1344     x = frameX + frameWidth + shaddowOffsetX - halfBlurRadius;
1345     Cairo::RefPtr<LinearGradient> rightEdgeFade = LinearGradient::create(
1346         x, 0.0,    x + blurRadius, 0.0);
1347     rightEdgeFade->add_color_stop_rgba (0, 0, 0, 0, shaddowAlpha);
1348     rightEdgeFade->add_color_stop_rgba (1, 0, 0, 0, 0);
1349     context->set_source(rightEdgeFade);
1350     context->rectangle (frameX + frameWidth + shaddowOffsetX - halfBlurRadius,
1351         frameY + shaddowOffsetY + halfBlurRadius,
1352         blurRadius, frameHeight - blurRadius);
1353     context->fill();
1354 
1355     // Top Edge
1356     y = frameY + shaddowOffsetY - halfBlurRadius;
1357     Cairo::RefPtr<LinearGradient> topEdgeFade = LinearGradient::create(
1358         0.0, y, 0.0, y + blurRadius);
1359     topEdgeFade->add_color_stop_rgba (0, 0, 0, 0, 0);
1360     topEdgeFade->add_color_stop_rgba (1, 0, 0, 0, shaddowAlpha);
1361     context->set_source(topEdgeFade);
1362     context->rectangle (frameX + shaddowOffsetX + halfBlurRadius, y,
1363         frameWidth - blurRadius, blurRadius);
1364     context->fill();
1365 
1366     // Bottom Edge
1367     y = frameY + frameHeight + shaddowOffsetY - halfBlurRadius;
1368     Cairo::RefPtr<LinearGradient> bottomEdgeFade = LinearGradient::create(
1369         0.0, y,    0.0, y + blurRadius);
1370     bottomEdgeFade->add_color_stop_rgba (0, 0, 0, 0, shaddowAlpha);
1371     bottomEdgeFade->add_color_stop_rgba (1, 0, 0, 0, 0);
1372     context->set_source(bottomEdgeFade);
1373     context->rectangle (frameX + shaddowOffsetX + halfBlurRadius, y,
1374         frameWidth - blurRadius, blurRadius);
1375     context->fill();
1376 
1377     // Top Left Corner
1378     x = frameX + shaddowOffsetX - halfBlurRadius;
1379     y = frameY + shaddowOffsetY - halfBlurRadius;
1380     Cairo::RefPtr<RadialGradient> topLeftCornerFade = RadialGradient::create(
1381         x + blurRadius, y + blurRadius, 0, x + blurRadius, y + blurRadius, blurRadius);
1382     topLeftCornerFade->add_color_stop_rgba (0, 0, 0, 0, shaddowAlpha);
1383     topLeftCornerFade->add_color_stop_rgba (1, 0, 0, 0, 0);
1384     context->set_source(topLeftCornerFade);
1385     context->rectangle (x, y, blurRadius, blurRadius);
1386     context->fill();
1387 
1388     // Top Right Corner
1389     x = frameX + frameWidth + shaddowOffsetX - halfBlurRadius;
1390     y = frameY + shaddowOffsetY - halfBlurRadius;
1391     Cairo::RefPtr<RadialGradient> topRightCornerFade = RadialGradient::create(
1392         x, y + blurRadius, 0, x, y + blurRadius, blurRadius);
1393     topRightCornerFade->add_color_stop_rgba (0, 0, 0, 0, shaddowAlpha);
1394     topRightCornerFade->add_color_stop_rgba (1, 0, 0, 0, 0);
1395     context->set_source(topRightCornerFade);
1396     context->rectangle (x, y, blurRadius, blurRadius);
1397     context->fill();
1398 
1399     // Bottom Left Corner
1400     x = frameX + shaddowOffsetX - halfBlurRadius;
1401     y = frameY + frameHeight + shaddowOffsetY - halfBlurRadius;
1402     Cairo::RefPtr<RadialGradient> bottomLeftCornerFade = RadialGradient::create(
1403         x + blurRadius, y, 0, x + blurRadius, y, blurRadius);
1404     bottomLeftCornerFade->add_color_stop_rgba (0, 0, 0, 0, shaddowAlpha);
1405     bottomLeftCornerFade->add_color_stop_rgba (1, 0, 0, 0, 0);
1406     context->set_source(bottomLeftCornerFade);
1407     context->rectangle (x, y, blurRadius, blurRadius);
1408     context->fill();
1409 
1410     // Bottom Right Corner
1411     x = frameX + frameWidth + shaddowOffsetX - halfBlurRadius;
1412     y = frameY + frameHeight + shaddowOffsetY - halfBlurRadius;
1413     Cairo::RefPtr<RadialGradient> bottomRightCornerFade = RadialGradient::create(
1414         x, y, 0, x, y, blurRadius);
1415     bottomRightCornerFade->add_color_stop_rgba (0, 0, 0, 0, shaddowAlpha);
1416     bottomRightCornerFade->add_color_stop_rgba (1, 0, 0, 0, 0);
1417     context->set_source(bottomRightCornerFade);
1418     context->rectangle (frameX + frameWidth + shaddowOffsetX - halfBlurRadius,
1419         frameY + frameHeight + shaddowOffsetY - halfBlurRadius,
1420         blurRadius, blurRadius);
1421     context->fill();
1422 
1423     // Draw the frame
1424     context->set_line_width(1);
1425     context->rectangle (frameX, frameY,    frameWidth, frameHeight);
1426 
1427     context->set_source_rgb(1.0, 1.0, 1.0);
1428     context->fill_preserve();
1429     context->set_source_rgb(0.25, 0.25, 0.25);
1430     context->stroke_preserve();
1431 
1432     // Draw the image
1433     if(_preview_bitmap_image)    // Is the image a pixbuf?
1434     {
1435         // Set the transformation
1436         const Cairo::Matrix matrix(
1437             scaleFactor, 0,
1438             0, scaleFactor,
1439             svgX, svgY);
1440         context->set_matrix (matrix);
1441 
1442         // Render the image
1443         set_source_pixbuf (context, _preview_bitmap_image, 0, 0);
1444         context->paint();
1445 
1446         // Reset the transformation
1447         context->set_identity_matrix();
1448     }
1449 
1450     // Draw the inner frame
1451     context->set_source_rgb(0.75, 0.75, 0.75);
1452     context->rectangle (svgX, svgY,    scaledSvgWidth, scaledSvgHeight);
1453     context->stroke();
1454 
1455     _mutex->unlock();
1456 
1457     // Finish drawing
1458     surface->finish();
1459 
1460     if (_preview_emf_image) {
1461         HENHMETAFILE hemf = MyGetEnhMetaFileW(_path_string);
1462         if (hemf) {
1463             RECT rc;
1464             rc.top = svgY+2;
1465             rc.left = svgX+2;
1466             rc.bottom = scaledSvgHeight-2;
1467             rc.right = scaledSvgWidth-2;
1468             PlayEnhMetaFile(hMemDC, hemf, &rc);
1469             DeleteEnhMetaFile(hemf);
1470         }
1471     }
1472 
1473     SelectObject(hMemDC, hOldBitmap) ;
1474     DeleteDC(hMemDC);
1475 
1476     // Refresh the preview pane
1477     InvalidateRect(_preview_wnd, NULL, FALSE);
1478 }
1479 
format_caption(wchar_t * caption,int caption_size)1480 int FileOpenDialogImplWin32::format_caption(wchar_t *caption, int caption_size)
1481 {
1482     wchar_t szFileName[_MAX_FNAME];
1483     _wsplitpath(_path_string, NULL, NULL, szFileName, NULL);
1484 
1485     return snwprintf(caption, caption_size,
1486         L"%ls\n%d\u2009kB\n%d\u2009px \xD7 %d\u2009px", szFileName, _preview_file_size,
1487         (int)_preview_document_width, (int)_preview_document_height);
1488 }
1489 
1490 /**
1491  * Show this dialog modally.  Return true if user hits [OK]
1492  */
1493 bool
show()1494 FileOpenDialogImplWin32::show()
1495 {
1496     // We can only run one worker thread at a time
1497     if(_mutex != NULL) return false;
1498 
1499 #if !GLIB_CHECK_VERSION(2,32,0)
1500     if(!Glib::thread_supported())
1501         Glib::thread_init();
1502 #endif
1503 
1504     _result = false;
1505     _finished = false;
1506     _file_selected = false;
1507     _main_loop = g_main_loop_new(g_main_context_default(), FALSE);
1508 
1509 #if GLIB_CHECK_VERSION(2,32,0)
1510     _mutex = new Glib::Threads::Mutex();
1511     if(Glib::Threads::Thread::create(sigc::mem_fun(*this, &FileOpenDialogImplWin32::GetOpenFileName_thread)))
1512 #else
1513     _mutex = new Glib::Mutex();
1514     if(Glib::Thread::create(sigc::mem_fun(*this, &FileOpenDialogImplWin32::GetOpenFileName_thread), true))
1515 #endif
1516     {
1517         while(1)
1518         {
1519             g_main_context_iteration(g_main_context_default(), FALSE);
1520 
1521             if(_mutex->trylock())
1522             {
1523                 // Read mutexed data
1524                 const bool finished = _finished;
1525                 const bool is_file_selected = _file_selected;
1526                 _file_selected = false;
1527                 _mutex->unlock();
1528 
1529                 if(finished) break;
1530                 if(is_file_selected) file_selected();
1531             }
1532 
1533             Sleep(10);
1534         }
1535     }
1536 
1537     // Tidy up
1538     delete _mutex;
1539     _mutex = NULL;
1540 
1541     return _result;
1542 }
1543 
1544 /**
1545  * To Get Multiple filenames selected at-once.
1546  */
getFilenames()1547 std::vector<Glib::ustring>FileOpenDialogImplWin32::getFilenames()
1548 {
1549     std::vector<Glib::ustring> result;
1550     result.push_back(getFilename());
1551     return result;
1552 }
1553 
1554 
1555 /*#########################################################################
1556 ### F I L E    S A V E
1557 #########################################################################*/
1558 
1559 /**
1560  * Constructor
1561  */
FileSaveDialogImplWin32(Gtk::Window & parent,const Glib::ustring & dir,FileDialogType fileTypes,const char * title,const Glib::ustring &,const char * docTitle,const Inkscape::Extension::FileSaveMethod save_method)1562 FileSaveDialogImplWin32::FileSaveDialogImplWin32(Gtk::Window &parent,
1563             const Glib::ustring &dir,
1564             FileDialogType fileTypes,
1565             const char *title,
1566             const Glib::ustring &/*default_key*/,
1567             const char *docTitle,
1568             const Inkscape::Extension::FileSaveMethod save_method) :
1569     FileDialogBaseWin32(parent, dir, title, fileTypes,
1570                         (save_method == Inkscape::Extension::FILE_SAVE_METHOD_SAVE_COPY) ? "dialogs.save_copy" :  "dialogs.save_as"),
1571         _title_label(NULL),
1572         _title_edit(NULL)
1573 {
1574     FileSaveDialog::myDocTitle = docTitle;
1575 
1576     if (dialogType != CUSTOM_TYPE)
1577         createFilterMenu();
1578 
1579     /* The code below sets the default file name */
1580         myFilename = "";
1581         if (dir.size() > 0) {
1582             Glib::ustring udir(dir);
1583             Glib::ustring::size_type len = udir.length();
1584             // leaving a trailing backslash on the directory name leads to the infamous
1585             // double-directory bug on win32
1586             if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
1587 
1588             // Remove the extension: remove everything past the last period found past the last slash
1589             // (not for CUSTOM_TYPE as we can not automatically add a file extension in that case yet)
1590             if (dialogType == CUSTOM_TYPE) {
1591                 myFilename = udir;
1592             } else {
1593                 size_t last_slash_index = udir.find_last_of( '\\' );
1594                 size_t last_period_index = udir.find_last_of( '.' );
1595                 if (last_period_index > last_slash_index) {
1596                     myFilename = udir.substr(0, last_period_index );
1597                 }
1598             }
1599 
1600             // remove one slash if double
1601             if (1 + myFilename.find("\\\\",2)) {
1602                 myFilename.replace(myFilename.find("\\\\",2), 1, "");
1603             }
1604         }
1605 }
1606 
~FileSaveDialogImplWin32()1607 FileSaveDialogImplWin32::~FileSaveDialogImplWin32()
1608 {
1609 }
1610 
createFilterMenu()1611 void FileSaveDialogImplWin32::createFilterMenu()
1612 {
1613     std::list<Filter> filter_list;
1614 
1615     knownExtensions.clear();
1616 
1617     // Compose the filter string
1618     Glib::ustring all_inkscape_files_filter, all_image_files_filter;
1619     Inkscape::Extension::DB::OutputList extension_list;
1620     Inkscape::Extension::db.get_output_list(extension_list);
1621 
1622     int filter_count = 0;
1623     int filter_length = 1;
1624     bool is_raster = dialogType == RASTER_TYPES;
1625 
1626     for (auto omod : extension_list) {
1627         // FIXME: would be nice to grey them out instead of not listing them
1628         if (omod->deactivated() || (omod->is_raster() != is_raster))
1629             continue;
1630 
1631         filter_count++;
1632 
1633         Filter filter;
1634 
1635         // Extension
1636         const gchar *filter_extension = omod->get_extension();
1637         filter.filter = g_utf8_to_utf16(
1638             filter_extension, -1, NULL, &filter.filter_length, NULL);
1639         knownExtensions.insert(std::pair<Glib::ustring, Inkscape::Extension::Output*>(Glib::ustring(filter_extension).casefold(), omod));
1640 
1641         // Type
1642         filter.name = g_utf8_to_utf16(omod->get_filetypename(true), -1, NULL, &filter.name_length, NULL);
1643 
1644         filter.mod = omod;
1645 
1646         filter_length += filter.name_length +
1647             filter.filter_length + 3;   // Add 3 for two \0s and a *
1648 
1649         filter_list.push_back(filter);
1650     }
1651 
1652     int extension_index = 0;
1653     _extension_map = new Inkscape::Extension::Extension*[filter_count];
1654 
1655     _filter = new wchar_t[filter_length];
1656     wchar_t *filterptr = _filter;
1657 
1658     for(std::list<Filter>::iterator filter_iterator = filter_list.begin();
1659         filter_iterator != filter_list.end(); ++filter_iterator)
1660     {
1661         const Filter &filter = *filter_iterator;
1662 
1663         wcsncpy(filterptr, (wchar_t*)filter.name, filter.name_length);
1664         filterptr += filter.name_length;
1665         g_free(filter.name);
1666 
1667         *(filterptr++) = L'\0';
1668         *(filterptr++) = L'*';
1669 
1670         wcsncpy(filterptr, (wchar_t*)filter.filter, filter.filter_length);
1671         filterptr += filter.filter_length;
1672         g_free(filter.filter);
1673 
1674         *(filterptr++) = L'\0';
1675 
1676         // Associate this input extension with the file type name
1677         _extension_map[extension_index++] = filter.mod;
1678     }
1679     *(filterptr++) = 0;
1680 
1681     _filter_count = extension_index;
1682     _filter_index = 1;  // A value of 1 selects the 1st filter - NOT the 2nd
1683 }
1684 
1685 
addFileType(Glib::ustring name,Glib::ustring pattern)1686 void FileSaveDialogImplWin32::addFileType(Glib::ustring name, Glib::ustring pattern)
1687 {
1688     std::list<Filter> filter_list;
1689 
1690     knownExtensions.clear();
1691 
1692     Filter all_exe_files;
1693 
1694     const gchar *all_exe_files_filter_name = name.data();
1695     const gchar *all_exe_files_filter = pattern.data();
1696 
1697     // Calculate the amount of memory required
1698     int filter_count = 1;
1699     int filter_length = 1;
1700 
1701     // Filter Executable Files
1702     all_exe_files.name = g_utf8_to_utf16(all_exe_files_filter_name,
1703         -1, NULL, &all_exe_files.name_length, NULL);
1704     all_exe_files.filter = g_utf8_to_utf16(all_exe_files_filter,
1705             -1, NULL, &all_exe_files.filter_length, NULL);
1706     all_exe_files.mod = NULL;
1707     filter_list.push_front(all_exe_files);
1708 
1709     filter_length = all_exe_files.name_length + all_exe_files.filter_length + 3; // Add 3 for two \0s and a *
1710 
1711     knownExtensions.insert(std::pair<Glib::ustring, Inkscape::Extension::Output*>(Glib::ustring(all_exe_files_filter).casefold(), NULL));
1712 
1713     int extension_index = 0;
1714     _extension_map = new Inkscape::Extension::Extension*[filter_count];
1715 
1716     _filter = new wchar_t[filter_length];
1717     wchar_t *filterptr = _filter;
1718 
1719     for(std::list<Filter>::iterator filter_iterator = filter_list.begin();
1720         filter_iterator != filter_list.end(); ++filter_iterator)
1721     {
1722         const Filter &filter = *filter_iterator;
1723 
1724         wcsncpy(filterptr, (wchar_t*)filter.name, filter.name_length);
1725         filterptr += filter.name_length;
1726         g_free(filter.name);
1727 
1728         *(filterptr++) = L'\0';
1729         *(filterptr++) = L'*';
1730 
1731         if(filter.filter != NULL)
1732         {
1733             wcsncpy(filterptr, (wchar_t*)filter.filter, filter.filter_length);
1734             filterptr += filter.filter_length;
1735             g_free(filter.filter);
1736         }
1737 
1738         *(filterptr++) = L'\0';
1739 
1740         // Associate this input extension with the file type name
1741         _extension_map[extension_index++] = filter.mod;
1742     }
1743     *(filterptr++) = L'\0';
1744 
1745     _filter_count = extension_index;
1746     _filter_index = 1;  // Select the 1st filter in the list
1747 
1748 
1749 }
1750 
GetSaveFileName_thread()1751 void FileSaveDialogImplWin32::GetSaveFileName_thread()
1752 {
1753     OPENFILENAMEW ofn;
1754 
1755     g_assert(_main_loop != NULL);
1756 
1757     WCHAR* current_directory_string = (WCHAR*)g_utf8_to_utf16(
1758         _current_directory.data(), _current_directory.length(),
1759         NULL, NULL, NULL);
1760 
1761     // Copy the selected file name, converting from UTF-8 to UTF-16
1762     memset(_path_string, 0, sizeof(_path_string));
1763     gunichar2* utf16_path_string = g_utf8_to_utf16(
1764         myFilename.data(), -1, NULL, NULL, NULL);
1765     wcsncpy(_path_string, (wchar_t*)utf16_path_string, _MAX_PATH);
1766     g_free(utf16_path_string);
1767 
1768     ZeroMemory(&ofn, sizeof(ofn));
1769     ofn.lStructSize = sizeof(ofn);
1770     ofn.hwndOwner = _ownerHwnd;
1771     ofn.lpstrFile = _path_string;
1772     ofn.nMaxFile = _MAX_PATH;
1773     ofn.nFilterIndex = _filter_index;
1774     ofn.lpstrFileTitle = NULL;
1775     ofn.nMaxFileTitle = 0;
1776     ofn.lpstrInitialDir = current_directory_string;
1777     ofn.lpstrTitle = _title;
1778     ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_ENABLEHOOK | OFN_ENABLESIZING;
1779     ofn.lpstrFilter = _filter;
1780     ofn.nFilterIndex = _filter_index;
1781     ofn.lpfnHook = GetSaveFileName_hookproc;
1782     ofn.lpstrDefExt = L"svg\0";
1783     ofn.lCustData = (LPARAM)this;
1784     _result = GetSaveFileNameW(&ofn) != 0;
1785 
1786     g_assert(ofn.nFilterIndex >= 1 && ofn.nFilterIndex <= _filter_count);
1787     _filter_index = ofn.nFilterIndex;
1788     _extension = _extension_map[ofn.nFilterIndex - 1];
1789 
1790     // Copy the selected file name, converting from UTF-16 to UTF-8
1791     myFilename = utf16_to_ustring(_path_string, _MAX_PATH);
1792 
1793     // Tidy up
1794     g_free(current_directory_string);
1795 
1796     g_main_loop_quit(_main_loop);
1797 }
1798 
1799 /**
1800  * Show this dialog modally.  Return true if user hits [OK]
1801  */
1802 bool
show()1803 FileSaveDialogImplWin32::show()
1804 {
1805 #if !GLIB_CHECK_VERSION(2,32,0)
1806     if(!Glib::thread_supported())
1807         Glib::thread_init();
1808 #endif
1809 
1810     _result = false;
1811     _main_loop = g_main_loop_new(g_main_context_default(), FALSE);
1812 
1813     if(_main_loop != NULL)
1814     {
1815 #if GLIB_CHECK_VERSION(2,32,0)
1816         if(Glib::Threads::Thread::create(sigc::mem_fun(*this, &FileSaveDialogImplWin32::GetSaveFileName_thread)))
1817 #else
1818         if(Glib::Thread::create(sigc::mem_fun(*this, &FileSaveDialogImplWin32::GetSaveFileName_thread), true))
1819 #endif
1820             g_main_loop_run(_main_loop);
1821 
1822         if(_result && _extension)
1823             appendExtension(myFilename, (Inkscape::Extension::Output*)_extension);
1824     }
1825 
1826     return _result;
1827 }
1828 
setSelectionType(Inkscape::Extension::Extension *)1829 void FileSaveDialogImplWin32::setSelectionType( Inkscape::Extension::Extension * /*key*/ )
1830 {
1831     // If no pointer to extension is passed in, look up based on filename extension.
1832 
1833 }
1834 
1835 
GetSaveFileName_hookproc(HWND hdlg,UINT uiMsg,WPARAM,LPARAM lParam)1836 UINT_PTR CALLBACK FileSaveDialogImplWin32::GetSaveFileName_hookproc(
1837     HWND hdlg, UINT uiMsg, WPARAM, LPARAM lParam)
1838 {
1839     FileSaveDialogImplWin32 *pImpl = reinterpret_cast<FileSaveDialogImplWin32*>
1840         (GetWindowLongPtr(hdlg, GWLP_USERDATA));
1841 
1842     switch(uiMsg)
1843     {
1844     case WM_INITDIALOG:
1845         {
1846             HWND hParentWnd = GetParent(hdlg);
1847             HINSTANCE hInstance = GetModuleHandle(NULL);
1848 
1849             // get size/pos of typical combo box
1850             RECT rEDT1, rCB1, rROOT, rST;
1851             GetWindowRect(GetDlgItem(hParentWnd, cmb1), &rCB1);
1852             GetWindowRect(GetDlgItem(hParentWnd, cmb13), &rEDT1);
1853             GetWindowRect(GetDlgItem(hParentWnd, stc2), &rST);
1854             GetWindowRect(hdlg, &rROOT);
1855             int ydelta = rCB1.top - rEDT1.top;
1856             if ( ydelta < 0 ) {
1857                 g_warning("Negative dialog ydelta");
1858                 ydelta = 0;
1859             }
1860 
1861             // Make the window a bit longer
1862             // Note: we have a width delta of 1 because there is a suspicion that MoveWindow() to the same size causes zero-width results.
1863             RECT rcRect;
1864             GetWindowRect(hParentWnd, &rcRect);
1865             MoveWindow(hParentWnd, rcRect.left, rcRect.top,
1866                        sanitizeWindowSizeParam( rcRect.right - rcRect.left, 1, WINDOW_WIDTH_MINIMUM, WINDOW_WIDTH_FALLBACK ),
1867                        sanitizeWindowSizeParam( rcRect.bottom - rcRect.top, ydelta, WINDOW_HEIGHT_MINIMUM, WINDOW_HEIGHT_FALLBACK ),
1868                        FALSE);
1869 
1870             // It is not necessary to delete stock objects by calling DeleteObject
1871             HGDIOBJ dlgFont = GetStockObject(DEFAULT_GUI_FONT);
1872 
1873             // Set the pointer to the object
1874             OPENFILENAMEW *ofn = reinterpret_cast<OPENFILENAMEW*>(lParam);
1875             SetWindowLongPtr(hdlg, GWLP_USERDATA, ofn->lCustData);
1876             SetWindowLongPtr(hParentWnd, GWLP_USERDATA, ofn->lCustData);
1877             pImpl = reinterpret_cast<FileSaveDialogImplWin32*>(ofn->lCustData);
1878 
1879             // Create the Title label and edit control
1880             wchar_t *title_label_str = (wchar_t *)g_utf8_to_utf16(_("Title:"), -1, NULL, NULL, NULL);
1881             pImpl->_title_label = CreateWindowExW(0, L"STATIC", title_label_str,
1882                                         WS_VISIBLE|WS_CHILD,
1883                                         CW_USEDEFAULT, CW_USEDEFAULT, rCB1.left-rST.left, rST.bottom-rST.top,
1884                                         hParentWnd, NULL, hInstance, NULL);
1885             g_free(title_label_str);
1886 
1887             if(pImpl->_title_label) {
1888               if(dlgFont) SendMessage(pImpl->_title_label, WM_SETFONT, (WPARAM)dlgFont, MAKELPARAM(FALSE, 0));
1889               SetWindowPos(pImpl->_title_label, NULL, rST.left-rROOT.left, rST.top+ydelta-rROOT.top,
1890                            rCB1.left-rST.left, rST.bottom-rST.top, SWP_SHOWWINDOW|SWP_NOZORDER);
1891             }
1892 
1893             pImpl->_title_edit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
1894                                         WS_VISIBLE|WS_CHILD|WS_TABSTOP|ES_AUTOHSCROLL,
1895                                         CW_USEDEFAULT, CW_USEDEFAULT, rCB1.right-rCB1.left, rCB1.bottom-rCB1.top,
1896                                         hParentWnd, NULL, hInstance, NULL);
1897             if(pImpl->_title_edit) {
1898               if(dlgFont) SendMessage(pImpl->_title_edit, WM_SETFONT, (WPARAM)dlgFont, MAKELPARAM(FALSE, 0));
1899               SetWindowPos(pImpl->_title_edit, NULL, rCB1.left-rROOT.left, rCB1.top+ydelta-rROOT.top,
1900                            rCB1.right-rCB1.left, rCB1.bottom-rCB1.top, SWP_SHOWWINDOW|SWP_NOZORDER);
1901               SetWindowTextW(pImpl->_title_edit,
1902                              (const wchar_t*)g_utf8_to_utf16(pImpl->myDocTitle.c_str(), -1, NULL, NULL, NULL));
1903             }
1904         }
1905         break;
1906     case WM_DESTROY:
1907       {
1908         if(pImpl->_title_edit) {
1909           int length = GetWindowTextLengthW(pImpl->_title_edit)+1;
1910           wchar_t* temp_title = new wchar_t[length];
1911           GetWindowTextW(pImpl->_title_edit, temp_title, length);
1912           pImpl->myDocTitle = g_utf16_to_utf8((gunichar2*)temp_title, -1, NULL, NULL, NULL);
1913           delete[] temp_title;
1914           DestroyWindow(pImpl->_title_label);
1915           pImpl->_title_label = NULL;
1916           DestroyWindow(pImpl->_title_edit);
1917           pImpl->_title_edit = NULL;
1918         }
1919       }
1920       break;
1921     }
1922 
1923     // Use default dialog behaviour
1924     return 0;
1925 }
1926 
1927 } } } // namespace Dialog, UI, Inkscape
1928 
1929 #endif // ifdef _WIN32
1930 
1931 /*
1932   Local Variables:
1933   mode:c++
1934   c-file-style:"stroustrup"
1935   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1936   indent-tabs-mode:nil
1937   fill-column:99
1938   End:
1939 */
1940 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
1941