1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <QUrl>
21 #include <KFileWidget>
22 
23 #include "gtk3_kde5_filepicker.hxx"
24 
25 #include <com/sun/star/lang/IllegalArgumentException.hpp>
26 #include <cppuhelper/supportsservice.hxx>
27 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
28 #include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
29 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
30 
31 #include <sal/log.hxx>
32 #include <vcl/svapp.hxx>
33 
34 #include "FPServiceInfo.hxx"
35 
36 #undef Region
37 
38 #include <strings.hrc>
39 
40 using namespace ::com::sun::star;
41 using namespace ::com::sun::star::ui::dialogs;
42 using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
43 using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
44 using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
45 using namespace ::com::sun::star::lang;
46 using namespace ::com::sun::star::beans;
47 using namespace ::com::sun::star::uno;
48 
49 // helper functions
50 
51 namespace
52 {
FilePicker_getSupportedServiceNames()53 uno::Sequence<OUString> FilePicker_getSupportedServiceNames()
54 {
55     return { "com.sun.star.ui.dialogs.FilePicker", "com.sun.star.ui.dialogs.SystemFilePicker",
56              "com.sun.star.ui.dialogs.Gtk3KDE5FilePicker" };
57 }
58 }
59 
60 // Gtk3KDE5FilePicker
61 
Gtk3KDE5FilePicker(const uno::Reference<uno::XComponentContext> &)62 Gtk3KDE5FilePicker::Gtk3KDE5FilePicker(const uno::Reference<uno::XComponentContext>&)
63     : Gtk3KDE5FilePicker_Base(_helperMutex)
64 {
65     setMultiSelectionMode(false);
66 
67     // tdf#124598 dummy KWidget use to make gtk3_kde5 VCL plugin link against KIO libraries
68     QString sDummyStr;
69     QUrl aUrl = KFileWidget::getStartUrl(QUrl(), sDummyStr);
70     aUrl.setPath("/dev/null");
71 }
72 
73 Gtk3KDE5FilePicker::~Gtk3KDE5FilePicker() = default;
74 
75 void SAL_CALL
addFilePickerListener(const uno::Reference<XFilePickerListener> & xListener)76 Gtk3KDE5FilePicker::addFilePickerListener(const uno::Reference<XFilePickerListener>& xListener)
77 {
78     SolarMutexGuard aGuard;
79     m_xListener = xListener;
80 }
81 
82 void SAL_CALL
removeFilePickerListener(const uno::Reference<XFilePickerListener> &)83 Gtk3KDE5FilePicker::removeFilePickerListener(const uno::Reference<XFilePickerListener>&)
84 {
85     SolarMutexGuard aGuard;
86     m_xListener.clear();
87 }
88 
setTitle(const OUString & title)89 void SAL_CALL Gtk3KDE5FilePicker::setTitle(const OUString& title)
90 {
91     m_ipc.sendCommand(Commands::SetTitle, title);
92 }
93 
execute()94 sal_Int16 SAL_CALL Gtk3KDE5FilePicker::execute()
95 {
96     SolarMutexGuard g;
97     return m_ipc.execute();
98 }
99 
setMultiSelectionMode(sal_Bool multiSelect)100 void SAL_CALL Gtk3KDE5FilePicker::setMultiSelectionMode(sal_Bool multiSelect)
101 {
102     m_ipc.sendCommand(Commands::SetMultiSelectionMode, bool(multiSelect));
103 }
104 
setDefaultName(const OUString & name)105 void SAL_CALL Gtk3KDE5FilePicker::setDefaultName(const OUString& name)
106 {
107     m_ipc.sendCommand(Commands::SetDefaultName, name);
108 }
109 
setDisplayDirectory(const OUString & dir)110 void SAL_CALL Gtk3KDE5FilePicker::setDisplayDirectory(const OUString& dir)
111 {
112     m_ipc.sendCommand(Commands::SetDisplayDirectory, dir);
113 }
114 
getDisplayDirectory()115 OUString SAL_CALL Gtk3KDE5FilePicker::getDisplayDirectory()
116 {
117     auto id = m_ipc.sendCommand(Commands::GetDisplayDirectory);
118     OUString dir;
119     m_ipc.readResponse(id, dir);
120     return dir;
121 }
122 
getFiles()123 uno::Sequence<OUString> SAL_CALL Gtk3KDE5FilePicker::getFiles()
124 {
125     uno::Sequence<OUString> seq = getSelectedFiles();
126     if (seq.getLength() > 1)
127         seq.realloc(1);
128     return seq;
129 }
130 
getSelectedFiles()131 uno::Sequence<OUString> SAL_CALL Gtk3KDE5FilePicker::getSelectedFiles()
132 {
133     auto id = m_ipc.sendCommand(Commands::GetSelectedFiles);
134     uno::Sequence<OUString> seq;
135     m_ipc.readResponse(id, seq);
136     return seq;
137 }
138 
appendFilter(const OUString & title,const OUString & filter)139 void SAL_CALL Gtk3KDE5FilePicker::appendFilter(const OUString& title, const OUString& filter)
140 {
141     m_ipc.sendCommand(Commands::AppendFilter, title, filter);
142 }
143 
setCurrentFilter(const OUString & title)144 void SAL_CALL Gtk3KDE5FilePicker::setCurrentFilter(const OUString& title)
145 {
146     m_ipc.sendCommand(Commands::SetCurrentFilter, title);
147 }
148 
getCurrentFilter()149 OUString SAL_CALL Gtk3KDE5FilePicker::getCurrentFilter()
150 {
151     auto id = m_ipc.sendCommand(Commands::GetCurrentFilter);
152     OUString filter;
153     m_ipc.readResponse(id, filter);
154     return filter;
155 }
156 
appendFilterGroup(const OUString &,const uno::Sequence<beans::StringPair> & filters)157 void SAL_CALL Gtk3KDE5FilePicker::appendFilterGroup(const OUString& /*rGroupTitle*/,
158                                                     const uno::Sequence<beans::StringPair>& filters)
159 {
160     const sal_uInt16 length = filters.getLength();
161     for (sal_uInt16 i = 0; i < length; ++i)
162     {
163         beans::StringPair aPair = filters[i];
164         appendFilter(aPair.First, aPair.Second);
165     }
166 }
167 
setValue(sal_Int16 controlId,sal_Int16 nControlAction,const uno::Any & value)168 void SAL_CALL Gtk3KDE5FilePicker::setValue(sal_Int16 controlId, sal_Int16 nControlAction,
169                                            const uno::Any& value)
170 {
171     if (value.has<bool>())
172     {
173         m_ipc.sendCommand(Commands::SetValue, controlId, nControlAction, value.get<bool>());
174     }
175     else
176     {
177         SAL_INFO("vcl.gtkkde5", "set value of unhandled type " << controlId);
178     }
179 }
180 
getValue(sal_Int16 controlId,sal_Int16 nControlAction)181 uno::Any SAL_CALL Gtk3KDE5FilePicker::getValue(sal_Int16 controlId, sal_Int16 nControlAction)
182 {
183     if (CHECKBOX_AUTOEXTENSION == controlId)
184         // We ignore this one and rely on QFileDialog to provide the function.
185         // Always return false, to pretend we do not support this, otherwise
186         // LO core would try to be smart and cut the extension in some places,
187         // interfering with QFileDialog's handling of it. QFileDialog also
188         // saves the value of the setting, so LO core is not needed for that either.
189         return uno::Any(false);
190 
191     auto id = m_ipc.sendCommand(Commands::GetValue, controlId, nControlAction);
192 
193     bool value = false;
194     m_ipc.readResponse(id, value);
195 
196     return uno::Any(value);
197 }
198 
enableControl(sal_Int16 controlId,sal_Bool enable)199 void SAL_CALL Gtk3KDE5FilePicker::enableControl(sal_Int16 controlId, sal_Bool enable)
200 {
201     m_ipc.sendCommand(Commands::EnableControl, controlId, bool(enable));
202 }
203 
setLabel(sal_Int16 controlId,const OUString & label)204 void SAL_CALL Gtk3KDE5FilePicker::setLabel(sal_Int16 controlId, const OUString& label)
205 {
206     m_ipc.sendCommand(Commands::SetLabel, controlId, label);
207 }
208 
getLabel(sal_Int16 controlId)209 OUString SAL_CALL Gtk3KDE5FilePicker::getLabel(sal_Int16 controlId)
210 {
211     auto id = m_ipc.sendCommand(Commands::GetLabel, controlId);
212     OUString label;
213     m_ipc.readResponse(id, label);
214     return label;
215 }
216 
addCustomControl(sal_Int16 controlId)217 void Gtk3KDE5FilePicker::addCustomControl(sal_Int16 controlId)
218 {
219     const char* resId = nullptr;
220 
221     switch (controlId)
222     {
223         case CHECKBOX_AUTOEXTENSION:
224             resId = STR_FPICKER_AUTO_EXTENSION;
225             break;
226         case CHECKBOX_PASSWORD:
227             resId = STR_FPICKER_PASSWORD;
228             break;
229         case CHECKBOX_FILTEROPTIONS:
230             resId = STR_FPICKER_FILTER_OPTIONS;
231             break;
232         case CHECKBOX_READONLY:
233             resId = STR_FPICKER_READONLY;
234             break;
235         case CHECKBOX_LINK:
236             resId = STR_FPICKER_INSERT_AS_LINK;
237             break;
238         case CHECKBOX_PREVIEW:
239             resId = STR_FPICKER_SHOW_PREVIEW;
240             break;
241         case CHECKBOX_SELECTION:
242             resId = STR_FPICKER_SELECTION;
243             break;
244         case CHECKBOX_GPGENCRYPTION:
245             resId = STR_FPICKER_GPGENCRYPT;
246             break;
247         case PUSHBUTTON_PLAY:
248             resId = STR_FPICKER_PLAY;
249             break;
250         case LISTBOX_VERSION:
251             resId = STR_FPICKER_VERSION;
252             break;
253         case LISTBOX_TEMPLATE:
254             resId = STR_FPICKER_TEMPLATES;
255             break;
256         case LISTBOX_IMAGE_TEMPLATE:
257             resId = STR_FPICKER_IMAGE_TEMPLATE;
258             break;
259         case LISTBOX_IMAGE_ANCHOR:
260             resId = STR_FPICKER_IMAGE_ANCHOR;
261             break;
262         case LISTBOX_VERSION_LABEL:
263         case LISTBOX_TEMPLATE_LABEL:
264         case LISTBOX_IMAGE_TEMPLATE_LABEL:
265         case LISTBOX_IMAGE_ANCHOR_LABEL:
266         case LISTBOX_FILTER_SELECTOR:
267             break;
268     }
269 
270     switch (controlId)
271     {
272         case CHECKBOX_AUTOEXTENSION:
273         case CHECKBOX_PASSWORD:
274         case CHECKBOX_FILTEROPTIONS:
275         case CHECKBOX_READONLY:
276         case CHECKBOX_LINK:
277         case CHECKBOX_PREVIEW:
278         case CHECKBOX_SELECTION:
279         case CHECKBOX_GPGENCRYPTION:
280         {
281             // the checkbox is created even for CHECKBOX_AUTOEXTENSION to simplify
282             // code, but the checkbox is hidden and ignored
283             bool hidden = controlId == CHECKBOX_AUTOEXTENSION;
284 
285             m_ipc.sendCommand(Commands::AddCheckBox, controlId, hidden, getResString(resId));
286 
287             break;
288         }
289         case PUSHBUTTON_PLAY:
290         case LISTBOX_VERSION:
291         case LISTBOX_TEMPLATE:
292         case LISTBOX_IMAGE_TEMPLATE:
293         case LISTBOX_IMAGE_ANCHOR:
294         case LISTBOX_VERSION_LABEL:
295         case LISTBOX_TEMPLATE_LABEL:
296         case LISTBOX_IMAGE_TEMPLATE_LABEL:
297         case LISTBOX_IMAGE_ANCHOR_LABEL:
298         case LISTBOX_FILTER_SELECTOR:
299             break;
300     }
301 }
302 
initialize(const uno::Sequence<uno::Any> & args)303 void SAL_CALL Gtk3KDE5FilePicker::initialize(const uno::Sequence<uno::Any>& args)
304 {
305     // parameter checking
306     uno::Any arg;
307     if (args.getLength() == 0)
308     {
309         throw lang::IllegalArgumentException("no arguments", static_cast<XFilePicker2*>(this), 1);
310     }
311 
312     arg = args[0];
313 
314     if ((arg.getValueType() != cppu::UnoType<sal_Int16>::get())
315         && (arg.getValueType() != cppu::UnoType<sal_Int8>::get()))
316     {
317         throw lang::IllegalArgumentException("invalid argument type",
318                                              static_cast<XFilePicker2*>(this), 1);
319     }
320 
321     sal_Int16 templateId = -1;
322     arg >>= templateId;
323 
324     bool saveDialog = false;
325     switch (templateId)
326     {
327         case FILEOPEN_SIMPLE:
328             break;
329 
330         case FILESAVE_SIMPLE:
331             saveDialog = true;
332             break;
333 
334         case FILESAVE_AUTOEXTENSION:
335             saveDialog = true;
336             addCustomControl(CHECKBOX_AUTOEXTENSION);
337             break;
338 
339         case FILESAVE_AUTOEXTENSION_PASSWORD:
340         {
341             saveDialog = true;
342             addCustomControl(CHECKBOX_PASSWORD);
343             addCustomControl(CHECKBOX_GPGENCRYPTION);
344             break;
345         }
346         case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
347         {
348             saveDialog = true;
349             addCustomControl(CHECKBOX_AUTOEXTENSION);
350             addCustomControl(CHECKBOX_PASSWORD);
351             addCustomControl(CHECKBOX_GPGENCRYPTION);
352             addCustomControl(CHECKBOX_FILTEROPTIONS);
353             break;
354         }
355         case FILESAVE_AUTOEXTENSION_SELECTION:
356             saveDialog = true;
357             addCustomControl(CHECKBOX_AUTOEXTENSION);
358             addCustomControl(CHECKBOX_SELECTION);
359             break;
360 
361         case FILESAVE_AUTOEXTENSION_TEMPLATE:
362             saveDialog = true;
363             addCustomControl(CHECKBOX_AUTOEXTENSION);
364             addCustomControl(LISTBOX_TEMPLATE);
365             break;
366 
367         case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
368             addCustomControl(CHECKBOX_LINK);
369             addCustomControl(CHECKBOX_PREVIEW);
370             addCustomControl(LISTBOX_IMAGE_TEMPLATE);
371             break;
372 
373         case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
374             addCustomControl(CHECKBOX_LINK);
375             addCustomControl(CHECKBOX_PREVIEW);
376             addCustomControl(LISTBOX_IMAGE_ANCHOR);
377             break;
378 
379         case FILEOPEN_PLAY:
380             addCustomControl(PUSHBUTTON_PLAY);
381             break;
382 
383         case FILEOPEN_LINK_PLAY:
384             addCustomControl(CHECKBOX_LINK);
385             addCustomControl(PUSHBUTTON_PLAY);
386             break;
387 
388         case FILEOPEN_READONLY_VERSION:
389             addCustomControl(CHECKBOX_READONLY);
390             addCustomControl(LISTBOX_VERSION);
391             break;
392 
393         case FILEOPEN_LINK_PREVIEW:
394             addCustomControl(CHECKBOX_LINK);
395             addCustomControl(CHECKBOX_PREVIEW);
396             break;
397 
398         case FILEOPEN_PREVIEW:
399             addCustomControl(CHECKBOX_PREVIEW);
400             break;
401 
402         default:
403             SAL_INFO("vcl.gtkkde5", "unknown templates " << templateId);
404             return;
405     }
406 
407     setTitle(getResString(saveDialog ? STR_FPICKER_SAVE : STR_FPICKER_OPEN));
408 
409     m_ipc.sendCommand(Commands::Initialize, saveDialog);
410 }
411 
cancel()412 void SAL_CALL Gtk3KDE5FilePicker::cancel()
413 {
414     // TODO
415 }
416 
disposing(const lang::EventObject & rEvent)417 void Gtk3KDE5FilePicker::disposing(const lang::EventObject& rEvent)
418 {
419     uno::Reference<XFilePickerListener> xFilePickerListener(rEvent.Source, uno::UNO_QUERY);
420 
421     if (xFilePickerListener.is())
422     {
423         removeFilePickerListener(xFilePickerListener);
424     }
425 }
426 
getImplementationName()427 OUString SAL_CALL Gtk3KDE5FilePicker::getImplementationName() { return FILE_PICKER_IMPL_NAME; }
428 
supportsService(const OUString & ServiceName)429 sal_Bool SAL_CALL Gtk3KDE5FilePicker::supportsService(const OUString& ServiceName)
430 {
431     return cppu::supportsService(this, ServiceName);
432 }
433 
getSupportedServiceNames()434 uno::Sequence<OUString> SAL_CALL Gtk3KDE5FilePicker::getSupportedServiceNames()
435 {
436     return FilePicker_getSupportedServiceNames();
437 }
438 
filterChanged()439 void Gtk3KDE5FilePicker::filterChanged()
440 {
441     FilePickerEvent aEvent;
442     aEvent.ElementId = LISTBOX_FILTER;
443     SAL_INFO("vcl.gtkkde5", "filter changed");
444     if (m_xListener.is())
445         m_xListener->controlStateChanged(aEvent);
446 }
447 
selectionChanged()448 void Gtk3KDE5FilePicker::selectionChanged()
449 {
450     FilePickerEvent aEvent;
451     SAL_INFO("vcl.gtkkde5", "file selection changed");
452     if (m_xListener.is())
453         m_xListener->fileSelectionChanged(aEvent);
454 }
455 
456 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
457