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