1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /** @file
3  * @brief Implementation of native file dialogs for Win32
4  */
5 /* Authors:
6  *   Joel Holdsworth
7  *   The Inkscape Organization
8  *
9  * Copyright (C) 2004-2008 The Inkscape Organization
10  *
11  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12  */
13 
14 #include <glibmm.h>
15 
16 #ifdef _WIN32
17 
18 #include "filedialogimpl-gtkmm.h"
19 
20 #include "inkgc/gc-core.h"
21 
22 #include <windows.h>
23 
24 
25 namespace Inkscape
26 {
27 namespace UI
28 {
29 namespace Dialog
30 {
31 
32 /*#########################################################################
33 ### F I L E     D I A L O G    B A S E    C L A S S
34 #########################################################################*/
35 
36 /// This class is the base implementation of a MS Windows
37 /// file dialog.
38 class FileDialogBaseWin32
39 {
40 protected:
41     /// Abstract Constructor
42     /// @param parent The parent window for the dialog
43     /// @param dir The directory to begin browing from
44     /// @param title The title caption for the dialog in UTF-8
45     /// @param type The dialog type
46     /// @param preferenceBase The preferences key
47     FileDialogBaseWin32(Gtk::Window &parent, const Glib::ustring &dir,
48         const char *title, FileDialogType type,
49         gchar const *preferenceBase);
50 
51     /// Destructor
52     ~FileDialogBaseWin32();
53 
54 public:
55 
56     /// Gets the currently selected extension. Valid after an [OK]
57     /// @return Returns a pointer to the selected extension, or NULL
58     /// if the selected filter requires an automatic type detection
59     Inkscape::Extension::Extension* getSelectionType();
60 
61     /// Get the path of the current directory
62     Glib::ustring getCurrentDirectory();
63 
64 
65 protected:
66     /// The dialog type
67     FileDialogType dialogType;
68 
69 	/// A pointer to the GTK main-loop context object. This
70     /// is used to keep the rest of the inkscape UI running
71     /// while the file dialog is displayed
72     GMainLoop *_main_loop;
73 
74     /// The result of the call to GetOpenFileName. If true
75     /// the user clicked OK, if false the user clicked cancel
76     bool _result;
77 
78     /// The parent window
79     Gtk::Window &parent;
80 
81     /// The windows handle of the parent window
82     HWND _ownerHwnd;
83 
84     /// The path of the directory that is currently being
85     /// browsed
86     Glib::ustring _current_directory;
87 
88     /// The title of the dialog in UTF-16
89     wchar_t *_title;
90 
91     /// The path of the currently selected file in UTF-16
92     wchar_t _path_string[_MAX_PATH];
93 
94     /// The filter string for GetOpenFileName in UTF-16
95     wchar_t *_filter;
96 
97     /// The index of the currently selected filter.
98 	/// This value must be greater than or equal to 1,
99 	/// and less than or equal to _filter_count.
100     unsigned int _filter_index;
101 
102 	/// The number of filters registered
103 	unsigned int _filter_count;
104 
105     /// An array of the extensions associated with the
106     /// file types of each filter. So the Nth entry of
107     /// this array corresponds to the extension of the Nth
108     /// filter in the list. NULL if no specific extension is
109     /// specified/
110     Inkscape::Extension::Extension **_extension_map;
111 
112 	/// The currently selected extension. Valid after an [OK]
113     Inkscape::Extension::Extension *_extension;
114 };
115 
116 
117 /*#########################################################################
118 ### F I L E    O P E N
119 #########################################################################*/
120 
121 /// An Inkscape compatible wrapper around MS Windows GetOpenFileName API
122 class FileOpenDialogImplWin32 : public FileOpenDialog, public FileDialogBaseWin32
123 {
124 public:
125     /// Constructor
126     /// @param parent The parent window for the dialog
127     /// @param dir The directory to begin browing from
128     /// @param title The title caption for the dialog in UTF-8
129     /// @param type The dialog type
130     FileOpenDialogImplWin32(Gtk::Window &parent,
131                             const Glib::ustring &dir,
132                             FileDialogType fileTypes,
133                             const char *title);
134 
135     /// Destructor
136     virtual ~FileOpenDialogImplWin32();
137 
138     /// Shows the file dialog, and blocks until a file
139     /// has been selected.
140     /// @return Returns true if the user selected a
141     /// file, or false if the user pressed cancel.
142     bool show();
143 
144     /// Gets a list of the selected file names
145     /// @return Returns an STL vector filled with the
146     /// GTK names of the selected files
147     std::vector<Glib::ustring> getFilenames();
148 
149     /// Get the path of the current directory
getCurrentDirectory()150     virtual Glib::ustring getCurrentDirectory()
151         { return FileDialogBaseWin32::getCurrentDirectory(); }
152 
153     /// Gets the currently selected extension. Valid after an [OK]
154     /// @return Returns a pointer to the selected extension, or NULL
155     /// if the selected filter requires an automatic type detection
getSelectionType()156     virtual Inkscape::Extension::Extension* getSelectionType()
157         { return FileDialogBaseWin32::getSelectionType(); }
158 
159 
160     /// Add a custom file filter menu item
161     /// @param name - Name of the filter (such as "Javscript")
162     /// @param pattern - File filtering patter (such as "*.js")
163     /// Use the FileDialogType::CUSTOM_TYPE in constructor to not include other file types
164     virtual void addFilterMenu(Glib::ustring name, Glib::ustring pattern);
165 
166 private:
167 
168     /// Create filter menu for this type of dialog
169     void createFilterMenu();
170 
171 
172     /// The handle of the preview pane window
173     HWND _preview_wnd;
174 
175     /// The handle of the file dialog window
176     HWND _file_dialog_wnd;
177 
178     /// A pointer to the standard window proc of the
179     /// unhooked file dialog
180     WNDPROC _base_window_proc;
181 
182     /// The handle of the bitmap of the "show preview"
183     /// toggle button
184     HBITMAP _show_preview_button_bitmap;
185 
186     /// The handle of the toolbar's window
187     HWND _toolbar_wnd;
188 
189     /// This flag is set true when the preview should be
190     /// shown, or false when it should be hidden
191     static bool _show_preview;
192 
193 
194     /// The current width of the preview pane in pixels
195     int _preview_width;
196 
197     /// The current height of the preview pane in pixels
198     int _preview_height;
199 
200     /// The handle of the windows to display within the
201     /// preview pane, or NULL if no image should be displayed
202     HBITMAP _preview_bitmap;
203 
204     /// The windows shell icon for the selected file
205     HICON _preview_file_icon;
206 
207     /// The size of the preview file in kilobytes
208     unsigned long _preview_file_size;
209 
210 
211     /// The width of the document to be shown in the preview panel
212     double _preview_document_width;
213 
214     /// The width of the document to be shown in the preview panel
215     double _preview_document_height;
216 
217     /// The width of the rendered preview image in pixels
218     int _preview_image_width;
219 
220     /// The height of the rendered preview image in pixels
221     int _preview_image_height;
222 
223     /// A GDK Pixbuf of the rendered preview to be displayed
224     Glib::RefPtr<Gdk::Pixbuf> _preview_bitmap_image;
225 
226     /// This flag is set true if a file has been selected
227     bool _file_selected;
228 
229 	/// This flag is set true when the GetOpenFileName call
230     /// has returned
231     bool _finished;
232 
233     /// This mutex is used to ensure that the worker thread
234     /// that calls GetOpenFileName cannot collide with the
235     /// main Inkscape thread
236 #if GLIB_CHECK_VERSION(2,32,0)
237     Glib::Threads::Mutex *_mutex;
238 #else
239     Glib::Mutex *_mutex;
240 #endif
241 
242 
243     /// The controller function for the thread which calls
244     /// GetOpenFileName
245     void GetOpenFileName_thread();
246 
247     /// Registers the Windows Class of the preview panel window
248     static void register_preview_wnd_class();
249 
250     /// A message proc which is called by the standard dialog
251     /// proc
252     static UINT_PTR CALLBACK GetOpenFileName_hookproc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam);
253 
254     /// A message proc which wraps the standard dialog proc,
255     /// but intercepts some calls
256     static LRESULT CALLBACK file_dialog_subclass_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
257 
258     /// The message proc for the preview panel window
259     static LRESULT CALLBACK preview_wnd_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
260 
261     /// Lays out the controls in the file dialog given it's
262     /// current size
263     /// GetOpenFileName thread only.
264     void layout_dialog();
265 
266     /// Enables or disables the file preview.
267     /// GetOpenFileName thread only.
268     void enable_preview(bool enable);
269 
270     /// This function is called in the App thread when a file had
271     /// been selected
272     void file_selected();
273 
274     /// Loads and renders the unshrunk preview image.
275     /// Main app thread only.
276     void load_preview();
277 
278     /// Frees all the allocated objects associated with the file
279     /// currently being previewed
280     /// Main app thread only.
281     void free_preview();
282 
283     /// Loads preview for an SVG or SVGZ file.
284     /// Main app thread only.
285     /// @return Returns true if the SVG loaded successfully
286     bool set_svg_preview();
287 
288     /// A callback to allow this class to dispose of the
289     /// memory block of the rendered SVG bitmap
290     /// @buffer buffer The buffer to free
291     static void destroy_svg_rendering(const guint8 *buffer);
292 
293     /// Loads the preview for a raster image
294     /// Main app thread only.
295     /// @return Returns true if the image loaded successfully
296     bool set_image_preview();
297 
298     /// Loads the preview for a meta file
299     /// Main app thread only.
300     /// @return Returns true if the image loaded successfully
301     bool set_emf_preview();
302 
303     /// This flag is set true when a meta file is previewed
304     bool _preview_emf_image;
305 
306     /// Renders the unshrunk preview image to a windows HTBITMAP
307     /// which can be painted in the preview pain.
308     /// Main app thread only.
309     void render_preview();
310 
311     /// Formats the caption in UTF-16 for the preview image
312     /// @param caption The buffer to format the caption string into
313     /// @param caption_size The number of wchar_ts in the caption buffer
314     /// @return Returns the number of characters in caption string
315     int format_caption(wchar_t *caption, int caption_size);
316 };
317 
318 
319 /*#########################################################################
320 ### F I L E    S A V E
321 #########################################################################*/
322 
323 /// An Inkscape compatible wrapper around MS Windows GetSaveFileName API
324 class FileSaveDialogImplWin32 : public FileSaveDialog, public FileDialogBaseWin32
325 {
326 
327 public:
328     FileSaveDialogImplWin32(Gtk::Window &parent,
329                             const Glib::ustring &dir,
330                             FileDialogType fileTypes,
331                             const char *title,
332                             const Glib::ustring &default_key,
333                             const char *docTitle,
334                             const Inkscape::Extension::FileSaveMethod save_method);
335 
336     /// Destructor
337     virtual ~FileSaveDialogImplWin32();
338 
339     /// Shows the file dialog, and blocks until a file
340     /// has been selected.
341     /// @return Returns true if the user selected a
342     /// file, or false if the user pressed cancel.
343     bool show();
344 
345     /// Get the path of the current directory
getCurrentDirectory()346     virtual Glib::ustring getCurrentDirectory()
347         { return FileDialogBaseWin32::getCurrentDirectory(); }
348 
349     /// Gets the currently selected extension. Valid after an [OK]
350     /// @return Returns a pointer to the selected extension, or NULL
351     /// if the selected filter requires an automatic type detection
getSelectionType()352     virtual Inkscape::Extension::Extension* getSelectionType()
353         { return FileDialogBaseWin32::getSelectionType(); }
354 
355     virtual void setSelectionType( Inkscape::Extension::Extension *key );
356 
357     virtual void addFileType(Glib::ustring name, Glib::ustring pattern);
358 
359 private:
360 	/// A handle to the title label and edit box
361     HWND _title_label;
362     HWND _title_edit;
363 
364     /// Create a filter menu for this type of dialog
365     void createFilterMenu();
366 
367     /// The controller function for the thread which calls
368     /// GetSaveFileName
369     void GetSaveFileName_thread();
370 
371     /// A message proc which is called by the standard dialog
372     /// proc
373     static UINT_PTR CALLBACK GetSaveFileName_hookproc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam);
374 
375 };
376 
377 
378 }
379 }
380 }
381 
382 #endif
383 
384 /*
385   Local Variables:
386   mode:c++
387   c-file-style:"stroustrup"
388   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
389   indent-tabs-mode:nil
390   fill-column:99
391   End:
392 */
393 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
394