Code

cb72d17f5e7382b175a2a3ded0e165b9bf45a868
[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-2006 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     D I A L O G    B A S E    C L A S S
681 #########################################################################*/
683 /**
684  * This class is the base implementation for the others.  This
685  * reduces redundancies and bugs.
686  */
687 class FileDialogBase : public Gtk::FileChooserDialog
689 public:
691     /**
692      *
693      */
694     FileDialogBase(const Glib::ustring &title) :
695                         Gtk::FileChooserDialog(title)
696         {
697         }
699     /**
700      *
701      */
702     FileDialogBase(const Glib::ustring &title,
703                    Gtk::FileChooserAction dialogType) :
704                    Gtk::FileChooserDialog(title, dialogType)
705         {
706         }
708     /**
709      *
710      */
711     virtual ~FileDialogBase()
712         {}
714 };
718 /*#########################################################################
719 ### F I L E    O P E N
720 #########################################################################*/
722 /**
723  * Our implementation class for the FileOpenDialog interface..
724  */
725 class FileOpenDialogImpl : public FileOpenDialog, public FileDialogBase
727 public:
729     FileOpenDialogImpl(const Glib::ustring &dir,
730                        FileDialogType fileTypes,
731                        const Glib::ustring &title);
733     virtual ~FileOpenDialogImpl();
735     bool show();
737     Inkscape::Extension::Extension *getSelectionType();
739     Glib::ustring getFilename();
741     std::vector<Glib::ustring> getFilenames ();
743 protected:
747 private:
750     /**
751      * What type of 'open' are we? (open, import, place, etc)
752      */
753     FileDialogType dialogType;
755     /**
756      * Our svg preview widget
757      */
758     SVGPreview svgPreview;
760     /**
761      * Callback for seeing if the preview needs to be drawn
762      */
763     void updatePreviewCallback();
765     /**
766      * Fix to allow the user to type the file name
767      */
768     Gtk::Entry fileNameEntry;
770     /**
771      *  Create a filter menu for this type of dialog
772      */
773     void createFilterMenu();
775     /**
776      * Callback for user input into fileNameEntry
777      */
778     void fileNameEntryChangedCallback();
780     /**
781      * Callback for user changing which item is selected on the list
782      */
783     void fileSelectedCallback();
786     /**
787      * Filter name->extension lookup
788      */
789     std::map<Glib::ustring, Inkscape::Extension::Extension *> extensionMap;
791     /**
792      * The extension to use to write this file
793      */
794     Inkscape::Extension::Extension *extension;
796     /**
797      * Filename that was given
798      */
799     Glib::ustring myFilename;
801 };
807 /**
808  * Callback for checking if the preview needs to be redrawn
809  */
810 void FileOpenDialogImpl::updatePreviewCallback()
812     Glib::ustring fileName = get_preview_filename();
813     if (fileName.length() < 1)
814         return;
815     svgPreview.set(fileName, dialogType);
822 /**
823  * Callback for fileNameEntry widget
824  */
825 void FileOpenDialogImpl::fileNameEntryChangedCallback()
827     Glib::ustring rawFileName = fileNameEntry.get_text();
829     Glib::ustring fileName = Glib::filename_from_utf8(rawFileName);
831     //g_message("User hit return.  Text is '%s'\n", fileName.c_str());
833     if (!Glib::path_is_absolute(fileName)) {
834         //try appending to the current path
835         // not this way: fileName = get_current_folder() + "/" + fName;
836         std::vector<Glib::ustring> pathSegments;
837         pathSegments.push_back( get_current_folder() );
838         pathSegments.push_back( fileName );
839         fileName = Glib::build_filename(pathSegments);
840     }
842     //g_message("path:'%s'\n", fName.c_str());
844     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
845         set_current_folder(fileName);
846     } else if (Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)) {
847         //dialog with either (1) select a regular file or (2) cd to dir
848         //simulate an 'OK'
849         set_filename(fileName);
850         response(Gtk::RESPONSE_OK);
851     }
858 /**
859  * Callback for fileNameEntry widget
860  */
861 void FileOpenDialogImpl::fileSelectedCallback()
863     Glib::ustring fileName     = get_filename();
864     if (!Glib::get_charset()) //If we are not utf8
865         fileName = Glib::filename_to_utf8(fileName);
866     //g_message("User selected '%s'\n",
867     //       filename().c_str());
869 #ifdef INK_DUMP_FILENAME_CONV
870     ::dump_ustr( get_filename() );
871 #endif
872     fileNameEntry.set_text(fileName);
878 void FileOpenDialogImpl::createFilterMenu()
880     //patterns added dynamically below
881     Gtk::FileFilter allImageFilter;
882     allImageFilter.set_name(_("All Images"));
883     extensionMap[Glib::ustring(_("All Images"))]=NULL;
884     add_filter(allImageFilter);
886     Gtk::FileFilter allFilter;
887     allFilter.set_name(_("All Files"));
888     extensionMap[Glib::ustring(_("All Files"))]=NULL;
889     allFilter.add_pattern("*");
890     add_filter(allFilter);
892     //patterns added dynamically below
893     Gtk::FileFilter allInkscapeFilter;
894     allInkscapeFilter.set_name(_("All Inkscape Files"));
895     extensionMap[Glib::ustring(_("All Inkscape Files"))]=NULL;
896     add_filter(allInkscapeFilter);
898     Inkscape::Extension::DB::InputList extension_list;
899     Inkscape::Extension::db.get_input_list(extension_list);
901     for (Inkscape::Extension::DB::InputList::iterator current_item = extension_list.begin();
902          current_item != extension_list.end(); current_item++)
903     {
904         Inkscape::Extension::Input * imod = *current_item;
906         // FIXME: would be nice to grey them out instead of not listing them
907         if (imod->deactivated()) continue;
909         Glib::ustring upattern("*");
910         Glib::ustring extension = imod->get_extension();
911         fileDialogExtensionToPattern(upattern, extension);
913         Gtk::FileFilter filter;
914         Glib::ustring uname(_(imod->get_filetypename()));
915         filter.set_name(uname);
916         filter.add_pattern(upattern);
917         add_filter(filter);
918         extensionMap[uname] = imod;
920         //g_message("ext %s:%s '%s'\n", ioext->name, ioext->mimetype, upattern.c_str());
921         allInkscapeFilter.add_pattern(upattern);
922         if ( strncmp("image", imod->get_mimetype(), 5)==0 )
923             allImageFilter.add_pattern(upattern);
924     }
926     return;
931 /**
932  * Constructor.  Not called directly.  Use the factory.
933  */
934 FileOpenDialogImpl::FileOpenDialogImpl(const Glib::ustring &dir,
935                                        FileDialogType fileTypes,
936                                        const Glib::ustring &title) :
937                                        FileDialogBase(title)
941     /* One file at a time */
942     /* And also Multiple Files */
943     set_select_multiple(true);
945     /* Initalize to Autodetect */
946     extension = NULL;
947     /* No filename to start out with */
948     myFilename = "";
950     /* Set our dialog type (open, import, etc...)*/
951     dialogType = fileTypes;
954     /* Set the pwd and/or the filename */
955     if (dir.size() > 0)
956         {
957         Glib::ustring udir(dir);
958         Glib::ustring::size_type len = udir.length();
959         // leaving a trailing backslash on the directory name leads to the infamous
960         // double-directory bug on win32
961         if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
962         set_current_folder(udir.c_str());
963         }
965     //###### Add the file types menu
966     createFilterMenu();
968     //###### Add a preview widget
969     set_preview_widget(svgPreview);
970     set_preview_widget_active(true);
971     set_use_preview_label (false);
973     //Catch selection-changed events, so we can adjust the text widget
974     signal_update_preview().connect(
975          sigc::mem_fun(*this, &FileOpenDialogImpl::updatePreviewCallback) );
978     //###### Add a text entry bar, and tie it to file chooser events
979     fileNameEntry.set_text(get_current_folder());
980     set_extra_widget(fileNameEntry);
981     fileNameEntry.grab_focus();
983     //Catch when user hits [return] on the text field
984     fileNameEntry.signal_activate().connect(
985          sigc::mem_fun(*this, &FileOpenDialogImpl::fileNameEntryChangedCallback) );
987     //Catch selection-changed events, so we can adjust the text widget
988     signal_selection_changed().connect(
989          sigc::mem_fun(*this, &FileOpenDialogImpl::fileSelectedCallback) );
991     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
992     add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_OK);
1000 /**
1001  * Public factory.  Called by file.cpp, among others.
1002  */
1003 FileOpenDialog *FileOpenDialog::create(const Glib::ustring &path,
1004                                        FileDialogType fileTypes,
1005                                        const Glib::ustring &title)
1007     FileOpenDialog *dialog = new FileOpenDialogImpl(path, fileTypes, title);
1008     return dialog;
1014 /**
1015  * Destructor
1016  */
1017 FileOpenDialogImpl::~FileOpenDialogImpl()
1023 /**
1024  * Show this dialog modally.  Return true if user hits [OK]
1025  */
1026 bool
1027 FileOpenDialogImpl::show()
1029     Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());
1030     if (s.length() == 0) 
1031         s = getcwd (NULL, 0);
1032     set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing
1033     set_modal (TRUE);                      //Window
1034     sp_transientize((GtkWidget *)gobj());  //Make transient
1035     gint b = run();                        //Dialog
1036     svgPreview.showNoPreview();
1037     hide();
1039     if (b == Gtk::RESPONSE_OK)
1040         {
1041         //This is a hack, to avoid the warning messages that
1042         //Gtk::FileChooser::get_filter() returns
1043         //should be:  Gtk::FileFilter *filter = get_filter();
1044         GtkFileChooser *gtkFileChooser = Gtk::FileChooser::gobj();
1045         GtkFileFilter *filter = gtk_file_chooser_get_filter(gtkFileChooser);
1046         if (filter)
1047             {
1048             //Get which extension was chosen, if any
1049             extension = extensionMap[gtk_file_filter_get_name(filter)];
1050             }
1051         myFilename = get_filename();
1052         return TRUE;
1053         }
1054     else
1055        {
1056        return FALSE;
1057        }
1063 /**
1064  * Get the file extension type that was selected by the user. Valid after an [OK]
1065  */
1066 Inkscape::Extension::Extension *
1067 FileOpenDialogImpl::getSelectionType()
1069     return extension;
1073 /**
1074  * Get the file name chosen by the user.   Valid after an [OK]
1075  */
1076 Glib::ustring
1077 FileOpenDialogImpl::getFilename (void)
1079     return g_strdup(myFilename.c_str());
1083 /**
1084  * To Get Multiple filenames selected at-once.
1085  */
1086 std::vector<Glib::ustring>FileOpenDialogImpl::getFilenames()
1087 {    
1088     std::vector<Glib::ustring> result = get_filenames();
1089     return result;
1097 //########################################################################
1098 //# F I L E    S A V E
1099 //########################################################################
1101 class FileType
1103     public:
1104     FileType() {}
1105     ~FileType() {}
1106     Glib::ustring name;
1107     Glib::ustring pattern;
1108     Inkscape::Extension::Extension *extension;
1109 };
1111 /**
1112  * Our implementation of the FileSaveDialog interface.
1113  */
1114 class FileSaveDialogImpl : public FileSaveDialog, public FileDialogBase
1117 public:
1118     FileSaveDialogImpl(const Glib::ustring &dir,
1119                        FileDialogType fileTypes,
1120                        const Glib::ustring &title,
1121                        const Glib::ustring &default_key);
1123     virtual ~FileSaveDialogImpl();
1125     bool show();
1127     Inkscape::Extension::Extension *getSelectionType();
1129     Glib::ustring getFilename();
1132 private:
1134     /**
1135      * What type of 'open' are we? (save, export, etc)
1136      */
1137     FileDialogType dialogType;
1139     /**
1140      * Our svg preview widget
1141      */
1142     SVGPreview svgPreview;
1144     /**
1145      * Fix to allow the user to type the file name
1146      */
1147     Gtk::Entry *fileNameEntry;
1149     /**
1150      * Callback for seeing if the preview needs to be drawn
1151      */
1152     void updatePreviewCallback();
1156     /**
1157      * Allow the specification of the output file type
1158      */
1159     Gtk::HBox fileTypeBox;
1161     /**
1162      * Allow the specification of the output file type
1163      */
1164     Gtk::ComboBoxText fileTypeComboBox;
1167     /**
1168      *  Data mirror of the combo box
1169      */
1170     std::vector<FileType> fileTypes;
1172     //# Child widgets
1173     Gtk::CheckButton fileTypeCheckbox;
1176     /**
1177      * Callback for user input into fileNameEntry
1178      */
1179     void fileTypeChangedCallback();
1181     /**
1182      *  Create a filter menu for this type of dialog
1183      */
1184     void createFileTypeMenu();
1187     bool append_extension;
1189     /**
1190      * The extension to use to write this file
1191      */
1192     Inkscape::Extension::Extension *extension;
1194     /**
1195      * Callback for user input into fileNameEntry
1196      */
1197     void fileNameEntryChangedCallback();
1199     /**
1200      * Filename that was given
1201      */
1202     Glib::ustring myFilename;
1203 };
1210 /**
1211  * Callback for checking if the preview needs to be redrawn
1212  */
1213 void FileSaveDialogImpl::updatePreviewCallback()
1215     Glib::ustring fileName = get_preview_filename();
1216     if (!fileName.c_str())
1217         return;
1218     bool retval = svgPreview.set(fileName, dialogType);
1219     set_preview_widget_active(retval);
1224 /**
1225  * Callback for fileNameEntry widget
1226  */
1227 void FileSaveDialogImpl::fileNameEntryChangedCallback()
1229     if (!fileNameEntry)
1230         return;
1232     Glib::ustring fileName = fileNameEntry->get_text();
1233     if (!Glib::get_charset()) //If we are not utf8
1234         fileName = Glib::filename_to_utf8(fileName);
1236     //g_message("User hit return.  Text is '%s'\n", fileName.c_str());
1238     if (!Glib::path_is_absolute(fileName)) {
1239         //try appending to the current path
1240         // not this way: fileName = get_current_folder() + "/" + fileName;
1241         std::vector<Glib::ustring> pathSegments;
1242         pathSegments.push_back( get_current_folder() );
1243         pathSegments.push_back( fileName );
1244         fileName = Glib::build_filename(pathSegments);
1245     }
1247     //g_message("path:'%s'\n", fileName.c_str());
1249     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
1250         set_current_folder(fileName);
1251     } else if (/*Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)*/1) {
1252         //dialog with either (1) select a regular file or (2) cd to dir
1253         //simulate an 'OK'
1254         set_filename(fileName);
1255         response(Gtk::RESPONSE_OK);
1256     }
1261 /**
1262  * Callback for fileNameEntry widget
1263  */
1264 void FileSaveDialogImpl::fileTypeChangedCallback()
1266     int sel = fileTypeComboBox.get_active_row_number();
1267     if (sel<0 || sel >= (int)fileTypes.size())
1268         return;
1269     FileType type = fileTypes[sel];
1270     //g_message("selected: %s\n", type.name.c_str());
1271     Gtk::FileFilter filter;
1272     filter.add_pattern(type.pattern);
1273     set_filter(filter);
1278 void FileSaveDialogImpl::createFileTypeMenu()
1280     Inkscape::Extension::DB::OutputList extension_list;
1281     Inkscape::Extension::db.get_output_list(extension_list);
1283     for (Inkscape::Extension::DB::OutputList::iterator current_item = extension_list.begin();
1284          current_item != extension_list.end(); current_item++)
1285     {
1286         Inkscape::Extension::Output * omod = *current_item;
1288         // FIXME: would be nice to grey them out instead of not listing them
1289         if (omod->deactivated()) continue;
1291         FileType type;
1292         type.name     = (_(omod->get_filetypename()));
1293         type.pattern  = "*";
1294         Glib::ustring extension = omod->get_extension();
1295         fileDialogExtensionToPattern (type.pattern, extension);
1296         type.extension= omod;
1297         fileTypeComboBox.append_text(type.name);
1298         fileTypes.push_back(type);
1299     }
1301     //#Let user choose
1302     FileType guessType;
1303     guessType.name = _("Guess from extension");
1304     guessType.pattern = "*";
1305     guessType.extension = NULL;
1306     fileTypeComboBox.append_text(guessType.name);
1307     fileTypes.push_back(guessType);
1310     fileTypeComboBox.set_active(0);
1311     fileTypeChangedCallback(); //call at least once to set the filter
1316 /**
1317  * Constructor
1318  */
1319 FileSaveDialogImpl::FileSaveDialogImpl(const Glib::ustring &dir,
1320             FileDialogType fileTypes,
1321             const Glib::ustring &title,
1322             const Glib::ustring &default_key) :
1323             FileDialogBase(title, Gtk::FILE_CHOOSER_ACTION_SAVE)
1325     append_extension = (bool)prefs_get_int_attribute("dialogs.save_as",
1326                                                   "append_extension", 1);
1328     /* One file at a time */
1329     set_select_multiple(false);
1331     /* Initalize to Autodetect */
1332     extension = NULL;
1333     /* No filename to start out with */
1334     myFilename = "";
1336     /* Set our dialog type (save, export, etc...)*/
1337     dialogType = fileTypes;
1339     /* Set the pwd and/or the filename */
1340     if (dir.size() > 0)
1341         {
1342         Glib::ustring udir(dir);
1343         Glib::ustring::size_type len = udir.length();
1344         // leaving a trailing backslash on the directory name leads to the infamous
1345         // double-directory bug on win32
1346         if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
1347         set_current_folder(udir.c_str());
1348         }
1350     //###### Add the file types menu
1351     //createFilterMenu();
1353     //###### Do we want the .xxx extension automatically added?
1354     fileTypeCheckbox.set_label(Glib::ustring(_("Append filename extension automatically")));
1355     fileTypeCheckbox.set_active(append_extension);
1357     fileTypeBox.pack_start(fileTypeCheckbox);
1358     createFileTypeMenu();
1359     fileTypeComboBox.set_size_request(200,40);
1360     fileTypeComboBox.signal_changed().connect(
1361          sigc::mem_fun(*this, &FileSaveDialogImpl::fileTypeChangedCallback) );
1363     fileTypeBox.pack_start(fileTypeComboBox);
1365     set_extra_widget(fileTypeBox);
1366     //get_vbox()->pack_start(fileTypeBox, false, false, 0);
1367     //get_vbox()->reorder_child(fileTypeBox, 2);
1369     //###### Add a preview widget
1370     set_preview_widget(svgPreview);
1371     set_preview_widget_active(true);
1372     set_use_preview_label (false);
1374     //Catch selection-changed events, so we can adjust the text widget
1375     signal_update_preview().connect(
1376          sigc::mem_fun(*this, &FileSaveDialogImpl::updatePreviewCallback) );
1379     //Let's do some customization
1380     fileNameEntry = NULL;
1381     Gtk::Container *cont = get_toplevel();
1382     std::vector<Gtk::Entry *> entries;
1383     findEntryWidgets(cont, entries);
1384     //g_message("Found %d entry widgets\n", entries.size());
1385     if (entries.size() >=1 )
1386         {
1387         //Catch when user hits [return] on the text field
1388         fileNameEntry = entries[0];
1389         fileNameEntry->signal_activate().connect(
1390              sigc::mem_fun(*this, &FileSaveDialogImpl::fileNameEntryChangedCallback) );
1391         }
1393     //Let's do more customization
1394     std::vector<Gtk::Expander *> expanders;
1395     findExpanderWidgets(cont, expanders);
1396     //g_message("Found %d expander widgets\n", expanders.size());
1397     if (expanders.size() >=1 )
1398         {
1399         //Always show the file list
1400         Gtk::Expander *expander = expanders[0];
1401         expander->set_expanded(true);
1402         }
1405     //if (extension == NULL)
1406     //    checkbox.set_sensitive(FALSE);
1408     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1409     add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_OK);
1411     show_all_children();
1416 /**
1417  * Public factory method.  Used in file.cpp
1418  */
1419 FileSaveDialog *FileSaveDialog::create(const Glib::ustring &path,
1420                                        FileDialogType fileTypes,
1421                                        const Glib::ustring &title,
1422                                        const Glib::ustring &default_key)
1424     FileSaveDialog *dialog = new FileSaveDialogImpl(path, fileTypes, title, default_key);
1425     return dialog;
1432 /**
1433  * Destructor
1434  */
1435 FileSaveDialogImpl::~FileSaveDialogImpl()
1441 /**
1442  * Show this dialog modally.  Return true if user hits [OK]
1443  */
1444 bool
1445 FileSaveDialogImpl::show()
1447     Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());
1448     if (s.length() == 0) 
1449         s = getcwd (NULL, 0);
1450     set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing
1451     set_modal (TRUE);                      //Window
1452     sp_transientize((GtkWidget *)gobj());  //Make transient
1453     gint b = run();                        //Dialog
1454     svgPreview.showNoPreview();
1455     hide();
1457     if (b == Gtk::RESPONSE_OK)
1458         {
1459         int sel = fileTypeComboBox.get_active_row_number ();
1460         if (sel>=0 && sel< (int)fileTypes.size())
1461             {
1462             FileType &type = fileTypes[sel];
1463             extension = type.extension;
1464             }
1465         myFilename = get_filename();
1467         /*
1469         // FIXME: Why do we have more code
1471         append_extension = checkbox.get_active();
1472         prefs_set_int_attribute("dialogs.save_as", "append_extension", append_extension);
1473         prefs_set_string_attribute("dialogs.save_as", "default",
1474                   ( extension != NULL ? extension->get_id() : "" ));
1475         */
1476         return TRUE;
1477         }
1478     else
1479         {
1480         return FALSE;
1481         }
1485 /**
1486  * Get the file extension type that was selected by the user. Valid after an [OK]
1487  */
1488 Inkscape::Extension::Extension *
1489 FileSaveDialogImpl::getSelectionType()
1491     return extension;
1495 /**
1496  * Get the file name chosen by the user.   Valid after an [OK]
1497  */
1498 Glib::ustring
1499 FileSaveDialogImpl::getFilename()
1501     return myFilename;
1511 //########################################################################
1512 //# F I L E     E X P O R T
1513 //########################################################################
1516 /**
1517  * Our implementation of the FileExportDialog interface.
1518  */
1519 class FileExportDialogImpl : public FileExportDialog, public FileDialogBase
1522 public:
1523     FileExportDialogImpl(const Glib::ustring &dir,
1524                        FileDialogType fileTypes,
1525                        const Glib::ustring &title,
1526                        const Glib::ustring &default_key);
1528     virtual ~FileExportDialogImpl();
1530     bool show();
1532     Inkscape::Extension::Extension *getSelectionType();
1534     Glib::ustring getFilename();
1537 private:
1539     /**
1540      * What type of 'open' are we? (save, export, etc)
1541      */
1542     FileDialogType dialogType;
1544     /**
1545      * Our svg preview widget
1546      */
1547     SVGPreview svgPreview;
1549     /**
1550      * Fix to allow the user to type the file name
1551      */
1552     Gtk::Entry *fileNameEntry;
1554     /**
1555      * Callback for seeing if the preview needs to be drawn
1556      */
1557     void updatePreviewCallback();
1561     /**
1562      * Allow the specification of the output file type
1563      */
1564     Gtk::HBox fileTypeBox;
1566     /**
1567      * Allow the specification of the output file type
1568      */
1569     Gtk::ComboBoxText fileTypeComboBox;
1572     /**
1573      *  Data mirror of the combo box
1574      */
1575     std::vector<FileType> fileTypes;
1577     //# Child widgets
1578     Gtk::CheckButton fileTypeCheckbox;
1581     /**
1582      * Callback for user input into fileNameEntry
1583      */
1584     void fileTypeChangedCallback();
1586     /**
1587      *  Create a filter menu for this type of dialog
1588      */
1589     void createFileTypeMenu();
1592     bool append_extension;
1594     /**
1595      * The extension to use to write this file
1596      */
1597     Inkscape::Extension::Extension *extension;
1599     /**
1600      * Callback for user input into fileNameEntry
1601      */
1602     void fileNameEntryChangedCallback();
1604     /**
1605      * Filename that was given
1606      */
1607     Glib::ustring myFilename;
1608 };
1615 /**
1616  * Callback for checking if the preview needs to be redrawn
1617  */
1618 void FileExportDialogImpl::updatePreviewCallback()
1620     Glib::ustring fileName = get_preview_filename();
1621     if (!fileName.c_str())
1622         return;
1623     bool retval = svgPreview.set(fileName, dialogType);
1624     set_preview_widget_active(retval);
1629 /**
1630  * Callback for fileNameEntry widget
1631  */
1632 void FileExportDialogImpl::fileNameEntryChangedCallback()
1634     if (!fileNameEntry)
1635         return;
1637     Glib::ustring fileName = fileNameEntry->get_text();
1638     if (!Glib::get_charset()) //If we are not utf8
1639         fileName = Glib::filename_to_utf8(fileName);
1641     //g_message("User hit return.  Text is '%s'\n", fileName.c_str());
1643     if (!Glib::path_is_absolute(fileName)) {
1644         //try appending to the current path
1645         // not this way: fileName = get_current_folder() + "/" + fileName;
1646         std::vector<Glib::ustring> pathSegments;
1647         pathSegments.push_back( get_current_folder() );
1648         pathSegments.push_back( fileName );
1649         fileName = Glib::build_filename(pathSegments);
1650     }
1652     //g_message("path:'%s'\n", fileName.c_str());
1654     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
1655         set_current_folder(fileName);
1656     } else if (/*Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)*/1) {
1657         //dialog with either (1) select a regular file or (2) cd to dir
1658         //simulate an 'OK'
1659         set_filename(fileName);
1660         response(Gtk::RESPONSE_OK);
1661     }
1666 /**
1667  * Callback for fileNameEntry widget
1668  */
1669 void FileExportDialogImpl::fileTypeChangedCallback()
1671     int sel = fileTypeComboBox.get_active_row_number();
1672     if (sel<0 || sel >= (int)fileTypes.size())
1673         return;
1674     FileType type = fileTypes[sel];
1675     //g_message("selected: %s\n", type.name.c_str());
1676     Gtk::FileFilter filter;
1677     filter.add_pattern(type.pattern);
1678     set_filter(filter);
1683 void FileExportDialogImpl::createFileTypeMenu()
1685     Inkscape::Extension::DB::OutputList extension_list;
1686     Inkscape::Extension::db.get_output_list(extension_list);
1688     for (Inkscape::Extension::DB::OutputList::iterator current_item = extension_list.begin();
1689          current_item != extension_list.end(); current_item++)
1690     {
1691         Inkscape::Extension::Output * omod = *current_item;
1693         // FIXME: would be nice to grey them out instead of not listing them
1694         if (omod->deactivated()) continue;
1696         FileType type;
1697         type.name     = (_(omod->get_filetypename()));
1698         type.pattern  = "*";
1699         Glib::ustring extension = omod->get_extension();
1700         fileDialogExtensionToPattern (type.pattern, extension);
1701         type.extension= omod;
1702         fileTypeComboBox.append_text(type.name);
1703         fileTypes.push_back(type);
1704     }
1706     //#Let user choose
1707     FileType guessType;
1708     guessType.name = _("Guess from extension");
1709     guessType.pattern = "*";
1710     guessType.extension = NULL;
1711     fileTypeComboBox.append_text(guessType.name);
1712     fileTypes.push_back(guessType);
1715     fileTypeComboBox.set_active(0);
1716     fileTypeChangedCallback(); //call at least once to set the filter
1720 /**
1721  * Constructor
1722  */
1723 FileExportDialogImpl::FileExportDialogImpl(const Glib::ustring &dir,
1724             FileDialogType fileTypes,
1725             const Glib::ustring &title,
1726             const Glib::ustring &default_key) :
1727             FileDialogBase(title, Gtk::FILE_CHOOSER_ACTION_SAVE)
1729     append_extension = (bool)prefs_get_int_attribute("dialogs.save_as", "append_extension", 1);
1731     /* One file at a time */
1732     set_select_multiple(false);
1734     /* Initalize to Autodetect */
1735     extension = NULL;
1736     /* No filename to start out with */
1737     myFilename = "";
1739     /* Set our dialog type (save, export, etc...)*/
1740     dialogType = fileTypes;
1742     /* Set the pwd and/or the filename */
1743     if (dir.size()>0)
1744         {
1745         Glib::ustring udir(dir);
1746         Glib::ustring::size_type len = udir.length();
1747         // leaving a trailing backslash on the directory name leads to the infamous
1748         // double-directory bug on win32
1749         if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
1750         set_current_folder(udir.c_str());
1751         }
1753     //###### Add the file types menu
1754     //createFilterMenu();
1756     //###### Do we want the .xxx extension automatically added?
1757     fileTypeCheckbox.set_label(Glib::ustring(_("Append filename extension automatically")));
1758     fileTypeCheckbox.set_active(append_extension);
1760     fileTypeBox.pack_start(fileTypeCheckbox);
1761     createFileTypeMenu();
1762     fileTypeComboBox.set_size_request(200,40);
1763     fileTypeComboBox.signal_changed().connect(
1764          sigc::mem_fun(*this, &FileExportDialogImpl::fileTypeChangedCallback) );
1766     fileTypeBox.pack_start(fileTypeComboBox);
1768     set_extra_widget(fileTypeBox);
1769     //get_vbox()->pack_start(fileTypeBox, false, false, 0);
1770     //get_vbox()->reorder_child(fileTypeBox, 2);
1772     //###### Add a preview widget
1773     set_preview_widget(svgPreview);
1774     set_preview_widget_active(true);
1775     set_use_preview_label (false);
1777     //Catch selection-changed events, so we can adjust the text widget
1778     signal_update_preview().connect(
1779          sigc::mem_fun(*this, &FileExportDialogImpl::updatePreviewCallback) );
1782     //Let's do some customization
1783     fileNameEntry = NULL;
1784     Gtk::Container *cont = get_toplevel();
1785     std::vector<Gtk::Entry *> entries;
1786     findEntryWidgets(cont, entries);
1787     //g_message("Found %d entry widgets\n", entries.size());
1788     if (entries.size() >=1 )
1789         {
1790         //Catch when user hits [return] on the text field
1791         fileNameEntry = entries[0];
1792         fileNameEntry->signal_activate().connect(
1793              sigc::mem_fun(*this, &FileExportDialogImpl::fileNameEntryChangedCallback) );
1794         }
1796     //Let's do more customization
1797     std::vector<Gtk::Expander *> expanders;
1798     findExpanderWidgets(cont, expanders);
1799     //g_message("Found %d expander widgets\n", expanders.size());
1800     if (expanders.size() >=1 )
1801         {
1802         //Always show the file list
1803         Gtk::Expander *expander = expanders[0];
1804         expander->set_expanded(true);
1805         }
1808     //if (extension == NULL)
1809     //    checkbox.set_sensitive(FALSE);
1811     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1812     add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_OK);
1814     show_all_children();
1819 /**
1820  * Public factory method.  Used in file.cpp
1821  */
1822 FileExportDialog *FileExportDialog::create(const Glib::ustring &path,
1823                                        FileDialogType fileTypes,
1824                                        const Glib::ustring &title,
1825                                        const Glib::ustring &default_key)
1827     FileExportDialog *dialog = new FileExportDialogImpl(path, fileTypes, title, default_key);
1828     return dialog;
1835 /**
1836  * Destructor
1837  */
1838 FileExportDialogImpl::~FileExportDialogImpl()
1844 /**
1845  * Show this dialog modally.  Return true if user hits [OK]
1846  */
1847 bool
1848 FileExportDialogImpl::show()
1850     Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());
1851     if (s.length() == 0) 
1852         s = getcwd (NULL, 0);
1853     set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing
1854     set_modal (TRUE);                      //Window
1855     sp_transientize((GtkWidget *)gobj());  //Make transient
1856     gint b = run();                        //Dialog
1857     svgPreview.showNoPreview();
1858     hide();
1860     if (b == Gtk::RESPONSE_OK)
1861         {
1862         int sel = fileTypeComboBox.get_active_row_number ();
1863         if (sel>=0 && sel< (int)fileTypes.size())
1864             {
1865             FileType &type = fileTypes[sel];
1866             extension = type.extension;
1867             }
1868         myFilename = get_filename();
1870         /*
1872         // FIXME: Why do we have more code
1874         append_extension = checkbox.get_active();
1875         prefs_set_int_attribute("dialogs.save_as", "append_extension", append_extension);
1876         prefs_set_string_attribute("dialogs.save_as", "default",
1877                   ( extension != NULL ? extension->get_id() : "" ));
1878         */
1879         return TRUE;
1880         }
1881     else
1882         {
1883         return FALSE;
1884         }
1888 /**
1889  * Get the file extension type that was selected by the user. Valid after an [OK]
1890  */
1891 Inkscape::Extension::Extension *
1892 FileExportDialogImpl::getSelectionType()
1894     return extension;
1898 /**
1899  * Get the file name chosen by the user.   Valid after an [OK]
1900  */
1901 Glib::ustring
1902 FileExportDialogImpl::getFilename()
1904     return myFilename;
1910 } //namespace Dialog
1911 } //namespace UI
1912 } //namespace Inkscape
1915 /*
1916   Local Variables:
1917   mode:c++
1918   c-file-style:"stroustrup"
1919   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1920   indent-tabs-mode:nil
1921   fill-column:99
1922   End:
1923 */
1924 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :