Code

8a1fc492ed461eb529c29093198ac70ad0073e99
[inkscape.git] / src / ui / dialog / filedialog.cpp
1 /*
2  * Implementation of the file dialog interfaces defined in filedialog.h
3  *
4  * Authors:
5  *   Bob Jamison
6  *   Other dudes from The Inkscape Organization
7  *
8  * Copyright (C) 2004 The Inkscape Organization
9  *
10  * Released under GNU GPL, read the file 'COPYING' for more information
11  */
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 #endif
19 //Temporary ugly hack
20 //Remove these after the get_filter() calls in
21 //show() on both classes are fixed
22 #include <gtk/gtkfilechooser.h>
24 //Another hack
25 #include <gtk/gtkentry.h>
26 #include <gtk/gtkexpander.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <glibmm/i18n.h>
31 #include <gtkmm/box.h>
32 #include <gtkmm/filechooserdialog.h>
33 #include <gtkmm/menubar.h>
34 #include <gtkmm/menu.h>
35 #include <gtkmm/entry.h>
36 #include <gtkmm/expander.h>
37 #include <gtkmm/comboboxtext.h>
38 #include <gtkmm/stock.h>
39 #include <gdkmm/pixbuf.h>
41 #include "prefs-utils.h"
42 #include <dialogs/dialog-events.h>
43 #include <extension/input.h>
44 #include <extension/output.h>
45 #include <extension/db.h>
46 #include "inkscape.h"
47 #include "svg-view-widget.h"
48 #include "filedialog.h"
49 #include "gc-core.h"
51 #undef INK_DUMP_FILENAME_CONV
53 #ifdef INK_DUMP_FILENAME_CONV
54 void dump_str( const gchar* str, const gchar* prefix );
55 void dump_ustr( const Glib::ustring& ustr );
56 #endif
58 namespace Inkscape
59 {
60 namespace UI
61 {
62 namespace Dialog
63 {
69 //########################################################################
70 //### U T I L I T Y
71 //########################################################################
73 /**
74     \brief  A quick function to turn a standard extension into a searchable
75             pattern for the file dialogs
76     \param  pattern  The patter that the extension should be written to
77     \param  in_file_extension  The C string that represents the extension
79     This function just goes through the string, and takes all characters
80     and puts a [<upper><lower>] so that both are searched and shown in
81     the file dialog.  This function edits the pattern string to make
82     this happen.
83 */
84 static void
85 fileDialogExtensionToPattern(Glib::ustring &pattern,
86                       Glib::ustring &extension)
87 {
88     for (unsigned int i = 0; i < extension.length(); i++ )
89         {
90         Glib::ustring::value_type ch = extension[i];
91         if ( Glib::Unicode::isalpha(ch) )
92             {
93             pattern += '[';
94             pattern += Glib::Unicode::toupper(ch);
95             pattern += Glib::Unicode::tolower(ch);
96             pattern += ']';
97             }
98         else
99             {
100             pattern += ch;
101             }
102         }
106 /**
107  *  Hack:  Find all entry widgets in a container
108  */
109 void findEntryWidgets(Gtk::Container *parent,
110                       std::vector<Gtk::Entry *> &result)
112     if (!parent)
113         return;
114     std::vector<Gtk::Widget *> children = parent->get_children();
115     for (unsigned int i=0; i<children.size() ; i++)
116         {
117         Gtk::Widget *child = children[i];
118         GtkWidget *wid = child->gobj();
119         if (GTK_IS_ENTRY(wid))
120            result.push_back((Gtk::Entry *)child);
121         else if (GTK_IS_CONTAINER(wid))
122             findEntryWidgets((Gtk::Container *)child, result);
123         }
130 /**
131  *  Hack:  Find all expander widgets in a container
132  */
133 void findExpanderWidgets(Gtk::Container *parent,
134                         std::vector<Gtk::Expander *> &result)
136     if (!parent)
137         return;
138     std::vector<Gtk::Widget *> children = parent->get_children();
139     for (unsigned int i=0; i<children.size() ; i++)
140         {
141         Gtk::Widget *child = children[i];
142         GtkWidget *wid = child->gobj();
143         if (GTK_IS_EXPANDER(wid))
144            result.push_back((Gtk::Expander *)child);
145         else if (GTK_IS_CONTAINER(wid))
146             findExpanderWidgets((Gtk::Container *)child, result);
147         }
152 /*#########################################################################
153 ### SVG Preview Widget
154 #########################################################################*/
155 /**
156  * Simple class for displaying an SVG file in the "preview widget."
157  * Currently, this is just a wrapper of the sp_svg_view Gtk widget.
158  * Hopefully we will eventually replace with a pure Gtkmm widget.
159  */
160 class SVGPreview : public Gtk::VBox
162 public:
164     SVGPreview();
166     ~SVGPreview();
168     bool setDocument(SPDocument *doc);
170     bool setFileName(Glib::ustring &fileName);
172     bool setFromMem(char const *xmlBuffer);
174     bool set(Glib::ustring &fileName, int dialogType);
176     bool setURI(URI &uri);
178     /**
179      * Show image embedded in SVG
180      */
181     void showImage(Glib::ustring &fileName);
183     /**
184      * Show the "No preview" image
185      */
186     void showNoPreview();
188     /**
189      * Show the "Too large" image
190      */
191     void showTooLarge(long fileLength);
193 private:
194     /**
195      * The svg document we are currently showing
196      */
197     SPDocument *document;
199     /**
200      * The sp_svg_view widget
201      */
202     GtkWidget *viewerGtk;
204     /**
205      * are we currently showing the "no preview" image?
206      */
207     bool showingNoPreview;
209 };
212 bool SVGPreview::setDocument(SPDocument *doc)
214     if (document)
215         sp_document_unref(document);
217     sp_document_ref(doc);
218     document = doc;
220     //This should remove it from the box, and free resources
221     if (viewerGtk) {
222         gtk_widget_destroy(viewerGtk);
223     }
225     viewerGtk  = sp_svg_view_widget_new(doc);
226     GtkWidget *vbox = (GtkWidget *)gobj();
227     gtk_box_pack_start(GTK_BOX(vbox), viewerGtk, TRUE, TRUE, 0);
228     gtk_widget_show(viewerGtk);
230     return true;
233 bool SVGPreview::setFileName(Glib::ustring &theFileName)
235     Glib::ustring fileName = theFileName;
237     fileName = Glib::filename_to_utf8(fileName);
239     SPDocument *doc = sp_document_new (fileName.c_str(), 0);
240     if (!doc) {
241         g_warning("SVGView: error loading document '%s'\n", fileName.c_str());
242         return false;
243     }
245     setDocument(doc);
247     sp_document_unref(doc);
249     return true;
254 bool SVGPreview::setFromMem(char const *xmlBuffer)
256     if (!xmlBuffer)
257         return false;
259     gint len = (gint)strlen(xmlBuffer);
260     SPDocument *doc = sp_document_new_from_mem(xmlBuffer, len, 0);
261     if (!doc) {
262         g_warning("SVGView: error loading buffer '%s'\n",xmlBuffer);
263         return false;
264     }
266     setDocument(doc);
268     sp_document_unref(doc);
270     Inkscape::GC::request_early_collection();
272     return true;
277 void SVGPreview::showImage(Glib::ustring &theFileName)
279     Glib::ustring fileName = theFileName;
282     /*#####################################
283     # LET'S HAVE SOME FUN WITH SVG!
284     # Instead of just loading an image, why
285     # don't we make a lovely little svg and
286     # display it nicely?
287     #####################################*/
289     //Arbitrary size of svg doc -- rather 'portrait' shaped
290     gint previewWidth  = 400;
291     gint previewHeight = 600;
293     //Get some image info. Smart pointer does not need to be deleted
294     Glib::RefPtr<Gdk::Pixbuf> img = Gdk::Pixbuf::create_from_file(fileName);
295     gint imgWidth  = img->get_width();
296     gint imgHeight = img->get_height();
298     //Find the minimum scale to fit the image inside the preview area
299     double scaleFactorX = (0.9 *(double)previewWidth)  / ((double)imgWidth);
300     double scaleFactorY = (0.9 *(double)previewHeight) / ((double)imgHeight);
301     double scaleFactor = scaleFactorX;
302     if (scaleFactorX > scaleFactorY)
303         scaleFactor = scaleFactorY;
305     //Now get the resized values
306     gint scaledImgWidth  = (int) (scaleFactor * (double)imgWidth);
307     gint scaledImgHeight = (int) (scaleFactor * (double)imgHeight);
309     //center the image on the area
310     gint imgX = (previewWidth  - scaledImgWidth)  / 2;
311     gint imgY = (previewHeight - scaledImgHeight) / 2;
313     //wrap a rectangle around the image
314     gint rectX      = imgX-1;
315     gint rectY      = imgY-1;
316     gint rectWidth  = scaledImgWidth +2;
317     gint rectHeight = scaledImgHeight+2;
319     //Our template.  Modify to taste
320     gchar const *xformat =
321           "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
322           "<svg\n"
323           "xmlns=\"http://www.w3.org/2000/svg\"\n"
324           "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
325           "width=\"%d\" height=\"%d\">\n"
326           "<rect\n"
327           "  style=\"fill:#eeeeee;stroke:none\"\n"
328           "  x=\"-100\" y=\"-100\" width=\"4000\" height=\"4000\"/>\n"
329           "<image x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"\n"
330           "xlink:href=\"%s\"/>\n"
331           "<rect\n"
332           "  style=\"fill:none;"
333           "    stroke:#000000;stroke-width:1.0;"
334           "    stroke-linejoin:miter;stroke-opacity:1.0000000;"
335           "    stroke-miterlimit:4.0000000;stroke-dasharray:none\"\n"
336           "  x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"/>\n"
337           "<text\n"
338           "  style=\"font-size:24.000000;font-style:normal;font-weight:normal;"
339           "    fill:#000000;fill-opacity:1.0000000;stroke:none;"
340           "    font-family:Bitstream Vera Sans\"\n"
341           "  x=\"10\" y=\"26\">%d x %d</text>\n"
342           "</svg>\n\n";
344     //if (!Glib::get_charset()) //If we are not utf8
345     fileName = Glib::filename_to_utf8(fileName);
347     //Fill in the template
348     /* FIXME: Do proper XML quoting for fileName. */
349     gchar *xmlBuffer = g_strdup_printf(xformat,
350            previewWidth, previewHeight,
351            imgX, imgY, scaledImgWidth, scaledImgHeight,
352            fileName.c_str(),
353            rectX, rectY, rectWidth, rectHeight,
354            imgWidth, imgHeight);
356     //g_message("%s\n", xmlBuffer);
358     //now show it!
359     setFromMem(xmlBuffer);
360     g_free(xmlBuffer);
365 void SVGPreview::showNoPreview()
367     //Are we already showing it?
368     if (showingNoPreview)
369         return;
371     //Arbitrary size of svg doc -- rather 'portrait' shaped
372     gint previewWidth  = 300;
373     gint previewHeight = 600;
375     //Our template.  Modify to taste
376     gchar const *xformat =
377           "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
378           "<svg\n"
379           "xmlns=\"http://www.w3.org/2000/svg\"\n"
380           "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
381           "width=\"%d\" height=\"%d\">\n"
382           "<g transform=\"translate(-190,24.27184)\" style=\"opacity:0.12\">\n"
383           "<path\n"
384           "style=\"font-size:12;fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.936193pt\"\n"
385           "d=\"M 397.64309 320.25301 L 280.39197 282.517 L 250.74227 124.83447 L 345.08225 "
386           "29.146783 L 393.59996 46.667064 L 483.89679 135.61619 L 397.64309 320.25301 z \"\n"
387           "id=\"whiteSpace\" />\n"
388           "<path\n"
389           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
390           "d=\"M 476.95792 339.17168 C 495.78197 342.93607 499.54842 356.11361 495.78197 359.87802 "
391           "C 492.01856 363.6434 482.6065 367.40781 475.07663 361.76014 C 467.54478 "
392           "356.11361 467.54478 342.93607 476.95792 339.17168 z \"\n"
393           "id=\"droplet01\" />\n"
394           "<path\n"
395           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
396           "d=\"M 286.46194 340.42914 C 284.6277 340.91835 269.30405 327.71337 257.16909 333.8338 "
397           "C 245.03722 339.95336 236.89276 353.65666 248.22676 359.27982 C 259.56184 364.90298 "
398           "267.66433 358.41867 277.60113 351.44119 C 287.53903 344.46477 "
399           "287.18046 343.1206 286.46194 340.42914 z \"\n"
400           "id=\"droplet02\" />\n"
401           "<path\n"
402           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
403           "d=\"M 510.35756 306.92856 C 520.59494 304.36879 544.24333 306.92856 540.47688 321.98634 "
404           "C 536.71354 337.04806 504.71297 331.39827 484.00371 323.87156 C 482.12141 "
405           "308.81083 505.53237 308.13423 510.35756 306.92856 z \"\n"
406           "id=\"droplet03\" />\n"
407           "<path\n"
408           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
409           "d=\"M 359.2403 21.362537 C 347.92693 21.362537 336.6347 25.683095 327.96556 34.35223 "
410           "L 173.87387 188.41466 C 165.37697 196.9114 161.1116 207.95813 160.94269 219.04577 "
411           "L 160.88418 219.04577 C 160.88418 219.08524 160.94076 219.12322 160.94269 219.16279 "
412           "C 160.94033 219.34888 160.88418 219.53256 160.88418 219.71865 L 161.14748 219.71865 "
413           "C 164.0966 230.93917 240.29699 245.24198 248.79866 253.74346 C 261.63771 266.58263 "
414           "199.5652 276.01151 212.4041 288.85074 C 225.24316 301.68979 289.99433 313.6933 302.8346 "
415           "326.53254 C 315.67368 339.37161 276.5961 353.04289 289.43532 365.88196 C 302.27439 "
416           "378.72118 345.40201 362.67257 337.5908 396.16198 C 354.92909 413.50026 391.10302 "
417           "405.2208 415.32417 387.88252 C 428.16323 375.04345 390.6948 376.17577 403.53397 "
418           "363.33668 C 416.37304 350.49745 448.78128 350.4282 476.08902 319.71589 C 465.09739 "
419           "302.62116 429.10801 295.34136 441.94719 282.50217 C 454.78625 269.66311 479.74708 "
420           "276.18423 533.60644 251.72479 C 559.89837 239.78398 557.72636 230.71459 557.62567 "
421           "219.71865 C 557.62356 219.48727 557.62567 219.27892 557.62567 219.04577 L 557.56716 "
422           "219.04577 C 557.3983 207.95812 553.10345 196.9114 544.60673 188.41466 L 390.54428 "
423           "34.35223 C 381.87515 25.683095 370.55366 21.362537 359.2403 21.362537 z M 357.92378 "
424           "41.402939 C 362.95327 41.533963 367.01541 45.368018 374.98006 50.530832 L 447.76915 "
425           "104.50827 C 448.56596 105.02498 449.32484 105.564 450.02187 106.11735 C 450.7189 106.67062 "
426           "451.3556 107.25745 451.95277 107.84347 C 452.54997 108.42842 453.09281 109.01553 453.59111 "
427           "109.62808 C 454.08837 110.24052 454.53956 110.86661 454.93688 111.50048 C 455.33532 112.13538 "
428           "455.69164 112.78029 455.9901 113.43137 C 456.28877 114.08363 456.52291 114.75639 456.7215 "
429           "115.42078 C 456.92126 116.08419 457.08982 116.73973 457.18961 117.41019 C 457.28949 "
430           "118.08184 457.33588 118.75535 457.33588 119.42886 L 414.21245 98.598549 L 409.9118 "
431           "131.16055 L 386.18512 120.04324 L 349.55654 144.50131 L 335.54288 96.1703 L 317.4919 "
432           "138.4453 L 267.08369 143.47735 L 267.63956 121.03795 C 267.63956 115.64823 296.69685 "
433           "77.915899 314.39075 68.932902 L 346.77721 45.674327 C 351.55594 42.576634 354.90608 "
434           "41.324327 357.92378 41.402939 z M 290.92738 261.61333 C 313.87149 267.56365 339.40299 "
435           "275.37038 359.88393 275.50997 L 360.76161 284.72563 C 343.2235 282.91785 306.11346 "
436           "274.45012 297.36372 269.98057 L 290.92738 261.61333 z \"\n"
437           "id=\"mountainDroplet\" />\n"
438           "</g> <g transform=\"translate(-20,0)\">\n"
439           "<text xml:space=\"preserve\"\n"
440           "style=\"font-size:32.000000;font-style:normal;font-variant:normal;font-weight:bold;"
441           "font-stretch:normal;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000pt;"
442           "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
443           "font-family:Bitstream Vera Sans;text-anchor:middle;writing-mode:lr\"\n"
444           "x=\"190\" y=\"240\">%s</text></g>\n"
445           "</svg>\n\n";
447     //Fill in the template
448     gchar *xmlBuffer = g_strdup_printf(xformat,
449            previewWidth, previewHeight, _("No preview"));
451     //g_message("%s\n", xmlBuffer);
453     //now show it!
454     setFromMem(xmlBuffer);
455     g_free(xmlBuffer);
456     showingNoPreview = true;
460 void SVGPreview::showTooLarge(long fileLength)
463     //Arbitrary size of svg doc -- rather 'portrait' shaped
464     gint previewWidth  = 300;
465     gint previewHeight = 600;
467     //Our template.  Modify to taste
468     gchar const *xformat =
469           "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
470           "<svg\n"
471           "xmlns=\"http://www.w3.org/2000/svg\"\n"
472           "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
473           "width=\"%d\" height=\"%d\">\n"
474           "<g transform=\"translate(-170,24.27184)\" style=\"opacity:0.12\">\n"
475           "<path\n"
476           "style=\"font-size:12;fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.936193pt\"\n"
477           "d=\"M 397.64309 320.25301 L 280.39197 282.517 L 250.74227 124.83447 L 345.08225 "
478           "29.146783 L 393.59996 46.667064 L 483.89679 135.61619 L 397.64309 320.25301 z \"\n"
479           "id=\"whiteSpace\" />\n"
480           "<path\n"
481           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
482           "d=\"M 476.95792 339.17168 C 495.78197 342.93607 499.54842 356.11361 495.78197 359.87802 "
483           "C 492.01856 363.6434 482.6065 367.40781 475.07663 361.76014 C 467.54478 "
484           "356.11361 467.54478 342.93607 476.95792 339.17168 z \"\n"
485           "id=\"droplet01\" />\n"
486           "<path\n"
487           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
488           "d=\"M 286.46194 340.42914 C 284.6277 340.91835 269.30405 327.71337 257.16909 333.8338 "
489           "C 245.03722 339.95336 236.89276 353.65666 248.22676 359.27982 C 259.56184 364.90298 "
490           "267.66433 358.41867 277.60113 351.44119 C 287.53903 344.46477 "
491           "287.18046 343.1206 286.46194 340.42914 z \"\n"
492           "id=\"droplet02\" />\n"
493           "<path\n"
494           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
495           "d=\"M 510.35756 306.92856 C 520.59494 304.36879 544.24333 306.92856 540.47688 321.98634 "
496           "C 536.71354 337.04806 504.71297 331.39827 484.00371 323.87156 C 482.12141 "
497           "308.81083 505.53237 308.13423 510.35756 306.92856 z \"\n"
498           "id=\"droplet03\" />\n"
499           "<path\n"
500           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
501           "d=\"M 359.2403 21.362537 C 347.92693 21.362537 336.6347 25.683095 327.96556 34.35223 "
502           "L 173.87387 188.41466 C 165.37697 196.9114 161.1116 207.95813 160.94269 219.04577 "
503           "L 160.88418 219.04577 C 160.88418 219.08524 160.94076 219.12322 160.94269 219.16279 "
504           "C 160.94033 219.34888 160.88418 219.53256 160.88418 219.71865 L 161.14748 219.71865 "
505           "C 164.0966 230.93917 240.29699 245.24198 248.79866 253.74346 C 261.63771 266.58263 "
506           "199.5652 276.01151 212.4041 288.85074 C 225.24316 301.68979 289.99433 313.6933 302.8346 "
507           "326.53254 C 315.67368 339.37161 276.5961 353.04289 289.43532 365.88196 C 302.27439 "
508           "378.72118 345.40201 362.67257 337.5908 396.16198 C 354.92909 413.50026 391.10302 "
509           "405.2208 415.32417 387.88252 C 428.16323 375.04345 390.6948 376.17577 403.53397 "
510           "363.33668 C 416.37304 350.49745 448.78128 350.4282 476.08902 319.71589 C 465.09739 "
511           "302.62116 429.10801 295.34136 441.94719 282.50217 C 454.78625 269.66311 479.74708 "
512           "276.18423 533.60644 251.72479 C 559.89837 239.78398 557.72636 230.71459 557.62567 "
513           "219.71865 C 557.62356 219.48727 557.62567 219.27892 557.62567 219.04577 L 557.56716 "
514           "219.04577 C 557.3983 207.95812 553.10345 196.9114 544.60673 188.41466 L 390.54428 "
515           "34.35223 C 381.87515 25.683095 370.55366 21.362537 359.2403 21.362537 z M 357.92378 "
516           "41.402939 C 362.95327 41.533963 367.01541 45.368018 374.98006 50.530832 L 447.76915 "
517           "104.50827 C 448.56596 105.02498 449.32484 105.564 450.02187 106.11735 C 450.7189 106.67062 "
518           "451.3556 107.25745 451.95277 107.84347 C 452.54997 108.42842 453.09281 109.01553 453.59111 "
519           "109.62808 C 454.08837 110.24052 454.53956 110.86661 454.93688 111.50048 C 455.33532 112.13538 "
520           "455.69164 112.78029 455.9901 113.43137 C 456.28877 114.08363 456.52291 114.75639 456.7215 "
521           "115.42078 C 456.92126 116.08419 457.08982 116.73973 457.18961 117.41019 C 457.28949 "
522           "118.08184 457.33588 118.75535 457.33588 119.42886 L 414.21245 98.598549 L 409.9118 "
523           "131.16055 L 386.18512 120.04324 L 349.55654 144.50131 L 335.54288 96.1703 L 317.4919 "
524           "138.4453 L 267.08369 143.47735 L 267.63956 121.03795 C 267.63956 115.64823 296.69685 "
525           "77.915899 314.39075 68.932902 L 346.77721 45.674327 C 351.55594 42.576634 354.90608 "
526           "41.324327 357.92378 41.402939 z M 290.92738 261.61333 C 313.87149 267.56365 339.40299 "
527           "275.37038 359.88393 275.50997 L 360.76161 284.72563 C 343.2235 282.91785 306.11346 "
528           "274.45012 297.36372 269.98057 L 290.92738 261.61333 z \"\n"
529           "id=\"mountainDroplet\" />\n"
530           "</g>\n"
531           "<text xml:space=\"preserve\"\n"
532           "style=\"font-size:32.000000;font-style:normal;font-variant:normal;font-weight:bold;"
533           "font-stretch:normal;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000pt;"
534           "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
535           "font-family:Bitstream Vera Sans;text-anchor:middle;writing-mode:lr\"\n"
536           "x=\"170\" y=\"215\">%5.1f MB</text>\n"
537           "<text xml:space=\"preserve\"\n"
538           "style=\"font-size:24.000000;font-style:normal;font-variant:normal;font-weight:bold;"
539           "font-stretch:normal;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000pt;"
540           "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
541           "font-family:Bitstream Vera Sans;text-anchor:middle;writing-mode:lr\"\n"
542           "x=\"180\" y=\"245\">%s</text>\n"
543           "</svg>\n\n";
545     //Fill in the template
546     double floatFileLength = ((double)fileLength) / 1048576.0;
547     //printf("%ld %f\n", fileLength, floatFileLength);
548     gchar *xmlBuffer = g_strdup_printf(xformat,
549            previewWidth, previewHeight, floatFileLength,
550            _("too large for preview"));
552     //g_message("%s\n", xmlBuffer);
554     //now show it!
555     setFromMem(xmlBuffer);
556     g_free(xmlBuffer);
560 static bool
561 hasSuffix(Glib::ustring &str, Glib::ustring &ext)
563     int strLen = str.length();
564     int extLen = ext.length();
565     if (extLen > strLen)
566     {
567         return false;
568     }
569     int strpos = strLen-1;
570     for (int extpos = extLen-1 ; extpos>=0 ; extpos--, strpos--)
571     {
572         Glib::ustring::value_type ch = str[strpos];
573         if (ch != ext[extpos])
574         {
575             if ( ((ch & 0xff80) != 0) ||
576                  static_cast<Glib::ustring::value_type>( g_ascii_tolower( static_cast<gchar>(0x07f & ch) ) ) != ext[extpos] )
577             {
578                 return false;
579             }
580         }
581     }
582     return true;
586 /**
587  * Return true if the image is loadable by Gdk, else false
588  */
589 static bool
590 isValidImageFile(Glib::ustring &fileName)
592     std::vector<Gdk::PixbufFormat>formats = Gdk::Pixbuf::get_formats();
593     for (unsigned int i=0; i<formats.size(); i++)
594         {
595         Gdk::PixbufFormat format = formats[i];
596         std::vector<Glib::ustring>extensions = format.get_extensions();
597         for (unsigned int j=0; j<extensions.size(); j++)
598             {
599             Glib::ustring ext = extensions[j];
600             if (hasSuffix(fileName, ext))
601                 {
602                 return true;
603                 }
604             }
605         }
606     return false;
609 bool SVGPreview::set(Glib::ustring &fileName, int dialogType)
612     if (!Glib::file_test(fileName, Glib::FILE_TEST_EXISTS))
613         return false;
615     gchar *fName = (gchar *)fileName.c_str();
616     //g_message("fname:%s\n", fName);
619     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR))
620         {
621         struct stat info;
622         if (stat(fName, &info))
623             {
624             return FALSE;
625             }
626         long fileLen = info.st_size;
627         if (fileLen > 0x150000L)
628             {
629             showingNoPreview = false;
630             showTooLarge(fileLen);
631             return FALSE;
632             }
633         }
635     Glib::ustring svg = ".svg";
636     Glib::ustring svgz = ".svgz";
638     if ((dialogType == SVG_TYPES || dialogType == IMPORT_TYPES) &&
639            (hasSuffix(fileName, svg) || hasSuffix(fileName, svgz)   )
640          )
641         {
642         bool retval = setFileName(fileName);
643         showingNoPreview = false;
644         return retval;
645         }
646     else if (isValidImageFile(fileName))
647         {
648         showImage(fileName);
649         showingNoPreview = false;
650         return true;
651         }
652     else
653         {
654         showNoPreview();
655         return false;
656         }
660 SVGPreview::SVGPreview()
662     if (!INKSCAPE)
663         inkscape_application_init("",false);
664     document = NULL;
665     viewerGtk = NULL;
666     set_size_request(150,150);
667     showingNoPreview = false;
670 SVGPreview::~SVGPreview()
679 /*#########################################################################
680 ### F I L E    O P E N
681 #########################################################################*/
683 /**
684  * Our implementation class for the FileOpenDialog interface..
685  */
686 class FileOpenDialogImpl : public FileOpenDialog, public Gtk::FileChooserDialog
688 public:
689     FileOpenDialogImpl(const Glib::ustring &dir,
690                        FileDialogType fileTypes,
691                        const Glib::ustring &title);
693     virtual ~FileOpenDialogImpl();
695     bool show();
697     Inkscape::Extension::Extension *getSelectionType();
699     gchar *getFilename();
701     Glib::SListHandle<Glib::ustring> getFilenames ();
702 protected:
706 private:
709     /**
710      * What type of 'open' are we? (open, import, place, etc)
711      */
712     FileDialogType dialogType;
714     /**
715      * Our svg preview widget
716      */
717     SVGPreview svgPreview;
719     /**
720      * Callback for seeing if the preview needs to be drawn
721      */
722     void updatePreviewCallback();
724     /**
725      * Fix to allow the user to type the file name
726      */
727     Gtk::Entry fileNameEntry;
729     /**
730      *  Create a filter menu for this type of dialog
731      */
732     void createFilterMenu();
734     /**
735      * Callback for user input into fileNameEntry
736      */
737     void fileNameEntryChangedCallback();
739     /**
740      * Callback for user changing which item is selected on the list
741      */
742     void fileSelectedCallback();
745     /**
746      * Filter name->extension lookup
747      */
748     std::map<Glib::ustring, Inkscape::Extension::Extension *> extensionMap;
750     /**
751      * The extension to use to write this file
752      */
753     Inkscape::Extension::Extension *extension;
755     /**
756      * Filename that was given
757      */
758     Glib::ustring myFilename;
760 };
766 /**
767  * Callback for checking if the preview needs to be redrawn
768  */
769 void FileOpenDialogImpl::updatePreviewCallback()
771     Glib::ustring fileName = get_preview_filename();
772     if (fileName.length() < 1)
773         return;
774     svgPreview.set(fileName, dialogType);
781 /**
782  * Callback for fileNameEntry widget
783  */
784 void FileOpenDialogImpl::fileNameEntryChangedCallback()
786     Glib::ustring fileName = fileNameEntry.get_text();
788     // TODO remove this leak
789     fileName = Glib::filename_from_utf8(fileName);
791     //g_message("User hit return.  Text is '%s'\n", fName.c_str());
793     if (!Glib::path_is_absolute(fileName)) {
794         //try appending to the current path
795         // not this way: fileName = get_current_folder() + "/" + fName;
796         std::vector<Glib::ustring> pathSegments;
797         pathSegments.push_back( get_current_folder() );
798         pathSegments.push_back( fileName );
799         fileName = Glib::build_filename(pathSegments);
800     }
802     //g_message("path:'%s'\n", fName.c_str());
804     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
805         set_current_folder(fileName);
806     } else if (Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)) {
807         //dialog with either (1) select a regular file or (2) cd to dir
808         //simulate an 'OK'
809         set_filename(fileName);
810         response(Gtk::RESPONSE_OK);
811     }
818 /**
819  * Callback for fileNameEntry widget
820  */
821 void FileOpenDialogImpl::fileSelectedCallback()
823     Glib::ustring fileName     = get_filename();
824     if (!Glib::get_charset()) //If we are not utf8
825         fileName = Glib::filename_to_utf8(fileName);
826     //g_message("User selected '%s'\n",
827     //       filename().c_str());
829 #ifdef INK_DUMP_FILENAME_CONV
830     ::dump_ustr( get_filename() );
831 #endif
832     fileNameEntry.set_text(fileName);
838 void FileOpenDialogImpl::createFilterMenu()
840     //patterns added dynamically below
841     Gtk::FileFilter allImageFilter;
842     allImageFilter.set_name(_("All Images"));
843     extensionMap[Glib::ustring(_("All Images"))]=NULL;
844     add_filter(allImageFilter);
846     Gtk::FileFilter allFilter;
847     allFilter.set_name(_("All Files"));
848     extensionMap[Glib::ustring(_("All Files"))]=NULL;
849     allFilter.add_pattern("*");
850     add_filter(allFilter);
852     //patterns added dynamically below
853     Gtk::FileFilter allInkscapeFilter;
854     allInkscapeFilter.set_name(_("All Inkscape Files"));
855     extensionMap[Glib::ustring(_("All Inkscape Files"))]=NULL;
856     add_filter(allInkscapeFilter);
858     Inkscape::Extension::DB::InputList extension_list;
859     Inkscape::Extension::db.get_input_list(extension_list);
861     for (Inkscape::Extension::DB::InputList::iterator current_item = extension_list.begin();
862          current_item != extension_list.end(); current_item++)
863     {
864         Inkscape::Extension::Input * imod = *current_item;
866         // FIXME: would be nice to grey them out instead of not listing them
867         if (imod->deactivated()) continue;
869         Glib::ustring upattern("*");
870         Glib::ustring extension = imod->get_extension();
871         fileDialogExtensionToPattern(upattern, extension);
873         Gtk::FileFilter filter;
874         Glib::ustring uname(_(imod->get_filetypename()));
875         filter.set_name(uname);
876         filter.add_pattern(upattern);
877         add_filter(filter);
878         extensionMap[uname] = imod;
880         //g_message("ext %s:%s '%s'\n", ioext->name, ioext->mimetype, upattern.c_str());
881         allInkscapeFilter.add_pattern(upattern);
882         if ( strncmp("image", imod->get_mimetype(), 5)==0 )
883             allImageFilter.add_pattern(upattern);
884     }
886     return;
891 /**
892  * Constructor.  Not called directly.  Use the factory.
893  */
894 FileOpenDialogImpl::FileOpenDialogImpl(const Glib::ustring &dir,
895                                        FileDialogType fileTypes,
896                                        const Glib::ustring &title) :
897                                        Gtk::FileChooserDialog(title)
901     /* One file at a time */
902     /* And also Multiple Files */
903     set_select_multiple(true);
905     /* Initalize to Autodetect */
906     extension = NULL;
907     /* No filename to start out with */
908     myFilename = "";
910     /* Set our dialog type (open, import, etc...)*/
911     dialogType = fileTypes;
914     /* Set the pwd and/or the filename */
915     if (dir.size() > 0)
916         {
917         Glib::ustring udir(dir);
918         Glib::ustring::size_type len = udir.length();
919         // leaving a trailing backslash on the directory name leads to the infamous
920         // double-directory bug on win32
921         if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
922         set_current_folder(udir.c_str());
923         }
925     //###### Add the file types menu
926     createFilterMenu();
928     //###### Add a preview widget
929     set_preview_widget(svgPreview);
930     set_preview_widget_active(true);
931     set_use_preview_label (false);
933     //Catch selection-changed events, so we can adjust the text widget
934     signal_update_preview().connect(
935          sigc::mem_fun(*this, &FileOpenDialogImpl::updatePreviewCallback) );
938     //###### Add a text entry bar, and tie it to file chooser events
939     fileNameEntry.set_text(get_current_folder());
940     set_extra_widget(fileNameEntry);
941     fileNameEntry.grab_focus();
943     //Catch when user hits [return] on the text field
944     fileNameEntry.signal_activate().connect(
945          sigc::mem_fun(*this, &FileOpenDialogImpl::fileNameEntryChangedCallback) );
947     //Catch selection-changed events, so we can adjust the text widget
948     signal_selection_changed().connect(
949          sigc::mem_fun(*this, &FileOpenDialogImpl::fileSelectedCallback) );
951     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
952     add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_OK);
960 /**
961  * Public factory.  Called by file.cpp, among others.
962  */
963 FileOpenDialog *FileOpenDialog::create(const Glib::ustring &path,
964                                        FileDialogType fileTypes,
965                                        const Glib::ustring &title)
967     FileOpenDialog *dialog = new FileOpenDialogImpl(path, fileTypes, title);
968     return dialog;
974 /**
975  * Destructor
976  */
977 FileOpenDialogImpl::~FileOpenDialogImpl()
983 /**
984  * Show this dialog modally.  Return true if user hits [OK]
985  */
986 bool
987 FileOpenDialogImpl::show()
989     Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());
990     if (s.length() == 0) 
991         s = getcwd (NULL, 0);
992     set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing
993     set_modal (TRUE);                      //Window
994     sp_transientize((GtkWidget *)gobj());  //Make transient
995     gint b = run();                        //Dialog
996     svgPreview.showNoPreview();
997     hide();
999     if (b == Gtk::RESPONSE_OK)
1000         {
1001         //This is a hack, to avoid the warning messages that
1002         //Gtk::FileChooser::get_filter() returns
1003         //should be:  Gtk::FileFilter *filter = get_filter();
1004         GtkFileChooser *gtkFileChooser = Gtk::FileChooser::gobj();
1005         GtkFileFilter *filter = gtk_file_chooser_get_filter(gtkFileChooser);
1006         if (filter)
1007             {
1008             //Get which extension was chosen, if any
1009             extension = extensionMap[gtk_file_filter_get_name(filter)];
1010             }
1011         myFilename = get_filename();
1012         return TRUE;
1013         }
1014     else
1015        {
1016        return FALSE;
1017        }
1023 /**
1024  * Get the file extension type that was selected by the user. Valid after an [OK]
1025  */
1026 Inkscape::Extension::Extension *
1027 FileOpenDialogImpl::getSelectionType()
1029     return extension;
1033 /**
1034  * Get the file name chosen by the user.   Valid after an [OK]
1035  */
1036 gchar *
1037 FileOpenDialogImpl::getFilename (void)
1039     return g_strdup(myFilename.c_str());
1043 /**
1044  * To Get Multiple filenames selected at-once.
1045  */
1046 Glib::SListHandle<Glib::ustring>FileOpenDialogImpl::getFilenames()
1047 {    
1048     return get_filenames();
1056 //########################################################################
1057 //# F I L E    S A V E
1058 //########################################################################
1060 class FileType
1062     public:
1063     FileType() {}
1064     ~FileType() {}
1065     Glib::ustring name;
1066     Glib::ustring pattern;
1067     Inkscape::Extension::Extension *extension;
1068 };
1070 /**
1071  * Our implementation of the FileSaveDialog interface.
1072  */
1073 class FileSaveDialogImpl : public FileSaveDialog, public Gtk::FileChooserDialog
1076 public:
1077     FileSaveDialogImpl(const Glib::ustring &dir,
1078                        FileDialogType fileTypes,
1079                        const Glib::ustring &title,
1080                        const Glib::ustring &default_key);
1082     virtual ~FileSaveDialogImpl();
1084     bool show();
1086     Inkscape::Extension::Extension *getSelectionType();
1088     gchar *getFilename();
1091 private:
1093     /**
1094      * What type of 'open' are we? (save, export, etc)
1095      */
1096     FileDialogType dialogType;
1098     /**
1099      * Our svg preview widget
1100      */
1101     SVGPreview svgPreview;
1103     /**
1104      * Fix to allow the user to type the file name
1105      */
1106     Gtk::Entry *fileNameEntry;
1108     /**
1109      * Callback for seeing if the preview needs to be drawn
1110      */
1111     void updatePreviewCallback();
1115     /**
1116      * Allow the specification of the output file type
1117      */
1118     Gtk::HBox fileTypeBox;
1120     /**
1121      * Allow the specification of the output file type
1122      */
1123     Gtk::ComboBoxText fileTypeComboBox;
1126     /**
1127      *  Data mirror of the combo box
1128      */
1129     std::vector<FileType> fileTypes;
1131     //# Child widgets
1132     Gtk::CheckButton fileTypeCheckbox;
1135     /**
1136      * Callback for user input into fileNameEntry
1137      */
1138     void fileTypeChangedCallback();
1140     /**
1141      *  Create a filter menu for this type of dialog
1142      */
1143     void createFileTypeMenu();
1146     bool append_extension;
1148     /**
1149      * The extension to use to write this file
1150      */
1151     Inkscape::Extension::Extension *extension;
1153     /**
1154      * Callback for user input into fileNameEntry
1155      */
1156     void fileNameEntryChangedCallback();
1158     /**
1159      * Filename that was given
1160      */
1161     Glib::ustring myFilename;
1162 };
1169 /**
1170  * Callback for checking if the preview needs to be redrawn
1171  */
1172 void FileSaveDialogImpl::updatePreviewCallback()
1174     Glib::ustring fileName = get_preview_filename();
1175     if (!fileName.c_str())
1176         return;
1177     bool retval = svgPreview.set(fileName, dialogType);
1178     set_preview_widget_active(retval);
1183 /**
1184  * Callback for fileNameEntry widget
1185  */
1186 void FileSaveDialogImpl::fileNameEntryChangedCallback()
1188     if (!fileNameEntry)
1189         return;
1191     Glib::ustring fileName = fileNameEntry->get_text();
1192     if (!Glib::get_charset()) //If we are not utf8
1193         fileName = Glib::filename_to_utf8(fileName);
1195     //g_message("User hit return.  Text is '%s'\n", fileName.c_str());
1197     if (!Glib::path_is_absolute(fileName)) {
1198         //try appending to the current path
1199         // not this way: fileName = get_current_folder() + "/" + fileName;
1200         std::vector<Glib::ustring> pathSegments;
1201         pathSegments.push_back( get_current_folder() );
1202         pathSegments.push_back( fileName );
1203         fileName = Glib::build_filename(pathSegments);
1204     }
1206     //g_message("path:'%s'\n", fileName.c_str());
1208     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
1209         set_current_folder(fileName);
1210     } else if (/*Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)*/1) {
1211         //dialog with either (1) select a regular file or (2) cd to dir
1212         //simulate an 'OK'
1213         set_filename(fileName);
1214         response(Gtk::RESPONSE_OK);
1215     }
1220 /**
1221  * Callback for fileNameEntry widget
1222  */
1223 void FileSaveDialogImpl::fileTypeChangedCallback()
1225     int sel = fileTypeComboBox.get_active_row_number();
1226     if (sel<0 || sel >= (int)fileTypes.size())
1227         return;
1228     FileType type = fileTypes[sel];
1229     //g_message("selected: %s\n", type.name.c_str());
1230     Gtk::FileFilter filter;
1231     filter.add_pattern(type.pattern);
1232     set_filter(filter);
1237 void FileSaveDialogImpl::createFileTypeMenu()
1239     Inkscape::Extension::DB::OutputList extension_list;
1240     Inkscape::Extension::db.get_output_list(extension_list);
1242     for (Inkscape::Extension::DB::OutputList::iterator current_item = extension_list.begin();
1243          current_item != extension_list.end(); current_item++)
1244     {
1245         Inkscape::Extension::Output * omod = *current_item;
1247         // FIXME: would be nice to grey them out instead of not listing them
1248         if (omod->deactivated()) continue;
1250         FileType type;
1251         type.name     = (_(omod->get_filetypename()));
1252         type.pattern  = "*";
1253         Glib::ustring extension = omod->get_extension();
1254         fileDialogExtensionToPattern (type.pattern, extension);
1255         type.extension= omod;
1256         fileTypeComboBox.append_text(type.name);
1257         fileTypes.push_back(type);
1258     }
1260     //#Let user choose
1261     FileType guessType;
1262     guessType.name = _("Guess from extension");
1263     guessType.pattern = "*";
1264     guessType.extension = NULL;
1265     fileTypeComboBox.append_text(guessType.name);
1266     fileTypes.push_back(guessType);
1269     fileTypeComboBox.set_active(0);
1270     fileTypeChangedCallback(); //call at least once to set the filter
1275 /**
1276  * Constructor
1277  */
1278 FileSaveDialogImpl::FileSaveDialogImpl(const Glib::ustring &dir,
1279                                        FileDialogType fileTypes,
1280                                        const Glib::ustring &title,
1281                                        const Glib::ustring &default_key) :
1282             Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_SAVE)
1284     append_extension = (bool)prefs_get_int_attribute("dialogs.save_as",
1285                                                   "append_extension", 1);
1287     /* One file at a time */
1288     set_select_multiple(false);
1290     /* Initalize to Autodetect */
1291     extension = NULL;
1292     /* No filename to start out with */
1293     myFilename = "";
1295     /* Set our dialog type (save, export, etc...)*/
1296     dialogType = fileTypes;
1298     /* Set the pwd and/or the filename */
1299     if (dir.size() > 0)
1300         {
1301         Glib::ustring udir(dir);
1302         Glib::ustring::size_type len = udir.length();
1303         // leaving a trailing backslash on the directory name leads to the infamous
1304         // double-directory bug on win32
1305         if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
1306         set_current_folder(udir.c_str());
1307         }
1309     //###### Add the file types menu
1310     //createFilterMenu();
1312     //###### Do we want the .xxx extension automatically added?
1313     fileTypeCheckbox.set_label(Glib::ustring(_("Append filename extension automatically")));
1314     fileTypeCheckbox.set_active(append_extension);
1316     fileTypeBox.pack_start(fileTypeCheckbox);
1317     createFileTypeMenu();
1318     fileTypeComboBox.set_size_request(200,40);
1319     fileTypeComboBox.signal_changed().connect(
1320          sigc::mem_fun(*this, &FileSaveDialogImpl::fileTypeChangedCallback) );
1322     fileTypeBox.pack_start(fileTypeComboBox);
1324     set_extra_widget(fileTypeBox);
1325     //get_vbox()->pack_start(fileTypeBox, false, false, 0);
1326     //get_vbox()->reorder_child(fileTypeBox, 2);
1328     //###### Add a preview widget
1329     set_preview_widget(svgPreview);
1330     set_preview_widget_active(true);
1331     set_use_preview_label (false);
1333     //Catch selection-changed events, so we can adjust the text widget
1334     signal_update_preview().connect(
1335          sigc::mem_fun(*this, &FileSaveDialogImpl::updatePreviewCallback) );
1338     //Let's do some customization
1339     fileNameEntry = NULL;
1340     Gtk::Container *cont = get_toplevel();
1341     std::vector<Gtk::Entry *> entries;
1342     findEntryWidgets(cont, entries);
1343     //g_message("Found %d entry widgets\n", entries.size());
1344     if (entries.size() >=1 )
1345         {
1346         //Catch when user hits [return] on the text field
1347         fileNameEntry = entries[0];
1348         fileNameEntry->signal_activate().connect(
1349              sigc::mem_fun(*this, &FileSaveDialogImpl::fileNameEntryChangedCallback) );
1350         }
1352     //Let's do more customization
1353     std::vector<Gtk::Expander *> expanders;
1354     findExpanderWidgets(cont, expanders);
1355     //g_message("Found %d expander widgets\n", expanders.size());
1356     if (expanders.size() >=1 )
1357         {
1358         //Always show the file list
1359         Gtk::Expander *expander = expanders[0];
1360         expander->set_expanded(true);
1361         }
1364     //if (extension == NULL)
1365     //    checkbox.set_sensitive(FALSE);
1367     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1368     add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_OK);
1370     show_all_children();
1375 /**
1376  * Public factory method.  Used in file.cpp
1377  */
1378 FileSaveDialog *FileSaveDialog::create(const Glib::ustring &path,
1379                                        FileDialogType fileTypes,
1380                                        const Glib::ustring &title,
1381                                        const Glib::ustring &default_key)
1383     FileSaveDialog *dialog = new FileSaveDialogImpl(path, fileTypes, title, default_key);
1384     return dialog;
1391 /**
1392  * Destructor
1393  */
1394 FileSaveDialogImpl::~FileSaveDialogImpl()
1400 /**
1401  * Show this dialog modally.  Return true if user hits [OK]
1402  */
1403 bool
1404 FileSaveDialogImpl::show()
1406     Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());
1407     if (s.length() == 0) 
1408         s = getcwd (NULL, 0);
1409     set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing
1410     set_modal (TRUE);                      //Window
1411     sp_transientize((GtkWidget *)gobj());  //Make transient
1412     gint b = run();                        //Dialog
1413     svgPreview.showNoPreview();
1414     hide();
1416     if (b == Gtk::RESPONSE_OK)
1417         {
1418         int sel = fileTypeComboBox.get_active_row_number ();
1419         if (sel>=0 && sel< (int)fileTypes.size())
1420             {
1421             FileType &type = fileTypes[sel];
1422             extension = type.extension;
1423             }
1424         myFilename = get_filename();
1426         /*
1428         // FIXME: Why do we have more code
1430         append_extension = checkbox.get_active();
1431         prefs_set_int_attribute("dialogs.save_as", "append_extension", append_extension);
1432         prefs_set_string_attribute("dialogs.save_as", "default",
1433                   ( extension != NULL ? extension->get_id() : "" ));
1434         */
1435         return TRUE;
1436         }
1437     else
1438         {
1439         return FALSE;
1440         }
1444 /**
1445  * Get the file extension type that was selected by the user. Valid after an [OK]
1446  */
1447 Inkscape::Extension::Extension *
1448 FileSaveDialogImpl::getSelectionType()
1450     return extension;
1454 /**
1455  * Get the file name chosen by the user.   Valid after an [OK]
1456  */
1457 gchar *
1458 FileSaveDialogImpl::getFilename()
1460     return g_strdup(myFilename.c_str());
1470 //########################################################################
1471 //# F I L E     E X P O R T
1472 //########################################################################
1475 /**
1476  * Our implementation of the FileExportDialog interface.
1477  */
1478 class FileExportDialogImpl : public FileExportDialog, public Gtk::FileChooserDialog
1481 public:
1482     FileExportDialogImpl(const Glib::ustring &dir,
1483                        FileDialogType fileTypes,
1484                        const Glib::ustring &title,
1485                        const Glib::ustring &default_key);
1487     virtual ~FileExportDialogImpl();
1489     bool show();
1491     Inkscape::Extension::Extension *getSelectionType();
1493     gchar *getFilename();
1496 private:
1498     /**
1499      * What type of 'open' are we? (save, export, etc)
1500      */
1501     FileDialogType dialogType;
1503     /**
1504      * Our svg preview widget
1505      */
1506     SVGPreview svgPreview;
1508     /**
1509      * Fix to allow the user to type the file name
1510      */
1511     Gtk::Entry *fileNameEntry;
1513     /**
1514      * Callback for seeing if the preview needs to be drawn
1515      */
1516     void updatePreviewCallback();
1520     /**
1521      * Allow the specification of the output file type
1522      */
1523     Gtk::HBox fileTypeBox;
1525     /**
1526      * Allow the specification of the output file type
1527      */
1528     Gtk::ComboBoxText fileTypeComboBox;
1531     /**
1532      *  Data mirror of the combo box
1533      */
1534     std::vector<FileType> fileTypes;
1536     //# Child widgets
1537     Gtk::CheckButton fileTypeCheckbox;
1540     /**
1541      * Callback for user input into fileNameEntry
1542      */
1543     void fileTypeChangedCallback();
1545     /**
1546      *  Create a filter menu for this type of dialog
1547      */
1548     void createFileTypeMenu();
1551     bool append_extension;
1553     /**
1554      * The extension to use to write this file
1555      */
1556     Inkscape::Extension::Extension *extension;
1558     /**
1559      * Callback for user input into fileNameEntry
1560      */
1561     void fileNameEntryChangedCallback();
1563     /**
1564      * Filename that was given
1565      */
1566     Glib::ustring myFilename;
1567 };
1574 /**
1575  * Callback for checking if the preview needs to be redrawn
1576  */
1577 void FileExportDialogImpl::updatePreviewCallback()
1579     Glib::ustring fileName = get_preview_filename();
1580     if (!fileName.c_str())
1581         return;
1582     bool retval = svgPreview.set(fileName, dialogType);
1583     set_preview_widget_active(retval);
1588 /**
1589  * Callback for fileNameEntry widget
1590  */
1591 void FileExportDialogImpl::fileNameEntryChangedCallback()
1593     if (!fileNameEntry)
1594         return;
1596     Glib::ustring fileName = fileNameEntry->get_text();
1597     if (!Glib::get_charset()) //If we are not utf8
1598         fileName = Glib::filename_to_utf8(fileName);
1600     //g_message("User hit return.  Text is '%s'\n", fileName.c_str());
1602     if (!Glib::path_is_absolute(fileName)) {
1603         //try appending to the current path
1604         // not this way: fileName = get_current_folder() + "/" + fileName;
1605         std::vector<Glib::ustring> pathSegments;
1606         pathSegments.push_back( get_current_folder() );
1607         pathSegments.push_back( fileName );
1608         fileName = Glib::build_filename(pathSegments);
1609     }
1611     //g_message("path:'%s'\n", fileName.c_str());
1613     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
1614         set_current_folder(fileName);
1615     } else if (/*Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)*/1) {
1616         //dialog with either (1) select a regular file or (2) cd to dir
1617         //simulate an 'OK'
1618         set_filename(fileName);
1619         response(Gtk::RESPONSE_OK);
1620     }
1625 /**
1626  * Callback for fileNameEntry widget
1627  */
1628 void FileExportDialogImpl::fileTypeChangedCallback()
1630     int sel = fileTypeComboBox.get_active_row_number();
1631     if (sel<0 || sel >= (int)fileTypes.size())
1632         return;
1633     FileType type = fileTypes[sel];
1634     //g_message("selected: %s\n", type.name.c_str());
1635     Gtk::FileFilter filter;
1636     filter.add_pattern(type.pattern);
1637     set_filter(filter);
1642 void FileExportDialogImpl::createFileTypeMenu()
1644     Inkscape::Extension::DB::OutputList extension_list;
1645     Inkscape::Extension::db.get_output_list(extension_list);
1647     for (Inkscape::Extension::DB::OutputList::iterator current_item = extension_list.begin();
1648          current_item != extension_list.end(); current_item++)
1649     {
1650         Inkscape::Extension::Output * omod = *current_item;
1652         // FIXME: would be nice to grey them out instead of not listing them
1653         if (omod->deactivated()) continue;
1655         FileType type;
1656         type.name     = (_(omod->get_filetypename()));
1657         type.pattern  = "*";
1658         Glib::ustring extension = omod->get_extension();
1659         fileDialogExtensionToPattern (type.pattern, extension);
1660         type.extension= omod;
1661         fileTypeComboBox.append_text(type.name);
1662         fileTypes.push_back(type);
1663     }
1665     //#Let user choose
1666     FileType guessType;
1667     guessType.name = _("Guess from extension");
1668     guessType.pattern = "*";
1669     guessType.extension = NULL;
1670     fileTypeComboBox.append_text(guessType.name);
1671     fileTypes.push_back(guessType);
1674     fileTypeComboBox.set_active(0);
1675     fileTypeChangedCallback(); //call at least once to set the filter
1679 /**
1680  * Constructor
1681  */
1682 FileExportDialogImpl::FileExportDialogImpl(const Glib::ustring &dir,
1683                                        FileDialogType fileTypes,
1684                                        const Glib::ustring &title,
1685                                        const Glib::ustring &default_key) :
1686             Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_SAVE)
1688     append_extension = (bool)prefs_get_int_attribute("dialogs.save_as", "append_extension", 1);
1690     /* One file at a time */
1691     set_select_multiple(false);
1693     /* Initalize to Autodetect */
1694     extension = NULL;
1695     /* No filename to start out with */
1696     myFilename = "";
1698     /* Set our dialog type (save, export, etc...)*/
1699     dialogType = fileTypes;
1701     /* Set the pwd and/or the filename */
1702     if (dir.size()>0)
1703         {
1704         Glib::ustring udir(dir);
1705         Glib::ustring::size_type len = udir.length();
1706         // leaving a trailing backslash on the directory name leads to the infamous
1707         // double-directory bug on win32
1708         if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
1709         set_current_folder(udir.c_str());
1710         }
1712     //###### Add the file types menu
1713     //createFilterMenu();
1715     //###### Do we want the .xxx extension automatically added?
1716     fileTypeCheckbox.set_label(Glib::ustring(_("Append filename extension automatically")));
1717     fileTypeCheckbox.set_active(append_extension);
1719     fileTypeBox.pack_start(fileTypeCheckbox);
1720     createFileTypeMenu();
1721     fileTypeComboBox.set_size_request(200,40);
1722     fileTypeComboBox.signal_changed().connect(
1723          sigc::mem_fun(*this, &FileExportDialogImpl::fileTypeChangedCallback) );
1725     fileTypeBox.pack_start(fileTypeComboBox);
1727     set_extra_widget(fileTypeBox);
1728     //get_vbox()->pack_start(fileTypeBox, false, false, 0);
1729     //get_vbox()->reorder_child(fileTypeBox, 2);
1731     //###### Add a preview widget
1732     set_preview_widget(svgPreview);
1733     set_preview_widget_active(true);
1734     set_use_preview_label (false);
1736     //Catch selection-changed events, so we can adjust the text widget
1737     signal_update_preview().connect(
1738          sigc::mem_fun(*this, &FileExportDialogImpl::updatePreviewCallback) );
1741     //Let's do some customization
1742     fileNameEntry = NULL;
1743     Gtk::Container *cont = get_toplevel();
1744     std::vector<Gtk::Entry *> entries;
1745     findEntryWidgets(cont, entries);
1746     //g_message("Found %d entry widgets\n", entries.size());
1747     if (entries.size() >=1 )
1748         {
1749         //Catch when user hits [return] on the text field
1750         fileNameEntry = entries[0];
1751         fileNameEntry->signal_activate().connect(
1752              sigc::mem_fun(*this, &FileExportDialogImpl::fileNameEntryChangedCallback) );
1753         }
1755     //Let's do more customization
1756     std::vector<Gtk::Expander *> expanders;
1757     findExpanderWidgets(cont, expanders);
1758     //g_message("Found %d expander widgets\n", expanders.size());
1759     if (expanders.size() >=1 )
1760         {
1761         //Always show the file list
1762         Gtk::Expander *expander = expanders[0];
1763         expander->set_expanded(true);
1764         }
1767     //if (extension == NULL)
1768     //    checkbox.set_sensitive(FALSE);
1770     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1771     add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_OK);
1773     show_all_children();
1778 /**
1779  * Public factory method.  Used in file.cpp
1780  */
1781 FileExportDialog *FileExportDialog::create(const Glib::ustring &path,
1782                                        FileDialogType fileTypes,
1783                                        const Glib::ustring &title,
1784                                        const Glib::ustring &default_key)
1786     FileExportDialog *dialog = new FileExportDialogImpl(path, fileTypes, title, default_key);
1787     return dialog;
1794 /**
1795  * Destructor
1796  */
1797 FileExportDialogImpl::~FileExportDialogImpl()
1803 /**
1804  * Show this dialog modally.  Return true if user hits [OK]
1805  */
1806 bool
1807 FileExportDialogImpl::show()
1809     Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());
1810     if (s.length() == 0) 
1811         s = getcwd (NULL, 0);
1812     set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing
1813     set_modal (TRUE);                      //Window
1814     sp_transientize((GtkWidget *)gobj());  //Make transient
1815     gint b = run();                        //Dialog
1816     svgPreview.showNoPreview();
1817     hide();
1819     if (b == Gtk::RESPONSE_OK)
1820         {
1821         int sel = fileTypeComboBox.get_active_row_number ();
1822         if (sel>=0 && sel< (int)fileTypes.size())
1823             {
1824             FileType &type = fileTypes[sel];
1825             extension = type.extension;
1826             }
1827         myFilename = get_filename();
1829         /*
1831         // FIXME: Why do we have more code
1833         append_extension = checkbox.get_active();
1834         prefs_set_int_attribute("dialogs.save_as", "append_extension", append_extension);
1835         prefs_set_string_attribute("dialogs.save_as", "default",
1836                   ( extension != NULL ? extension->get_id() : "" ));
1837         */
1838         return TRUE;
1839         }
1840     else
1841         {
1842         return FALSE;
1843         }
1847 /**
1848  * Get the file extension type that was selected by the user. Valid after an [OK]
1849  */
1850 Inkscape::Extension::Extension *
1851 FileExportDialogImpl::getSelectionType()
1853     return extension;
1857 /**
1858  * Get the file name chosen by the user.   Valid after an [OK]
1859  */
1860 gchar *
1861 FileExportDialogImpl::getFilename()
1863     return g_strdup(myFilename.c_str());
1867 } //namespace Dialog
1868 } //namespace UI
1869 } //namespace Inkscape
1872 /*
1873   Local Variables:
1874   mode:c++
1875   c-file-style:"stroustrup"
1876   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1877   indent-tabs-mode:nil
1878   fill-column:99
1879   End:
1880 */
1881 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :