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