1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2
3 /* AbiSource Application Framework
4 * Copyright (C) 1998 AbiSource, Inc.
5 * Copyright (C) 2009 Hubert Figuiere
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301 USA.
21 */
22
23 /*
24 * Port to Maemo Development Platform
25 * Author: INdT - Renato Araujo <renato.filho@indt.org.br>
26 */
27
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <gtk/gtk.h>
34 #include <gdk/gdkkeysyms.h> // this include seems to fix 12332 (it defines GDK_Escape)
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include "ut_string.h"
43 #include "ut_assert.h"
44 #include "xap_UnixDialogHelper.h"
45 #include "xap_GtkComboBoxHelpers.h"
46 #include "xap_Dialog_Id.h"
47 #include "xap_Dlg_MessageBox.h"
48 #include "xap_UnixDlg_FileOpenSaveAs.h"
49 #include "xap_UnixApp.h"
50 #include "xap_Frame.h"
51 #include "xap_UnixFrameImpl.h"
52 #include "xap_Strings.h"
53 #include "xap_Prefs.h"
54 #include "ut_debugmsg.h"
55 #include "ut_string_class.h"
56 #include "ut_path.h"
57 #include "ut_png.h"
58 #include "ut_svg.h"
59 #include "ut_misc.h"
60 #include "fg_Graphic.h"
61 #include "fg_GraphicRaster.h"
62 #include "ie_impGraphic.h"
63
64 #include "gr_UnixImage.h"
65 #include "gr_Painter.h"
66 #include "gr_UnixCairoGraphics.h"
67 #include "ut_bytebuf.h"
68
69 #if defined(EMBEDDED_TARGET) && EMBEDDED_TARGET == EMBEDDED_TARGET_HILDON
70 #include <hildon/hildon-file-chooser-dialog.h>
71 #endif
72
73
74 #include <sys/stat.h>
75
76 #include "../../../wp/impexp/xp/ie_types.h"
77 #include "../../../wp/impexp/xp/ie_imp.h"
78 #include "../../../wp/impexp/xp/ie_exp.h"
79 #include "../../../wp/impexp/xp/ie_impGraphic.h"
80
81 #define PREVIEW_WIDTH 100
82 #define PREVIEW_HEIGHT 100
83
84
85
86 /*****************************************************************/
static_constructor(XAP_DialogFactory * pFactory,XAP_Dialog_Id id)87 XAP_Dialog * XAP_UnixDialog_FileOpenSaveAs::static_constructor(XAP_DialogFactory * pFactory,
88 XAP_Dialog_Id id)
89 {
90 XAP_UnixDialog_FileOpenSaveAs * p = new XAP_UnixDialog_FileOpenSaveAs(pFactory,id);
91 return p;
92 }
93
XAP_UnixDialog_FileOpenSaveAs(XAP_DialogFactory * pDlgFactory,XAP_Dialog_Id id)94 XAP_UnixDialog_FileOpenSaveAs::XAP_UnixDialog_FileOpenSaveAs(XAP_DialogFactory * pDlgFactory,
95 XAP_Dialog_Id id)
96 : XAP_Dialog_FileOpenSaveAs(pDlgFactory,id), m_FC(0), m_preview(0), m_bSave(true)
97 {
98 m_szFinalPathnameCandidate = NULL;
99 }
100
~XAP_UnixDialog_FileOpenSaveAs(void)101 XAP_UnixDialog_FileOpenSaveAs::~XAP_UnixDialog_FileOpenSaveAs(void)
102 {
103 FREEP(m_szFinalPathnameCandidate);
104 }
105
106 /*****************************************************************/
107
s_dialog_response(GtkWidget *,gint answer,XAP_Dialog_FileOpenSaveAs::tAnswer * ptr,bool bQuit=true)108 static void s_dialog_response(GtkWidget * /* widget */,
109 gint answer,
110 XAP_Dialog_FileOpenSaveAs::tAnswer * ptr, bool bQuit = true)
111 {
112 switch (answer)
113 {
114 case GTK_RESPONSE_CANCEL:
115 case GTK_RESPONSE_ACCEPT:
116 case GTK_RESPONSE_OK:
117 if (answer == GTK_RESPONSE_CANCEL)
118 *ptr = XAP_Dialog_FileOpenSaveAs::a_CANCEL;
119 else
120 *ptr = XAP_Dialog_FileOpenSaveAs::a_OK;
121 if (bQuit)
122 gtk_main_quit();
123 break;
124 default:
125 // do nothing
126 break;
127 }
128 }
129
dialog_response(GtkWidget * widget,gint answer,XAP_Dialog_FileOpenSaveAs::tAnswer * ptr)130 static void dialog_response(GtkWidget *widget,
131 gint answer,
132 XAP_Dialog_FileOpenSaveAs::tAnswer * ptr) {
133 s_dialog_response(widget, answer, ptr);
134 }
135
s_delete_clicked(GtkWidget *,GdkEvent *,gpointer data)136 static void s_delete_clicked(GtkWidget * /*widget*/,
137 GdkEvent * /*event*/,
138 gpointer data)
139 {
140 XAP_UnixDialog_FileOpenSaveAs *dlg = static_cast<XAP_UnixDialog_FileOpenSaveAs *>(data);
141 dlg->onDeleteCancel();
142 gtk_main_quit();
143 }
144
145 #if GTK_CHECK_VERSION(3,0,0)
s_preview_draw(GtkWidget *,cairo_t *,gpointer ptr)146 static gint s_preview_draw(GtkWidget * /* widget */,
147 cairo_t * /* cr */,
148 gpointer ptr)
149 #else
150 static gint s_preview_exposed(GtkWidget * /* widget */,
151 GdkEventExpose*,
152 gpointer ptr)
153 #endif
154 {
155 XAP_UnixDialog_FileOpenSaveAs * dlg = static_cast<XAP_UnixDialog_FileOpenSaveAs *> (ptr);
156 UT_ASSERT(dlg);
157 dlg->previewPicture();
158 return FALSE;
159 }
160
s_filetypechanged(GtkWidget * w,gpointer p)161 static void s_filetypechanged(GtkWidget * w, gpointer p)
162 {
163 XAP_UnixDialog_FileOpenSaveAs * dlg = static_cast<XAP_UnixDialog_FileOpenSaveAs *>(p);
164 dlg->fileTypeChanged(w);
165 }
166
167 static gint
fsel_key_event(GtkWidget * widget,GdkEventKey * event,XAP_Dialog_FileOpenSaveAs::tAnswer * answer)168 fsel_key_event (GtkWidget * widget, GdkEventKey * event, XAP_Dialog_FileOpenSaveAs::tAnswer * answer)
169 {
170 if (event->keyval == GDK_KEY_Escape) {
171 g_signal_stop_emission_by_name (G_OBJECT (widget), "key_press_event");
172 s_dialog_response(widget, GTK_RESPONSE_CANCEL, answer);
173 return TRUE;
174 }
175
176 return FALSE;
177 }
178
s_file_activated(GtkWidget * w,XAP_Dialog_FileOpenSaveAs::tAnswer * answer)179 static void s_file_activated(GtkWidget * w, XAP_Dialog_FileOpenSaveAs::tAnswer * answer)
180 {
181 // whenever the "file-activated" signal is called, it will also be followed
182 // (or preceded?) by a "response" signal. That "response" signal will manage
183 // the closing of the dialog for us. Now we don't want to close the dialog
184 // twice, hence the last 'false' parameter.
185 // Hardly elegant, but none of this code is :/ It fixes bug #11647 too - MARCM.
186 s_dialog_response(w, GTK_RESPONSE_ACCEPT, answer, false);
187 }
188
file_selection_changed(GtkTreeSelection *,gpointer ptr)189 static void file_selection_changed (GtkTreeSelection * /*selection*/,
190 gpointer ptr)
191 {
192 XAP_UnixDialog_FileOpenSaveAs * dlg = static_cast<XAP_UnixDialog_FileOpenSaveAs *> (ptr);
193
194 UT_ASSERT(dlg);
195 dlg->previewPicture();
196 }
197
_run_gtk_main(XAP_Frame * pFrame,GtkWidget * filetypes_pulldown)198 bool XAP_UnixDialog_FileOpenSaveAs::_run_gtk_main(XAP_Frame * pFrame,
199 GtkWidget * filetypes_pulldown)
200 {
201 /*
202 Run the dialog in a loop to catch bad filenames.
203 The location of this check being in this dialog loop
204 could be considered temporary. Doing this matches the Windows
205 common control behavior (where the dialog checks everything
206 for the programmer), but lacks flexibility for different
207 uses of this dialog (file export, print export, directory
208 (not file) selection).
209
210 This check might need to be moved into the ap code which calls
211 this dialog, and certain interfaces exposed so that the
212 dialog is displayed throughout the verification.
213
214 For right now you can signal this check on and off with
215 bCheckWritePermission.
216 */
217
218 char * szDialogFilename = NULL; // this is the file name returned from the dialog
219 char * szFinalPathname = NULL; // this is the file name after suffix addition, if any
220 char * szFinalPathnameCopy = NULL; // one to mangle when looking for dirs, etc.
221
222 char * pLastSlash;
223
224 // if m_bSave is not set, we're looking to OPEN a file.
225 // otherwise we are looking to SAVE a file.
226 if (!m_bSave)
227 {
228 while (1)
229 {
230 gtk_main();
231 if (m_answer == a_CANCEL) // The easy way out
232 return false;
233
234 m_szFinalPathnameCandidate = gtk_file_chooser_get_uri(m_FC);
235 UT_ASSERT(m_szFinalPathnameCandidate);
236 return (m_answer == a_OK);
237 }
238 }
239 else
240 {
241 while(1)
242 {
243 gtk_main();
244 if (m_answer == a_CANCEL) // The easy way out
245 return false;
246
247 // Give us a filename we can mangle
248
249 szDialogFilename = gtk_file_chooser_get_uri(m_FC);
250 if (!szDialogFilename)
251 continue;
252
253 // We append the suffix of the default type, so the user doesn't
254 // have to. This is adapted from the Windows front-end code
255 // (xap_Win32Dlg_FileOpenSaveAs.cpp), since it should act the same.
256 // If, however, the user doesn't want suffixes, they don't have to have them.
257 {
258 //UT_uint32 end = g_strv_length(m_szSuffixes);
259 UT_sint32 nFileType = XAP_comboBoxGetActiveInt(GTK_COMBO_BOX(filetypes_pulldown));
260
261 // set to first item, which should probably be auto detect
262 // TODO : "probably" isn't very good.
263 UT_uint32 nIndex = 0;
264
265 // the index in the types table will match the index in the suffix
266 // table. nFileType is the data we are searching for.
267 if(m_nTypeList != NULL)
268 {
269 for (UT_uint32 i = 0; m_nTypeList[i]; i++)
270 {
271 if (m_nTypeList[i] == nFileType)
272 {
273 nIndex = i;
274 break;
275 }
276 }
277 }
278
279 bool wantSuffix = true;
280 XAP_Prefs *pPrefs= XAP_App::getApp()->getPrefs();
281 pPrefs->getPrefsValueBool(static_cast<const gchar *>(XAP_PREF_KEY_UseSuffix), &wantSuffix);
282 UT_DEBUGMSG(("UseSuffix: %d\n", wantSuffix));
283
284 if (nFileType > 0 && getDialogId() != XAP_DIALOG_ID_FILE_SAVE_IMAGE) // 0 means autodetect
285 {
286 if (!UT_pathSuffix(szDialogFilename).empty())
287 {
288 // warn if we have a suffix that doesn't match the selected file type
289 IE_ExpSniffer* pSniffer = IE_Exp::snifferForFileType(m_nTypeList[nIndex]);
290 if (pSniffer && !pSniffer->recognizeSuffix(UT_pathSuffix(szDialogFilename).c_str()))
291 {
292 std::string msg;
293 const XAP_StringSet * pSS = m_pApp->getStringSet();
294 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_ExtensionDoesNotMatch, msg);
295 if (pFrame->showMessageBox(msg.c_str(), XAP_Dialog_MessageBox::b_YN, XAP_Dialog_MessageBox::a_NO) != XAP_Dialog_MessageBox::a_YES)
296 goto ContinueLoop;
297 }
298 szFinalPathname = g_strdup(szDialogFilename);
299 }
300 else if (wantSuffix)
301 {
302 // if the file doesn't have a suffix already, and the file type
303 // is normal (special types are negative, like auto detect),
304 // and the user wants extensions, slap a suffix on it.
305 // add suffix based on selected file type
306 // UT_UTF8String suffix (IE_Exp::preferredSuffixForFileType(m_nTypeList[nIndex]));
307 // UT_uint32 length = strlen(szDialogFilename) + suffix.size() + 1;
308
309 // szFinalPathname = static_cast<char *>(UT_calloc(length,sizeof(char)));
310
311 // if (szFinalPathname)
312 // {
313 // char * p = szFinalPathname;
314 // strcpy(p,szDialogFilename);
315 // strcat(p,suffix.utf8_str());
316 // }
317
318 std::string n = m_appendDefaultSuffixFunctor( szDialogFilename,
319 m_nTypeList[nIndex] );
320 szFinalPathname = g_strdup( n.c_str() );
321 }
322 else
323 szFinalPathname = g_strdup(szDialogFilename);
324 }
325 else
326 {
327 // the file type is special (auto detect)
328 // set to plain name, and let the auto detector in the
329 // exporter figure it out
330 szFinalPathname = g_strdup(szDialogFilename);
331 }
332
333 // g_free szDialogFilename since it's been put into szFinalPathname (with
334 // or without changes) and it's invalid (missing an extension which
335 // might have been appended)
336
337 FREEP(szDialogFilename);
338 }
339
340 szFinalPathnameCopy = g_strdup(szFinalPathname);
341
342 if (UT_go_file_exists(szFinalPathnameCopy))
343 {
344 // we have an existing file, ask to overwrite
345 if (_askOverwrite_YesNo(pFrame, szFinalPathname))
346 {
347 m_szFinalPathnameCandidate = g_strdup(szFinalPathname);
348 goto ReturnTrue;
349 }
350
351 goto ContinueLoop;
352 }
353
354 // We have a string that may contain a path, and may have a file
355 // at the end. First, strip off a file (if it exists), and test
356 // for a matching directory. We can then proceed with the file
357 // if another stat of that dir passes.
358
359 if (szFinalPathnameCopy && strlen(szFinalPathnameCopy))
360 pLastSlash = strrchr(szFinalPathnameCopy,'/');
361 else
362 pLastSlash = NULL;
363
364 if (!pLastSlash)
365 {
366 _notifyError_OKOnly(pFrame,XAP_STRING_ID_DLG_InvalidPathname);
367 goto ContinueLoop;
368 }
369
370 m_szFinalPathnameCandidate = g_strdup(szFinalPathname);
371 goto ReturnTrue;
372
373 // complain about write permission on the directory.
374 // lop off ugly trailing slash only if we don't have
375 // the root dir ('/') for a path
376
377 if (pLastSlash > szFinalPathnameCopy)
378 *pLastSlash = 0;
379
380 _notifyError_OKOnly(pFrame,XAP_STRING_ID_DLG_NoSaveFile_DirNotWriteable,
381 szFinalPathname);
382 ContinueLoop:
383 FREEP(szFinalPathnameCopy);
384 }
385 } /* if m_bSave */
386
387 /*NOTREACHED*/
388
389 ReturnTrue:
390 FREEP(szFinalPathnameCopy);
391 FREEP(szFinalPathname);
392 return true;
393 }
394
395
_askOverwrite_YesNo(XAP_Frame * pFrame,const char * fileName)396 bool XAP_UnixDialog_FileOpenSaveAs::_askOverwrite_YesNo(XAP_Frame * pFrame, const char * fileName)
397 {
398 return (pFrame->showMessageBox(XAP_STRING_ID_DLG_OverwriteFile,
399 XAP_Dialog_MessageBox::b_YN,
400 XAP_Dialog_MessageBox::a_NO, // should this be YES?
401 fileName)
402 == XAP_Dialog_MessageBox::a_YES);
403 }
404
_notifyError_OKOnly(XAP_Frame * pFrame,XAP_String_Id sid)405 void XAP_UnixDialog_FileOpenSaveAs::_notifyError_OKOnly(XAP_Frame * pFrame, XAP_String_Id sid)
406 {
407 pFrame->showMessageBox(sid,
408 XAP_Dialog_MessageBox::b_O,
409 XAP_Dialog_MessageBox::a_OK);
410 }
411
_notifyError_OKOnly(XAP_Frame * pFrame,XAP_String_Id sid,const char * sz1)412 void XAP_UnixDialog_FileOpenSaveAs::_notifyError_OKOnly(XAP_Frame * pFrame,
413 XAP_String_Id sid,
414 const char * sz1)
415 {
416 pFrame->showMessageBox(sid,
417 XAP_Dialog_MessageBox::b_O,
418 XAP_Dialog_MessageBox::a_OK,
419 sz1);
420 }
421
fileTypeChanged(GtkWidget * w)422 void XAP_UnixDialog_FileOpenSaveAs::fileTypeChanged(GtkWidget * w)
423 {
424 if (!m_bSave)
425 return;
426
427 UT_sint32 nFileType = XAP_comboBoxGetActiveInt(GTK_COMBO_BOX(w));
428 UT_DEBUGMSG(("File type widget is %p filetype number is %d \n",w,nFileType));
429 // I have no idea for 0, but XAP_DIALOG_FILEOPENSAVEAS_FILE_TYPE_AUTO
430 // definitely means "skip this"
431 if((nFileType == 0) || (nFileType == XAP_DIALOG_FILEOPENSAVEAS_FILE_TYPE_AUTO))
432 {
433 return;
434 }
435
436 gchar * filename = gtk_file_chooser_get_filename(m_FC);
437 UT_String sFileName = filename;
438 FREEP(filename);
439
440 UT_String sSuffix = m_szSuffixes[nFileType-1];
441 sSuffix = sSuffix.substr(1,sSuffix.length()-1);
442 UT_sint32 i = 0;
443 bool bFoundComma = false;
444 for(i=0; i< static_cast<UT_sint32>(sSuffix.length()); i++)
445 {
446 if(sSuffix[i] == ';')
447 {
448 bFoundComma = true;
449 break;
450 }
451 }
452 if(bFoundComma)
453 {
454 sSuffix = sSuffix.substr(0,i);
455 }
456
457 //
458 // Hard code a suffix
459 //
460 if(strstr(sSuffix.c_str(),"gz") != NULL)
461 {
462 sSuffix = ".zabw";
463 }
464 bool bFoundSuffix = false;
465 for(i= sFileName.length()-1; i> 0; i--)
466 {
467 if(sFileName[i] == '.')
468 {
469 bFoundSuffix = true;
470 break;
471 }
472 }
473 if(!bFoundSuffix)
474 {
475 return;
476 }
477 sFileName = sFileName.substr(0,i);
478 sFileName += sSuffix;
479
480 gtk_file_chooser_set_current_name(m_FC, UT_basename(sFileName.c_str()));
481 }
482
onDeleteCancel()483 void XAP_UnixDialog_FileOpenSaveAs::onDeleteCancel()
484 {
485 if (m_FC != NULL && gtk_widget_has_grab(GTK_WIDGET (m_FC))) {
486 gtk_grab_remove (GTK_WIDGET (m_FC));
487 }
488 m_FC = NULL;
489 m_answer = a_CANCEL;
490 }
491
492 /*****************************************************************/
493
runModal(XAP_Frame * pFrame)494 void XAP_UnixDialog_FileOpenSaveAs::runModal(XAP_Frame * pFrame)
495 {
496 const XAP_StringSet * pSS = m_pApp->getStringSet();
497 std::string szTitle;
498 std::string szFileTypeLabel;
499
500 switch (m_id)
501 {
502 case XAP_DIALOG_ID_INSERT_PICTURE:
503 {
504 pSS->getValueUTF8(XAP_STRING_ID_DLG_IP_Title, szTitle);
505 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_FileOpenTypeLabel, szFileTypeLabel);
506 m_bSave = false;
507 break;
508 }
509 case XAP_DIALOG_ID_FILE_OPEN:
510 {
511 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_OpenTitle,szTitle);
512 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_FileOpenTypeLabel,szFileTypeLabel);
513 m_bSave = false;
514 break;
515 }
516 case XAP_DIALOG_ID_FILE_IMPORT:
517 {
518 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_ImportTitle,szTitle);
519 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_FileOpenTypeLabel,szFileTypeLabel);
520 m_bSave = false;
521 break;
522 }
523 case XAP_DIALOG_ID_INSERTMATHML:
524 {
525 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_InsertMath,szTitle);
526 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_FileInsertMath,szFileTypeLabel);
527 m_bSave = false;
528 break;
529 }
530 case XAP_DIALOG_ID_INSERTOBJECT:
531 {
532 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_InsertObject,szTitle);
533 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_FileInsertObject,szFileTypeLabel);
534 m_bSave = false;
535 break;
536 }
537 case XAP_DIALOG_ID_INSERT_FILE:
538 {
539 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_InsertTitle,szTitle);
540 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_FileOpenTypeLabel,szFileTypeLabel);
541 m_bSave = false;
542 break;
543 }
544 case XAP_DIALOG_ID_FILE_SAVEAS:
545 case XAP_DIALOG_ID_FILE_SAVE_IMAGE:
546 {
547 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_SaveAsTitle,szTitle);
548 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_FileSaveTypeLabel,szFileTypeLabel);
549 m_bSave = true;
550 break;
551 }
552 case XAP_DIALOG_ID_FILE_EXPORT:
553 {
554 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_ExportTitle,szTitle);
555 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_FileSaveTypeLabel,szFileTypeLabel);
556 m_bSave = true;
557 break;
558 }
559 case XAP_DIALOG_ID_PRINTTOFILE:
560 {
561 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_PrintToFileTitle,szTitle);
562 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_FilePrintTypeLabel,szFileTypeLabel);
563 m_bSave = true;
564 break;
565 }
566 case XAP_DIALOG_ID_RECORDTOFILE:
567 {
568 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_RecordToFileTitle,szTitle);
569 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_RecordToFileLabel,szFileTypeLabel);
570 m_bSave = true;
571 break;
572 }
573 case XAP_DIALOG_ID_REPLAYFROMFILE:
574 {
575 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_ReplayFromFileTitle,szTitle);
576 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_ReplayFromFileLabel,szFileTypeLabel);
577 m_bSave = false;
578 break;
579 }
580 default:
581 UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
582 m_bSave = false;
583 break;
584 }
585
586 // NOTE: we use our string mechanism to localize the dialog's
587 // NOTE: title and the error/confirmation message boxes. we
588 // NOTE: let GTK take care of the localization of the actual
589 // NOTE: buttons and labels on the FileSelection dialog.
590
591 // Get the GtkWindow of the parent frame
592 XAP_UnixFrameImpl * pUnixFrameImpl = static_cast<XAP_UnixFrameImpl *>(pFrame->getFrameImpl());
593 GtkWidget * parent = pUnixFrameImpl->getTopLevelWindow();
594
595 if(parent && (gtk_widget_is_toplevel(parent) != TRUE))
596 {
597 parent = gtk_widget_get_toplevel (parent);
598 }
599
600 #if defined(EMBEDDED_TARGET) && EMBEDDED_TARGET == EMBEDDED_TARGET_HILDON
601 m_FC = GTK_FILE_CHOOSER( hildon_file_chooser_dialog_new(GTK_WINDOW(parent),
602 (!m_bSave ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE))
603 );
604 #else
605 m_FC = GTK_FILE_CHOOSER( gtk_file_chooser_dialog_new (szTitle.c_str(),
606 GTK_WINDOW(parent),
607 (!m_bSave ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE),
608 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
609 (m_bSave ? GTK_STOCK_SAVE : GTK_STOCK_OPEN), GTK_RESPONSE_ACCEPT,
610 (gchar*)NULL)
611 );
612 #endif
613
614 gtk_file_chooser_set_local_only(m_FC, FALSE);
615
616 abiSetupModalDialog(GTK_DIALOG(m_FC), pFrame, this, GTK_RESPONSE_ACCEPT);
617 GtkWidget * filetypes_pulldown = NULL;
618
619 std::string s;
620
621 /*
622 Add a drop-down list of known types to facilitate a file-types selection.
623 We store an indexer in the user data for each menu item in the popup, so
624 we can read the type we need to return.
625 */
626
627 if (m_id == XAP_DIALOG_ID_INSERT_PICTURE)
628 {
629 GtkWidget * preview = createDrawingArea ();
630 gtk_widget_show (preview);
631 m_preview = preview;
632 gtk_widget_set_size_request (preview, PREVIEW_WIDTH, PREVIEW_HEIGHT);
633
634 // place the preview area inside a container to get a nice border
635 GtkWidget * preview_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
636 gtk_container_set_border_width (GTK_CONTAINER(preview_hbox), 4);
637 gtk_box_pack_start(GTK_BOX(preview_hbox), preview, TRUE, TRUE, 0);
638
639 // attach the preview area to the dialog
640 gtk_file_chooser_set_preview_widget (m_FC, preview_hbox);
641 gtk_file_chooser_set_preview_widget_active (m_FC, true);
642
643 // connect some signals
644 g_signal_connect (m_FC, "update_preview",
645 G_CALLBACK (file_selection_changed), static_cast<gpointer>(this));
646
647 #if GTK_CHECK_VERSION(3,0,0)
648 g_signal_connect (preview, "draw",
649 G_CALLBACK (s_preview_draw), static_cast<gpointer>(this));
650 #else
651 g_signal_connect (preview, "expose_event",
652 G_CALLBACK (s_preview_exposed), static_cast<gpointer>(this));
653 #endif
654 }
655
656 #if defined(EMBEDDED_TARGET) && EMBEDDED_TARGET == EMBEDDED_TARGET_HILDON
657 filetypes_pulldown = gtk_combo_box_new();
658 gtk_widget_show(filetypes_pulldown);
659 GtkWidget * pulldown_hbox = filetypes_pulldown;
660 #else
661 // hbox for our pulldown menu (GTK does its pulldown this way */
662 GtkWidget * pulldown_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 15);
663 gtk_widget_show(pulldown_hbox);
664
665 // pulldown label
666 GtkWidget * filetypes_label = gtk_label_new_with_mnemonic(convertMnemonics(szFileTypeLabel).c_str());
667 gtk_label_set_justify(GTK_LABEL(filetypes_label), GTK_JUSTIFY_RIGHT);
668 gtk_misc_set_alignment(GTK_MISC(filetypes_label), 1.0, 0.5);
669 gtk_widget_show(filetypes_label);
670 gtk_box_pack_start(GTK_BOX(pulldown_hbox), filetypes_label, TRUE, TRUE, 0);
671
672 // pulldown menu
673 filetypes_pulldown = gtk_combo_box_new();
674 gtk_widget_show(filetypes_pulldown);
675 gtk_box_pack_end(GTK_BOX(pulldown_hbox), filetypes_pulldown, TRUE, TRUE, 0);
676 gtk_label_set_mnemonic_widget(GTK_LABEL(filetypes_label), filetypes_pulldown);
677 #endif
678 //
679 // add the filters to the dropdown list
680 //
681 GtkComboBox* combo = GTK_COMBO_BOX(filetypes_pulldown);
682 XAP_makeGtkComboBoxText(combo, G_TYPE_INT);
683
684 // Auto-detect is always an option, but a special one, so we use
685 // a pre-defined constant for the type, and don't use the user-supplied
686 // types yet.
687 pSS->getValueUTF8(XAP_STRING_ID_DLG_FOSA_FileTypeAutoDetect,s);
688 XAP_appendComboBoxTextAndInt(combo, s.c_str(), XAP_DIALOG_FILEOPENSAVEAS_FILE_TYPE_AUTO);
689
690 UT_sint32 activeItemIndex = -1;
691
692 // add list items
693 {
694 UT_ASSERT(g_strv_length((gchar **) m_szSuffixes) == g_strv_length((gchar **) m_szDescriptions));
695
696 // measure one list, they should all be the same length
697 UT_uint32 end = g_strv_length((gchar **) m_szDescriptions);
698
699 for (UT_uint32 i = 0; i < end; i++)
700 {
701 // If this type is default, save its index (i) for later use
702 if (m_nTypeList[i] == m_nDefaultFileType)
703 activeItemIndex = i;
704
705 XAP_appendComboBoxTextAndInt(combo, m_szDescriptions[i], m_nTypeList[i]);
706 //
707 // Attach a callback when it is activated to change the file suffix
708 //
709 // g_signal_connect(G_OBJECT(thismenuitem), "activate",
710 // G_CALLBACK(s_filetypechanged),
711 // reinterpret_cast<gpointer>(this));
712 }
713 }
714
715 m_wFileTypes_PullDown = filetypes_pulldown;
716 // dialog; open dialog always does auto-detect
717 // TODO: should this also apply to the open dialog?
718 if (m_id == XAP_DIALOG_ID_FILE_SAVEAS
719 || m_id == XAP_DIALOG_ID_FILE_SAVE_IMAGE
720 || ( m_id == XAP_DIALOG_ID_FILE_EXPORT && activeItemIndex >= 0 )
721 )
722 {
723 gtk_combo_box_set_active(combo, activeItemIndex + 1);
724 }
725 else
726 {
727 gtk_combo_box_set_active(combo, 0);
728 }
729
730 #if defined(EMBEDDED_TARGET) && EMBEDDED_TARGET == EMBEDDED_TARGET_HILDON
731 hildon_file_chooser_dialog_add_extra ((HildonFileChooserDialog*)m_FC,
732 pulldown_hbox);
733 #else
734 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(m_FC), pulldown_hbox);
735 #endif
736
737 // connect the signals for OK and CANCEL and the requisite clean-close signals
738 g_signal_connect(G_OBJECT(m_FC),
739 "delete-event",
740 G_CALLBACK(s_delete_clicked),
741 this);
742
743 g_signal_connect(G_OBJECT(m_FC),
744 "key_press_event",
745 G_CALLBACK(fsel_key_event), &m_answer);
746
747 g_signal_connect (G_OBJECT (m_FC),
748 "response",
749 G_CALLBACK(dialog_response), &m_answer);
750
751 g_signal_connect (G_OBJECT (m_FC),
752 "file-activated",
753 G_CALLBACK(s_file_activated), &m_answer);
754
755 g_signal_connect(G_OBJECT(filetypes_pulldown), "changed",
756 G_CALLBACK(s_filetypechanged),
757 reinterpret_cast<gpointer>(this));
758
759 // use the persistence info and/or the suggested filename
760 // to properly seed the dialog.
761
762 gchar * szPersistDirectory = NULL; // we must g_free this
763
764 if (!m_szInitialPathname || !*m_szInitialPathname)
765 {
766 // the caller did not supply initial pathname
767 // (or supplied an empty one). see if we have
768 // some persistent info.
769
770 UT_ASSERT(!m_bSuggestName);
771 if (m_szPersistPathname)
772 {
773 // we have a pathname from a previous use,
774 // extract the directory portion and start
775 // the dialog there (but without a filename).
776
777 szPersistDirectory = UT_go_dirname_from_uri(m_szPersistPathname, FALSE);
778 gtk_file_chooser_set_current_folder_uri(m_FC, szPersistDirectory);
779 }
780 else
781 {
782 // no initial pathname given and we don't have
783 // a pathname from a previous use, so just let
784 // it come up in the current working directory.
785 }
786 }
787 else
788 {
789 // we have an initial pathname (the name of the document
790 // in the frame that we were invoked on). if the caller
791 // wanted us to suggest a filename, use the initial
792 // pathname as is. if not, use the directory portion of
793 // it.
794
795 if (m_bSuggestName)
796 {
797 xxx_UT_DEBUGMSG(("Iniitial filename is %s \n",m_szInitialPathname));
798 #if 0
799 if (!g_path_is_absolute (m_szInitialPathname)) { // DAL: todo: is this correct?
800 gchar *dir = g_get_current_dir ();
801 gchar *file = m_szInitialPathname;
802 gchar *filename = g_build_filename (dir, file, (gchar *)NULL);
803 m_szInitialPathname = UT_go_filename_to_uri(filename);
804 g_free(filename);
805 g_free (dir);
806 g_free (file);
807 }
808 #endif
809 if(m_id == XAP_DIALOG_ID_FILE_SAVEAS)
810 {
811 std::string szInitialSuffix = UT_pathSuffix(m_szInitialPathname);
812 std::string szSaveTypeSuffix = IE_Exp::preferredSuffixForFileType(m_nDefaultFileType).utf8_str();
813 if(!szInitialSuffix.empty() && !szSaveTypeSuffix.empty()
814 && (szSaveTypeSuffix != szInitialSuffix))
815 {
816 std::string sFileName = m_szInitialPathname;
817 std::string::size_type i = sFileName.find_last_of('.');
818
819 if(i != std::string::npos)
820 {
821 // erase to the end()
822 sFileName.erase(i);
823 sFileName += szSaveTypeSuffix;
824 FREEP(m_szInitialPathname);
825 m_szInitialPathname = g_strdup(sFileName.c_str());
826 }
827 }
828 }
829 if (UT_go_path_is_uri(m_szInitialPathname) || UT_go_path_is_path(m_szInitialPathname))
830 {
831 gtk_file_chooser_set_uri(m_FC, m_szInitialPathname);
832 }
833 }
834 else
835 {
836 if (UT_go_path_is_uri(m_szInitialPathname) || UT_go_path_is_path(m_szInitialPathname))
837 {
838 szPersistDirectory = UT_go_dirname_from_uri(m_szInitialPathname, FALSE);
839 gtk_file_chooser_set_current_folder_uri(m_FC, szPersistDirectory);
840 }
841 else
842 {
843 // we are dealing with a plain filename, not an URI or path, so
844 // just let it come up in the current working directory.
845 }
846 }
847 }
848
849 // center the dialog
850 xxx_UT_DEBUGMSG(("before center IS WIDGET_TOP_LEVL %d \n",(GTK_WIDGET_TOPLEVEL(parent))));
851 xxx_UT_DEBUGMSG(("before center IS WIDGET WINDOW %d \n",(GTK_IS_WINDOW(parent))));
852 centerDialog(parent, GTK_WIDGET(m_FC));
853 xxx_UT_DEBUGMSG(("After center IS WIDGET WINDOW %d \n",(GTK_IS_WINDOW(parent))));
854
855 gtk_widget_show(GTK_WIDGET(m_FC));
856 gtk_grab_add(GTK_WIDGET(m_FC));
857
858 bool bResult = _run_gtk_main(pFrame,filetypes_pulldown);
859
860 if (bResult)
861 {
862 UT_ASSERT(m_szFinalPathnameCandidate);
863
864 // store final path name and file type
865 m_szFinalPathname = g_strdup(m_szFinalPathnameCandidate);
866
867 FREEP(m_szFinalPathnameCandidate);
868
869 // what a long ugly line of code
870 m_nFileType = XAP_comboBoxGetActiveInt(GTK_COMBO_BOX(filetypes_pulldown));
871 }
872
873 if (m_FC != NULL) {
874 gtk_grab_remove (GTK_WIDGET(m_FC));
875 gtk_widget_destroy (GTK_WIDGET(m_FC));
876 m_FC = NULL;
877 FREEP(szPersistDirectory);
878 }
879
880 return;
881 }
882
previewPicture(void)883 gint XAP_UnixDialog_FileOpenSaveAs::previewPicture (void)
884 {
885 UT_ASSERT (m_FC && m_preview);
886
887 UT_ASSERT(XAP_App::getApp());
888
889 const XAP_StringSet * pSS = m_pApp->getStringSet();
890 UT_return_val_if_fail( pSS, 0 );
891
892 /* do not try to scale an image to a 1x1 pibuf, this might happen when
893 the widget size has notbeen allocated or if there is no room for it,
894 that might freeze gdk_pixbuf */
895 GtkAllocation allocation;
896 gtk_widget_get_allocation (m_preview, &allocation);
897 if (allocation.width <= 1)
898 return 0;
899
900 // attach and clear the area immediately
901 GR_UnixCairoAllocInfo ai(m_preview);
902 GR_CairoGraphics* pGr =
903 (GR_CairoGraphics*) XAP_App::getApp()->newGraphics(ai);
904
905 const gchar * file_name = gtk_file_chooser_get_uri (m_FC);
906
907 GR_Font * fnt = pGr->findFont("Times New Roman",
908 "normal", "", "normal",
909 "", "12pt",
910 pSS->getLanguageName());
911 pGr->setFont(fnt);
912
913 std::string s;
914 pSS->getValueUTF8(XAP_STRING_ID_DLG_IP_No_Picture_Label, s);
915 UT_UTF8String str(s);
916
917 int answer = 0;
918
919 FG_Graphic * pGraphic = 0;
920 GR_Image *pImage = NULL;
921
922 double scale_factor = 0.0;
923 UT_sint32 scaled_width,scaled_height;
924 UT_sint32 iImageWidth,iImageHeight;
925
926 {
927 GR_Painter painter(pGr);
928 GtkAllocation alloc;
929 gtk_widget_get_allocation(m_preview, &alloc);
930 painter.clearArea(0, 0, pGr->tlu(alloc.width), pGr->tlu(alloc.height));
931
932 if (!file_name)
933 {
934 painter.drawChars (str.ucs4_str().ucs4_str(), 0, str.size(), pGr->tlu(12), pGr->tlu(static_cast<int>(alloc.height / 2)) - pGr->getFontHeight(fnt)/2);
935 goto Cleanup;
936 }
937
938 // are we dealing with a file or directory here?
939 struct stat st;
940 if (!stat (file_name, &st))
941 {
942 if (!S_ISREG(st.st_mode))
943 {
944 painter.drawChars (str.ucs4_str().ucs4_str(), 0, str.size(), pGr->tlu(12), pGr->tlu(static_cast<int>(alloc.height / 2)) - pGr->getFontHeight(fnt)/2);
945 goto Cleanup;
946 }
947 }
948
949 GsfInput * input = NULL;
950 UT_DEBUGMSG(("file_name %s \n",file_name));
951 input = UT_go_file_open (file_name, NULL);
952 if (!input)
953 goto Cleanup;
954 char Buf[4097] = ""; // 4096+nul ought to be enough
955 UT_uint32 iNumbytes = UT_MIN(4096, gsf_input_size(input));
956 gsf_input_read(input, iNumbytes, (guint8 *)(Buf));
957 Buf[iNumbytes] = '\0';
958
959 IEGraphicFileType ief = IE_ImpGraphic::fileTypeForContents(Buf,4096);
960 if((ief == IEGFT_Unknown) || (ief == IEGFT_Bogus))
961 {
962 painter.drawChars (str.ucs4_str().ucs4_str(), 0, str.size(), pGr->tlu(12), pGr->tlu(static_cast<int>(alloc.height / 2)) - pGr->getFontHeight(fnt)/2);
963 g_object_unref (G_OBJECT (input));
964 goto Cleanup;
965 }
966 g_object_unref (G_OBJECT (input));
967 input = UT_go_file_open (file_name, NULL);
968 size_t num_bytes = gsf_input_size(input);
969 UT_Byte * bytes = (UT_Byte *) gsf_input_read(input, num_bytes,NULL );
970 if(bytes == NULL)
971 {
972 painter.drawChars (str.ucs4_str().ucs4_str(), 0, str.size(), pGr->tlu(12), pGr->tlu(static_cast<int>(alloc.height / 2)) - pGr->getFontHeight(fnt)/2);
973 g_object_unref (G_OBJECT (input));
974 goto Cleanup;
975 }
976 UT_ByteBuf * pBB = new UT_ByteBuf();
977 pBB->append(bytes,num_bytes);
978 g_object_unref (G_OBJECT (input));
979 //
980 // OK load the data into a GdkPixbuf
981 //
982 // Jean: comented out next line (and an other one below), we don't use it
983 // bool bLoadFailed = false;
984 //
985 GdkPixbuf * pixbuf = pixbufForByteBuf ( pBB);
986 delete pBB;
987 if(pixbuf == NULL)
988 {
989 //
990 // Try a fallback loader here.
991 //
992 painter.drawChars (str.ucs4_str().ucs4_str(), 0, str.size(), pGr->tlu(12), pGr->tlu(static_cast<int>(alloc.height / 2)) - pGr->getFontHeight(fnt)/2);
993 // bLoadFailed = true;
994 goto Cleanup;
995 }
996
997 pImage = new GR_UnixImage(NULL,pixbuf);
998
999 iImageWidth = gdk_pixbuf_get_width (pixbuf);
1000 iImageHeight = gdk_pixbuf_get_height (pixbuf);
1001 if (alloc.width >= iImageWidth && alloc.height >= iImageHeight)
1002 scale_factor = 1.0;
1003 else
1004 scale_factor = MIN( static_cast<double>(alloc.width)/iImageWidth,
1005 static_cast<double>(alloc.height)/iImageHeight);
1006
1007 scaled_width = static_cast<int>(scale_factor * iImageWidth);
1008 scaled_height = static_cast<int>(scale_factor * iImageHeight);
1009
1010 static_cast<GR_UnixImage *>(pImage)->scale(scaled_width,scaled_height);
1011 painter.drawImage(pImage,
1012 pGr->tlu(static_cast<int>((alloc.width - scaled_width ) / 2)),
1013 pGr->tlu(static_cast<int>((alloc.height - scaled_height) / 2)));
1014
1015 answer = 1;
1016 }
1017
1018 Cleanup:
1019 FREEP(file_name);
1020 DELETEP(pImage);
1021 DELETEP(pGr);
1022 DELETEP(pGraphic);
1023
1024 return answer;
1025 }
1026
_loadXPM(UT_ByteBuf * pBB)1027 GdkPixbuf * XAP_UnixDialog_FileOpenSaveAs::_loadXPM(UT_ByteBuf * pBB)
1028 {
1029 GdkPixbuf * pixbuf = NULL;
1030 const char * pBC = reinterpret_cast<const char *>(pBB->getPointer(0));
1031
1032 UT_GenericVector<char*> vecStr;
1033 UT_sint32 k =0;
1034 UT_sint32 iBase =0;
1035
1036 //
1037 // Find dimension line to start with.
1038 //
1039 UT_sint32 length = static_cast<UT_sint32>(pBB->getLength());
1040 for(k =0; (*(pBC+k) != '"') &&( k < length); k++)
1041 ;
1042
1043 if(k >= length)
1044 {
1045 return NULL;
1046 }
1047
1048 k++;
1049 iBase = k;
1050 for( ; (*(pBC+k) != '"') && (k < length); k++)
1051 ;
1052 if(k >= length)
1053 {
1054 return NULL;
1055 }
1056
1057 char * sz = NULL;
1058 UT_sint32 kLen = k-iBase+1;
1059 sz = static_cast<char *>(UT_calloc(kLen,sizeof(char)));
1060 UT_sint32 i =0;
1061
1062 for(i=0; i< (kLen -1); i++)
1063 {
1064 *(sz+i) = *(pBC+iBase+i);
1065 }
1066 *(sz+i) = 0;
1067 vecStr.addItem(sz);
1068
1069 //
1070 // Now loop through all the lines until we get to "}" outside the
1071 // '"'
1072 while((*(pBC+k) != '}') && (k < length) )
1073 {
1074 k++;
1075
1076 //
1077 // Load a single string of data into our vector.
1078 //
1079 if(*(pBC+k) =='"')
1080 {
1081 //
1082 // Start of a line
1083 //
1084 k++;
1085 iBase = k;
1086 for( ; (*(pBC+k) != '"') && (k < length); k++) {}
1087 if(k >= length)
1088 {
1089 return NULL;
1090 }
1091 sz = NULL;
1092 kLen = k-iBase+1;
1093 sz = static_cast<char *>(UT_calloc(kLen,sizeof(char)));
1094 for(i=0; i<(kLen -1); i++)
1095 {
1096 *(sz+i) = *(pBC+iBase+i);
1097 }
1098 *(sz +i) = 0;
1099 vecStr.addItem(sz);
1100 }
1101 }
1102
1103 if(k >= length)
1104 {
1105 for(i=0; i<vecStr.getItemCount(); i++)
1106 {
1107 char * psz = vecStr.getNthItem(i);
1108 FREEP(psz);
1109 }
1110 return NULL;
1111 }
1112
1113 const char ** pszStr = static_cast<const char **>(UT_calloc(vecStr.getItemCount(),sizeof(char *)));
1114 for(i=0; i<vecStr.getItemCount(); i++)
1115 pszStr[i] = vecStr.getNthItem(i);
1116 pixbuf = gdk_pixbuf_new_from_xpm_data(pszStr);
1117 DELETEP(pszStr);
1118 return pixbuf;
1119 }
1120
pixbufForByteBuf(UT_ByteBuf * pBB)1121 GdkPixbuf * XAP_UnixDialog_FileOpenSaveAs::pixbufForByteBuf (UT_ByteBuf * pBB)
1122 {
1123 if ( !pBB || !pBB->getLength() )
1124 return NULL;
1125
1126 GdkPixbuf * pixbuf = NULL;
1127
1128 bool bIsXPM = false;
1129 const char * szBuf = reinterpret_cast<const char *>(pBB->getPointer(0));
1130 if((pBB->getLength() > 9) && (strncmp (szBuf, "/* XPM */", 9) == 0))
1131 {
1132 bIsXPM = true;
1133 }
1134
1135 if(bIsXPM)
1136 {
1137 pixbuf = _loadXPM(pBB);
1138 }
1139 else
1140 {
1141 GError * err = 0;
1142 GdkPixbufLoader * ldr = 0;
1143
1144 ldr = gdk_pixbuf_loader_new ();
1145 if (!ldr)
1146 {
1147 UT_DEBUGMSG (("GdkPixbuf: couldn't create loader! WTF?\n"));
1148 UT_ASSERT (ldr);
1149 return NULL ;
1150 }
1151
1152 if ( FALSE== gdk_pixbuf_loader_write (ldr, static_cast<const guchar *>(pBB->getPointer (0)),
1153 static_cast<gsize>(pBB->getLength ()), &err) )
1154 {
1155 UT_DEBUGMSG(("DOM: couldn't write to loader: %s\n", err->message));
1156 g_error_free(err);
1157 gdk_pixbuf_loader_close (ldr, NULL);
1158 g_object_unref (G_OBJECT(ldr));
1159 return NULL ;
1160 }
1161
1162 gdk_pixbuf_loader_close (ldr, NULL);
1163 pixbuf = gdk_pixbuf_loader_get_pixbuf (ldr);
1164
1165 // ref before closing the loader
1166 if ( pixbuf )
1167 g_object_ref (G_OBJECT(pixbuf));
1168
1169 g_object_unref (G_OBJECT(ldr));
1170 }
1171
1172 return pixbuf;
1173 }
1174