Code

e5f01e8bf9a58705a84db20bd7d54534c279a5fa
[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/colorbutton.h>
33 #include <gtkmm/frame.h>
34 #include <gtkmm/filechooserdialog.h>
35 #include <gtkmm/menubar.h>
36 #include <gtkmm/menu.h>
37 #include <gtkmm/entry.h>
38 #include <gtkmm/expander.h>
39 #include <gtkmm/comboboxtext.h>
40 #include <gtkmm/stock.h>
41 #include <gdkmm/pixbuf.h>
43 #include "prefs-utils.h"
44 #include <dialogs/dialog-events.h>
45 #include <extension/input.h>
46 #include <extension/output.h>
47 #include <extension/db.h>
48 #include "inkscape.h"
49 #include "svg-view-widget.h"
50 #include "filedialog.h"
51 #include "gc-core.h"
53 //For export dialog
54 #include "ui/widget/scalar-unit.h"
57 #undef INK_DUMP_FILENAME_CONV
59 #ifdef INK_DUMP_FILENAME_CONV
60 void dump_str( const gchar* str, const gchar* prefix );
61 void dump_ustr( const Glib::ustring& ustr );
62 #endif
64 namespace Inkscape
65 {
66 namespace UI
67 {
68 namespace Dialog
69 {
75 //########################################################################
76 //### U T I L I T Y
77 //########################################################################
79 /**
80     \brief  A quick function to turn a standard extension into a searchable
81             pattern for the file dialogs
82     \param  pattern  The patter that the extension should be written to
83     \param  in_file_extension  The C string that represents the extension
85     This function just goes through the string, and takes all characters
86     and puts a [<upper><lower>] so that both are searched and shown in
87     the file dialog.  This function edits the pattern string to make
88     this happen.
89 */
90 static void
91 fileDialogExtensionToPattern(Glib::ustring &pattern,
92                       Glib::ustring &extension)
93 {
94     for (unsigned int i = 0; i < extension.length(); i++ )
95         {
96         Glib::ustring::value_type ch = extension[i];
97         if ( Glib::Unicode::isalpha(ch) )
98             {
99             pattern += '[';
100             pattern += Glib::Unicode::toupper(ch);
101             pattern += Glib::Unicode::tolower(ch);
102             pattern += ']';
103             }
104         else
105             {
106             pattern += ch;
107             }
108         }
112 /**
113  *  Hack:  Find all entry widgets in a container
114  */
115 void findEntryWidgets(Gtk::Container *parent,
116                       std::vector<Gtk::Entry *> &result)
118     if (!parent)
119         return;
120     std::vector<Gtk::Widget *> children = parent->get_children();
121     for (unsigned int i=0; i<children.size() ; i++)
122         {
123         Gtk::Widget *child = children[i];
124         GtkWidget *wid = child->gobj();
125         if (GTK_IS_ENTRY(wid))
126            result.push_back((Gtk::Entry *)child);
127         else if (GTK_IS_CONTAINER(wid))
128             findEntryWidgets((Gtk::Container *)child, result);
129         }
136 /**
137  *  Hack:  Find all expander widgets in a container
138  */
139 void findExpanderWidgets(Gtk::Container *parent,
140                         std::vector<Gtk::Expander *> &result)
142     if (!parent)
143         return;
144     std::vector<Gtk::Widget *> children = parent->get_children();
145     for (unsigned int i=0; i<children.size() ; i++)
146         {
147         Gtk::Widget *child = children[i];
148         GtkWidget *wid = child->gobj();
149         if (GTK_IS_EXPANDER(wid))
150            result.push_back((Gtk::Expander *)child);
151         else if (GTK_IS_CONTAINER(wid))
152             findExpanderWidgets((Gtk::Container *)child, result);
153         }
158 /*#########################################################################
159 ### SVG Preview Widget
160 #########################################################################*/
161 /**
162  * Simple class for displaying an SVG file in the "preview widget."
163  * Currently, this is just a wrapper of the sp_svg_view Gtk widget.
164  * Hopefully we will eventually replace with a pure Gtkmm widget.
165  */
166 class SVGPreview : public Gtk::VBox
168 public:
170     SVGPreview();
172     ~SVGPreview();
174     bool setDocument(SPDocument *doc);
176     bool setFileName(Glib::ustring &fileName);
178     bool setFromMem(char const *xmlBuffer);
180     bool set(Glib::ustring &fileName, int dialogType);
182     bool setURI(URI &uri);
184     /**
185      * Show image embedded in SVG
186      */
187     void showImage(Glib::ustring &fileName);
189     /**
190      * Show the "No preview" image
191      */
192     void showNoPreview();
194     /**
195      * Show the "Too large" image
196      */
197     void showTooLarge(long fileLength);
199 private:
200     /**
201      * The svg document we are currently showing
202      */
203     SPDocument *document;
205     /**
206      * The sp_svg_view widget
207      */
208     GtkWidget *viewerGtk;
210     /**
211      * are we currently showing the "no preview" image?
212      */
213     bool showingNoPreview;
215 };
218 bool SVGPreview::setDocument(SPDocument *doc)
220     if (document)
221         sp_document_unref(document);
223     sp_document_ref(doc);
224     document = doc;
226     //This should remove it from the box, and free resources
227     if (viewerGtk) {
228         gtk_widget_destroy(viewerGtk);
229     }
231     viewerGtk  = sp_svg_view_widget_new(doc);
232     GtkWidget *vbox = (GtkWidget *)gobj();
233     gtk_box_pack_start(GTK_BOX(vbox), viewerGtk, TRUE, TRUE, 0);
234     gtk_widget_show(viewerGtk);
236     return true;
239 bool SVGPreview::setFileName(Glib::ustring &theFileName)
241     Glib::ustring fileName = theFileName;
243     fileName = Glib::filename_to_utf8(fileName);
245     SPDocument *doc = sp_document_new (fileName.c_str(), 0);
246     if (!doc) {
247         g_warning("SVGView: error loading document '%s'\n", fileName.c_str());
248         return false;
249     }
251     setDocument(doc);
253     sp_document_unref(doc);
255     return true;
260 bool SVGPreview::setFromMem(char const *xmlBuffer)
262     if (!xmlBuffer)
263         return false;
265     gint len = (gint)strlen(xmlBuffer);
266     SPDocument *doc = sp_document_new_from_mem(xmlBuffer, len, 0);
267     if (!doc) {
268         g_warning("SVGView: error loading buffer '%s'\n",xmlBuffer);
269         return false;
270     }
272     setDocument(doc);
274     sp_document_unref(doc);
276     Inkscape::GC::request_early_collection();
278     return true;
283 void SVGPreview::showImage(Glib::ustring &theFileName)
285     Glib::ustring fileName = theFileName;
288     /*#####################################
289     # LET'S HAVE SOME FUN WITH SVG!
290     # Instead of just loading an image, why
291     # don't we make a lovely little svg and
292     # display it nicely?
293     #####################################*/
295     //Arbitrary size of svg doc -- rather 'portrait' shaped
296     gint previewWidth  = 400;
297     gint previewHeight = 600;
299     //Get some image info. Smart pointer does not need to be deleted
300     Glib::RefPtr<Gdk::Pixbuf> img = Gdk::Pixbuf::create_from_file(fileName);
301     gint imgWidth  = img->get_width();
302     gint imgHeight = img->get_height();
304     //Find the minimum scale to fit the image inside the preview area
305     double scaleFactorX = (0.9 *(double)previewWidth)  / ((double)imgWidth);
306     double scaleFactorY = (0.9 *(double)previewHeight) / ((double)imgHeight);
307     double scaleFactor = scaleFactorX;
308     if (scaleFactorX > scaleFactorY)
309         scaleFactor = scaleFactorY;
311     //Now get the resized values
312     gint scaledImgWidth  = (int) (scaleFactor * (double)imgWidth);
313     gint scaledImgHeight = (int) (scaleFactor * (double)imgHeight);
315     //center the image on the area
316     gint imgX = (previewWidth  - scaledImgWidth)  / 2;
317     gint imgY = (previewHeight - scaledImgHeight) / 2;
319     //wrap a rectangle around the image
320     gint rectX      = imgX-1;
321     gint rectY      = imgY-1;
322     gint rectWidth  = scaledImgWidth +2;
323     gint rectHeight = scaledImgHeight+2;
325     //Our template.  Modify to taste
326     gchar const *xformat =
327           "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
328           "<svg\n"
329           "xmlns=\"http://www.w3.org/2000/svg\"\n"
330           "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
331           "width=\"%d\" height=\"%d\">\n"
332           "<rect\n"
333           "  style=\"fill:#eeeeee;stroke:none\"\n"
334           "  x=\"-100\" y=\"-100\" width=\"4000\" height=\"4000\"/>\n"
335           "<image x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"\n"
336           "xlink:href=\"%s\"/>\n"
337           "<rect\n"
338           "  style=\"fill:none;"
339           "    stroke:#000000;stroke-width:1.0;"
340           "    stroke-linejoin:miter;stroke-opacity:1.0000000;"
341           "    stroke-miterlimit:4.0000000;stroke-dasharray:none\"\n"
342           "  x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"/>\n"
343           "<text\n"
344           "  style=\"font-size:24.000000;font-style:normal;font-weight:normal;"
345           "    fill:#000000;fill-opacity:1.0000000;stroke:none;"
346           "    font-family:Bitstream Vera Sans\"\n"
347           "  x=\"10\" y=\"26\">%d x %d</text>\n"
348           "</svg>\n\n";
350     //if (!Glib::get_charset()) //If we are not utf8
351     fileName = Glib::filename_to_utf8(fileName);
353     //Fill in the template
354     /* FIXME: Do proper XML quoting for fileName. */
355     gchar *xmlBuffer = g_strdup_printf(xformat,
356            previewWidth, previewHeight,
357            imgX, imgY, scaledImgWidth, scaledImgHeight,
358            fileName.c_str(),
359            rectX, rectY, rectWidth, rectHeight,
360            imgWidth, imgHeight);
362     //g_message("%s\n", xmlBuffer);
364     //now show it!
365     setFromMem(xmlBuffer);
366     g_free(xmlBuffer);
371 void SVGPreview::showNoPreview()
373     //Are we already showing it?
374     if (showingNoPreview)
375         return;
377     //Arbitrary size of svg doc -- rather 'portrait' shaped
378     gint previewWidth  = 300;
379     gint previewHeight = 600;
381     //Our template.  Modify to taste
382     gchar const *xformat =
383           "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
384           "<svg\n"
385           "xmlns=\"http://www.w3.org/2000/svg\"\n"
386           "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
387           "width=\"%d\" height=\"%d\">\n"
388           "<g transform=\"translate(-190,24.27184)\" style=\"opacity:0.12\">\n"
389           "<path\n"
390           "style=\"font-size:12;fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.936193pt\"\n"
391           "d=\"M 397.64309 320.25301 L 280.39197 282.517 L 250.74227 124.83447 L 345.08225 "
392           "29.146783 L 393.59996 46.667064 L 483.89679 135.61619 L 397.64309 320.25301 z \"\n"
393           "id=\"whiteSpace\" />\n"
394           "<path\n"
395           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
396           "d=\"M 476.95792 339.17168 C 495.78197 342.93607 499.54842 356.11361 495.78197 359.87802 "
397           "C 492.01856 363.6434 482.6065 367.40781 475.07663 361.76014 C 467.54478 "
398           "356.11361 467.54478 342.93607 476.95792 339.17168 z \"\n"
399           "id=\"droplet01\" />\n"
400           "<path\n"
401           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
402           "d=\"M 286.46194 340.42914 C 284.6277 340.91835 269.30405 327.71337 257.16909 333.8338 "
403           "C 245.03722 339.95336 236.89276 353.65666 248.22676 359.27982 C 259.56184 364.90298 "
404           "267.66433 358.41867 277.60113 351.44119 C 287.53903 344.46477 "
405           "287.18046 343.1206 286.46194 340.42914 z \"\n"
406           "id=\"droplet02\" />\n"
407           "<path\n"
408           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
409           "d=\"M 510.35756 306.92856 C 520.59494 304.36879 544.24333 306.92856 540.47688 321.98634 "
410           "C 536.71354 337.04806 504.71297 331.39827 484.00371 323.87156 C 482.12141 "
411           "308.81083 505.53237 308.13423 510.35756 306.92856 z \"\n"
412           "id=\"droplet03\" />\n"
413           "<path\n"
414           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
415           "d=\"M 359.2403 21.362537 C 347.92693 21.362537 336.6347 25.683095 327.96556 34.35223 "
416           "L 173.87387 188.41466 C 165.37697 196.9114 161.1116 207.95813 160.94269 219.04577 "
417           "L 160.88418 219.04577 C 160.88418 219.08524 160.94076 219.12322 160.94269 219.16279 "
418           "C 160.94033 219.34888 160.88418 219.53256 160.88418 219.71865 L 161.14748 219.71865 "
419           "C 164.0966 230.93917 240.29699 245.24198 248.79866 253.74346 C 261.63771 266.58263 "
420           "199.5652 276.01151 212.4041 288.85074 C 225.24316 301.68979 289.99433 313.6933 302.8346 "
421           "326.53254 C 315.67368 339.37161 276.5961 353.04289 289.43532 365.88196 C 302.27439 "
422           "378.72118 345.40201 362.67257 337.5908 396.16198 C 354.92909 413.50026 391.10302 "
423           "405.2208 415.32417 387.88252 C 428.16323 375.04345 390.6948 376.17577 403.53397 "
424           "363.33668 C 416.37304 350.49745 448.78128 350.4282 476.08902 319.71589 C 465.09739 "
425           "302.62116 429.10801 295.34136 441.94719 282.50217 C 454.78625 269.66311 479.74708 "
426           "276.18423 533.60644 251.72479 C 559.89837 239.78398 557.72636 230.71459 557.62567 "
427           "219.71865 C 557.62356 219.48727 557.62567 219.27892 557.62567 219.04577 L 557.56716 "
428           "219.04577 C 557.3983 207.95812 553.10345 196.9114 544.60673 188.41466 L 390.54428 "
429           "34.35223 C 381.87515 25.683095 370.55366 21.362537 359.2403 21.362537 z M 357.92378 "
430           "41.402939 C 362.95327 41.533963 367.01541 45.368018 374.98006 50.530832 L 447.76915 "
431           "104.50827 C 448.56596 105.02498 449.32484 105.564 450.02187 106.11735 C 450.7189 106.67062 "
432           "451.3556 107.25745 451.95277 107.84347 C 452.54997 108.42842 453.09281 109.01553 453.59111 "
433           "109.62808 C 454.08837 110.24052 454.53956 110.86661 454.93688 111.50048 C 455.33532 112.13538 "
434           "455.69164 112.78029 455.9901 113.43137 C 456.28877 114.08363 456.52291 114.75639 456.7215 "
435           "115.42078 C 456.92126 116.08419 457.08982 116.73973 457.18961 117.41019 C 457.28949 "
436           "118.08184 457.33588 118.75535 457.33588 119.42886 L 414.21245 98.598549 L 409.9118 "
437           "131.16055 L 386.18512 120.04324 L 349.55654 144.50131 L 335.54288 96.1703 L 317.4919 "
438           "138.4453 L 267.08369 143.47735 L 267.63956 121.03795 C 267.63956 115.64823 296.69685 "
439           "77.915899 314.39075 68.932902 L 346.77721 45.674327 C 351.55594 42.576634 354.90608 "
440           "41.324327 357.92378 41.402939 z M 290.92738 261.61333 C 313.87149 267.56365 339.40299 "
441           "275.37038 359.88393 275.50997 L 360.76161 284.72563 C 343.2235 282.91785 306.11346 "
442           "274.45012 297.36372 269.98057 L 290.92738 261.61333 z \"\n"
443           "id=\"mountainDroplet\" />\n"
444           "</g> <g transform=\"translate(-20,0)\">\n"
445           "<text xml:space=\"preserve\"\n"
446           "style=\"font-size:32.000000;font-style:normal;font-variant:normal;font-weight:bold;"
447           "font-stretch:normal;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000pt;"
448           "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
449           "font-family:Bitstream Vera Sans;text-anchor:middle;writing-mode:lr\"\n"
450           "x=\"190\" y=\"240\">%s</text></g>\n"
451           "</svg>\n\n";
453     //Fill in the template
454     gchar *xmlBuffer = g_strdup_printf(xformat,
455            previewWidth, previewHeight, _("No preview"));
457     //g_message("%s\n", xmlBuffer);
459     //now show it!
460     setFromMem(xmlBuffer);
461     g_free(xmlBuffer);
462     showingNoPreview = true;
466 void SVGPreview::showTooLarge(long fileLength)
469     //Arbitrary size of svg doc -- rather 'portrait' shaped
470     gint previewWidth  = 300;
471     gint previewHeight = 600;
473     //Our template.  Modify to taste
474     gchar const *xformat =
475           "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
476           "<svg\n"
477           "xmlns=\"http://www.w3.org/2000/svg\"\n"
478           "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
479           "width=\"%d\" height=\"%d\">\n"
480           "<g transform=\"translate(-170,24.27184)\" style=\"opacity:0.12\">\n"
481           "<path\n"
482           "style=\"font-size:12;fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.936193pt\"\n"
483           "d=\"M 397.64309 320.25301 L 280.39197 282.517 L 250.74227 124.83447 L 345.08225 "
484           "29.146783 L 393.59996 46.667064 L 483.89679 135.61619 L 397.64309 320.25301 z \"\n"
485           "id=\"whiteSpace\" />\n"
486           "<path\n"
487           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
488           "d=\"M 476.95792 339.17168 C 495.78197 342.93607 499.54842 356.11361 495.78197 359.87802 "
489           "C 492.01856 363.6434 482.6065 367.40781 475.07663 361.76014 C 467.54478 "
490           "356.11361 467.54478 342.93607 476.95792 339.17168 z \"\n"
491           "id=\"droplet01\" />\n"
492           "<path\n"
493           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
494           "d=\"M 286.46194 340.42914 C 284.6277 340.91835 269.30405 327.71337 257.16909 333.8338 "
495           "C 245.03722 339.95336 236.89276 353.65666 248.22676 359.27982 C 259.56184 364.90298 "
496           "267.66433 358.41867 277.60113 351.44119 C 287.53903 344.46477 "
497           "287.18046 343.1206 286.46194 340.42914 z \"\n"
498           "id=\"droplet02\" />\n"
499           "<path\n"
500           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
501           "d=\"M 510.35756 306.92856 C 520.59494 304.36879 544.24333 306.92856 540.47688 321.98634 "
502           "C 536.71354 337.04806 504.71297 331.39827 484.00371 323.87156 C 482.12141 "
503           "308.81083 505.53237 308.13423 510.35756 306.92856 z \"\n"
504           "id=\"droplet03\" />\n"
505           "<path\n"
506           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
507           "d=\"M 359.2403 21.362537 C 347.92693 21.362537 336.6347 25.683095 327.96556 34.35223 "
508           "L 173.87387 188.41466 C 165.37697 196.9114 161.1116 207.95813 160.94269 219.04577 "
509           "L 160.88418 219.04577 C 160.88418 219.08524 160.94076 219.12322 160.94269 219.16279 "
510           "C 160.94033 219.34888 160.88418 219.53256 160.88418 219.71865 L 161.14748 219.71865 "
511           "C 164.0966 230.93917 240.29699 245.24198 248.79866 253.74346 C 261.63771 266.58263 "
512           "199.5652 276.01151 212.4041 288.85074 C 225.24316 301.68979 289.99433 313.6933 302.8346 "
513           "326.53254 C 315.67368 339.37161 276.5961 353.04289 289.43532 365.88196 C 302.27439 "
514           "378.72118 345.40201 362.67257 337.5908 396.16198 C 354.92909 413.50026 391.10302 "
515           "405.2208 415.32417 387.88252 C 428.16323 375.04345 390.6948 376.17577 403.53397 "
516           "363.33668 C 416.37304 350.49745 448.78128 350.4282 476.08902 319.71589 C 465.09739 "
517           "302.62116 429.10801 295.34136 441.94719 282.50217 C 454.78625 269.66311 479.74708 "
518           "276.18423 533.60644 251.72479 C 559.89837 239.78398 557.72636 230.71459 557.62567 "
519           "219.71865 C 557.62356 219.48727 557.62567 219.27892 557.62567 219.04577 L 557.56716 "
520           "219.04577 C 557.3983 207.95812 553.10345 196.9114 544.60673 188.41466 L 390.54428 "
521           "34.35223 C 381.87515 25.683095 370.55366 21.362537 359.2403 21.362537 z M 357.92378 "
522           "41.402939 C 362.95327 41.533963 367.01541 45.368018 374.98006 50.530832 L 447.76915 "
523           "104.50827 C 448.56596 105.02498 449.32484 105.564 450.02187 106.11735 C 450.7189 106.67062 "
524           "451.3556 107.25745 451.95277 107.84347 C 452.54997 108.42842 453.09281 109.01553 453.59111 "
525           "109.62808 C 454.08837 110.24052 454.53956 110.86661 454.93688 111.50048 C 455.33532 112.13538 "
526           "455.69164 112.78029 455.9901 113.43137 C 456.28877 114.08363 456.52291 114.75639 456.7215 "
527           "115.42078 C 456.92126 116.08419 457.08982 116.73973 457.18961 117.41019 C 457.28949 "
528           "118.08184 457.33588 118.75535 457.33588 119.42886 L 414.21245 98.598549 L 409.9118 "
529           "131.16055 L 386.18512 120.04324 L 349.55654 144.50131 L 335.54288 96.1703 L 317.4919 "
530           "138.4453 L 267.08369 143.47735 L 267.63956 121.03795 C 267.63956 115.64823 296.69685 "
531           "77.915899 314.39075 68.932902 L 346.77721 45.674327 C 351.55594 42.576634 354.90608 "
532           "41.324327 357.92378 41.402939 z M 290.92738 261.61333 C 313.87149 267.56365 339.40299 "
533           "275.37038 359.88393 275.50997 L 360.76161 284.72563 C 343.2235 282.91785 306.11346 "
534           "274.45012 297.36372 269.98057 L 290.92738 261.61333 z \"\n"
535           "id=\"mountainDroplet\" />\n"
536           "</g>\n"
537           "<text xml:space=\"preserve\"\n"
538           "style=\"font-size:32.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=\"170\" y=\"215\">%5.1f MB</text>\n"
543           "<text xml:space=\"preserve\"\n"
544           "style=\"font-size:24.000000;font-style:normal;font-variant:normal;font-weight:bold;"
545           "font-stretch:normal;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000pt;"
546           "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
547           "font-family:Bitstream Vera Sans;text-anchor:middle;writing-mode:lr\"\n"
548           "x=\"180\" y=\"245\">%s</text>\n"
549           "</svg>\n\n";
551     //Fill in the template
552     double floatFileLength = ((double)fileLength) / 1048576.0;
553     //printf("%ld %f\n", fileLength, floatFileLength);
554     gchar *xmlBuffer = g_strdup_printf(xformat,
555            previewWidth, previewHeight, floatFileLength,
556            _("too large for preview"));
558     //g_message("%s\n", xmlBuffer);
560     //now show it!
561     setFromMem(xmlBuffer);
562     g_free(xmlBuffer);
566 static bool
567 hasSuffix(Glib::ustring &str, Glib::ustring &ext)
569     int strLen = str.length();
570     int extLen = ext.length();
571     if (extLen > strLen)
572     {
573         return false;
574     }
575     int strpos = strLen-1;
576     for (int extpos = extLen-1 ; extpos>=0 ; extpos--, strpos--)
577     {
578         Glib::ustring::value_type ch = str[strpos];
579         if (ch != ext[extpos])
580         {
581             if ( ((ch & 0xff80) != 0) ||
582                  static_cast<Glib::ustring::value_type>( g_ascii_tolower( static_cast<gchar>(0x07f & ch) ) ) != ext[extpos] )
583             {
584                 return false;
585             }
586         }
587     }
588     return true;
592 /**
593  * Return true if the image is loadable by Gdk, else false
594  */
595 static bool
596 isValidImageFile(Glib::ustring &fileName)
598     std::vector<Gdk::PixbufFormat>formats = Gdk::Pixbuf::get_formats();
599     for (unsigned int i=0; i<formats.size(); i++)
600         {
601         Gdk::PixbufFormat format = formats[i];
602         std::vector<Glib::ustring>extensions = format.get_extensions();
603         for (unsigned int j=0; j<extensions.size(); j++)
604             {
605             Glib::ustring ext = extensions[j];
606             if (hasSuffix(fileName, ext))
607                 {
608                 return true;
609                 }
610             }
611         }
612     return false;
615 bool SVGPreview::set(Glib::ustring &fileName, int dialogType)
618     if (!Glib::file_test(fileName, Glib::FILE_TEST_EXISTS))
619         return false;
621     gchar *fName = (gchar *)fileName.c_str();
622     //g_message("fname:%s\n", fName);
625     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR))
626         {
627         struct stat info;
628         if (stat(fName, &info))
629             {
630             return FALSE;
631             }
632         long fileLen = info.st_size;
633         if (fileLen > 0x150000L)
634             {
635             showingNoPreview = false;
636             showTooLarge(fileLen);
637             return FALSE;
638             }
639         }
641     Glib::ustring svg = ".svg";
642     Glib::ustring svgz = ".svgz";
644     if ((dialogType == SVG_TYPES || dialogType == IMPORT_TYPES) &&
645            (hasSuffix(fileName, svg) || hasSuffix(fileName, svgz)   )
646          )
647         {
648         bool retval = setFileName(fileName);
649         showingNoPreview = false;
650         return retval;
651         }
652     else if (isValidImageFile(fileName))
653         {
654         showImage(fileName);
655         showingNoPreview = false;
656         return true;
657         }
658     else
659         {
660         showNoPreview();
661         return false;
662         }
666 SVGPreview::SVGPreview()
668     if (!INKSCAPE)
669         inkscape_application_init("",false);
670     document = NULL;
671     viewerGtk = NULL;
672     set_size_request(150,150);
673     showingNoPreview = false;
676 SVGPreview::~SVGPreview()
685 /*#########################################################################
686 ### F I L E     D I A L O G    B A S E    C L A S S
687 #########################################################################*/
689 /**
690  * This class is the base implementation for the others.  This
691  * reduces redundancies and bugs.
692  */
693 class FileDialogBase : public Gtk::FileChooserDialog
695 public:
697     /**
698      *
699      */
700     FileDialogBase(const Glib::ustring &title) :
701                         Gtk::FileChooserDialog(title)
702         {
703         }
705     /**
706      *
707      */
708     FileDialogBase(const Glib::ustring &title,
709                    Gtk::FileChooserAction dialogType) :
710                    Gtk::FileChooserDialog(title, dialogType)
711         {
712         }
714     /**
715      *
716      */
717     virtual ~FileDialogBase()
718         {}
720 };
724 /*#########################################################################
725 ### F I L E    O P E N
726 #########################################################################*/
728 /**
729  * Our implementation class for the FileOpenDialog interface..
730  */
731 class FileOpenDialogImpl : public FileOpenDialog, public FileDialogBase
733 public:
735     FileOpenDialogImpl(const Glib::ustring &dir,
736                        FileDialogType fileTypes,
737                        const Glib::ustring &title);
739     virtual ~FileOpenDialogImpl();
741     bool show();
743     Inkscape::Extension::Extension *getSelectionType();
745     Glib::ustring getFilename();
747     std::vector<Glib::ustring> getFilenames ();
749 protected:
753 private:
756     /**
757      * What type of 'open' are we? (open, import, place, etc)
758      */
759     FileDialogType dialogType;
761     /**
762      * Our svg preview widget
763      */
764     SVGPreview svgPreview;
766     /**
767      * Callback for seeing if the preview needs to be drawn
768      */
769     void updatePreviewCallback();
771     /**
772      * Fix to allow the user to type the file name
773      */
774     Gtk::Entry fileNameEntry;
776     /**
777      *  Create a filter menu for this type of dialog
778      */
779     void createFilterMenu();
781     /**
782      * Callback for user input into fileNameEntry
783      */
784     void fileNameEntryChangedCallback();
786     /**
787      * Callback for user changing which item is selected on the list
788      */
789     void fileSelectedCallback();
792     /**
793      * Filter name->extension lookup
794      */
795     std::map<Glib::ustring, Inkscape::Extension::Extension *> extensionMap;
797     /**
798      * The extension to use to write this file
799      */
800     Inkscape::Extension::Extension *extension;
802     /**
803      * Filename that was given
804      */
805     Glib::ustring myFilename;
807 };
813 /**
814  * Callback for checking if the preview needs to be redrawn
815  */
816 void FileOpenDialogImpl::updatePreviewCallback()
818     Glib::ustring fileName = get_preview_filename();
819     if (fileName.length() < 1)
820         return;
821     svgPreview.set(fileName, dialogType);
828 /**
829  * Callback for fileNameEntry widget
830  */
831 void FileOpenDialogImpl::fileNameEntryChangedCallback()
833     Glib::ustring rawFileName = fileNameEntry.get_text();
835     Glib::ustring fileName = Glib::filename_from_utf8(rawFileName);
837     //g_message("User hit return.  Text is '%s'\n", fileName.c_str());
839     if (!Glib::path_is_absolute(fileName)) {
840         //try appending to the current path
841         // not this way: fileName = get_current_folder() + "/" + fName;
842         std::vector<Glib::ustring> pathSegments;
843         pathSegments.push_back( get_current_folder() );
844         pathSegments.push_back( fileName );
845         fileName = Glib::build_filename(pathSegments);
846     }
848     //g_message("path:'%s'\n", fName.c_str());
850     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
851         set_current_folder(fileName);
852     } else if (Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)) {
853         //dialog with either (1) select a regular file or (2) cd to dir
854         //simulate an 'OK'
855         set_filename(fileName);
856         response(Gtk::RESPONSE_OK);
857     }
864 /**
865  * Callback for fileNameEntry widget
866  */
867 void FileOpenDialogImpl::fileSelectedCallback()
869     Glib::ustring fileName     = get_filename();
870     if (!Glib::get_charset()) //If we are not utf8
871         fileName = Glib::filename_to_utf8(fileName);
872     //g_message("User selected '%s'\n",
873     //       filename().c_str());
875 #ifdef INK_DUMP_FILENAME_CONV
876     ::dump_ustr( get_filename() );
877 #endif
878     fileNameEntry.set_text(fileName);
884 void FileOpenDialogImpl::createFilterMenu()
886     //patterns added dynamically below
887     Gtk::FileFilter allImageFilter;
888     allImageFilter.set_name(_("All Images"));
889     extensionMap[Glib::ustring(_("All Images"))]=NULL;
890     add_filter(allImageFilter);
892     Gtk::FileFilter allFilter;
893     allFilter.set_name(_("All Files"));
894     extensionMap[Glib::ustring(_("All Files"))]=NULL;
895     allFilter.add_pattern("*");
896     add_filter(allFilter);
898     //patterns added dynamically below
899     Gtk::FileFilter allInkscapeFilter;
900     allInkscapeFilter.set_name(_("All Inkscape Files"));
901     extensionMap[Glib::ustring(_("All Inkscape Files"))]=NULL;
902     add_filter(allInkscapeFilter);
904     Inkscape::Extension::DB::InputList extension_list;
905     Inkscape::Extension::db.get_input_list(extension_list);
907     for (Inkscape::Extension::DB::InputList::iterator current_item = extension_list.begin();
908          current_item != extension_list.end(); current_item++)
909     {
910         Inkscape::Extension::Input * imod = *current_item;
912         // FIXME: would be nice to grey them out instead of not listing them
913         if (imod->deactivated()) continue;
915         Glib::ustring upattern("*");
916         Glib::ustring extension = imod->get_extension();
917         fileDialogExtensionToPattern(upattern, extension);
919         Gtk::FileFilter filter;
920         Glib::ustring uname(_(imod->get_filetypename()));
921         filter.set_name(uname);
922         filter.add_pattern(upattern);
923         add_filter(filter);
924         extensionMap[uname] = imod;
926         //g_message("ext %s:%s '%s'\n", ioext->name, ioext->mimetype, upattern.c_str());
927         allInkscapeFilter.add_pattern(upattern);
928         if ( strncmp("image", imod->get_mimetype(), 5)==0 )
929             allImageFilter.add_pattern(upattern);
930     }
932     return;
937 /**
938  * Constructor.  Not called directly.  Use the factory.
939  */
940 FileOpenDialogImpl::FileOpenDialogImpl(const Glib::ustring &dir,
941                                        FileDialogType fileTypes,
942                                        const Glib::ustring &title) :
943                                        FileDialogBase(title)
947     /* One file at a time */
948     /* And also Multiple Files */
949     set_select_multiple(true);
951     /* Initalize to Autodetect */
952     extension = NULL;
953     /* No filename to start out with */
954     myFilename = "";
956     /* Set our dialog type (open, import, etc...)*/
957     dialogType = fileTypes;
960     /* Set the pwd and/or the filename */
961     if (dir.size() > 0)
962         {
963         Glib::ustring udir(dir);
964         Glib::ustring::size_type len = udir.length();
965         // leaving a trailing backslash on the directory name leads to the infamous
966         // double-directory bug on win32
967         if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
968         set_current_folder(udir.c_str());
969         }
971     //###### Add the file types menu
972     createFilterMenu();
974     //###### Add a preview widget
975     set_preview_widget(svgPreview);
976     set_preview_widget_active(true);
977     set_use_preview_label (false);
979     //Catch selection-changed events, so we can adjust the text widget
980     signal_update_preview().connect(
981          sigc::mem_fun(*this, &FileOpenDialogImpl::updatePreviewCallback) );
984     //###### Add a text entry bar, and tie it to file chooser events
985     fileNameEntry.set_text(get_current_folder());
986     set_extra_widget(fileNameEntry);
987     fileNameEntry.grab_focus();
989     //Catch when user hits [return] on the text field
990     fileNameEntry.signal_activate().connect(
991          sigc::mem_fun(*this, &FileOpenDialogImpl::fileNameEntryChangedCallback) );
993     //Catch selection-changed events, so we can adjust the text widget
994     signal_selection_changed().connect(
995          sigc::mem_fun(*this, &FileOpenDialogImpl::fileSelectedCallback) );
997     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
998     add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_OK);
1006 /**
1007  * Public factory.  Called by file.cpp, among others.
1008  */
1009 FileOpenDialog *FileOpenDialog::create(const Glib::ustring &path,
1010                                        FileDialogType fileTypes,
1011                                        const Glib::ustring &title)
1013     FileOpenDialog *dialog = new FileOpenDialogImpl(path, fileTypes, title);
1014     return dialog;
1020 /**
1021  * Destructor
1022  */
1023 FileOpenDialogImpl::~FileOpenDialogImpl()
1029 /**
1030  * Show this dialog modally.  Return true if user hits [OK]
1031  */
1032 bool
1033 FileOpenDialogImpl::show()
1035     Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());
1036     if (s.length() == 0) 
1037         s = getcwd (NULL, 0);
1038     set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing
1039     set_modal (TRUE);                      //Window
1040     sp_transientize((GtkWidget *)gobj());  //Make transient
1041     gint b = run();                        //Dialog
1042     svgPreview.showNoPreview();
1043     hide();
1045     if (b == Gtk::RESPONSE_OK)
1046         {
1047         //This is a hack, to avoid the warning messages that
1048         //Gtk::FileChooser::get_filter() returns
1049         //should be:  Gtk::FileFilter *filter = get_filter();
1050         GtkFileChooser *gtkFileChooser = Gtk::FileChooser::gobj();
1051         GtkFileFilter *filter = gtk_file_chooser_get_filter(gtkFileChooser);
1052         if (filter)
1053             {
1054             //Get which extension was chosen, if any
1055             extension = extensionMap[gtk_file_filter_get_name(filter)];
1056             }
1057         myFilename = get_filename();
1058         return TRUE;
1059         }
1060     else
1061        {
1062        return FALSE;
1063        }
1069 /**
1070  * Get the file extension type that was selected by the user. Valid after an [OK]
1071  */
1072 Inkscape::Extension::Extension *
1073 FileOpenDialogImpl::getSelectionType()
1075     return extension;
1079 /**
1080  * Get the file name chosen by the user.   Valid after an [OK]
1081  */
1082 Glib::ustring
1083 FileOpenDialogImpl::getFilename (void)
1085     return g_strdup(myFilename.c_str());
1089 /**
1090  * To Get Multiple filenames selected at-once.
1091  */
1092 std::vector<Glib::ustring>FileOpenDialogImpl::getFilenames()
1093 {    
1094     std::vector<Glib::ustring> result = get_filenames();
1095     return result;
1103 //########################################################################
1104 //# F I L E    S A V E
1105 //########################################################################
1107 class FileType
1109     public:
1110     FileType() {}
1111     ~FileType() {}
1112     Glib::ustring name;
1113     Glib::ustring pattern;
1114     Inkscape::Extension::Extension *extension;
1115 };
1117 /**
1118  * Our implementation of the FileSaveDialog interface.
1119  */
1120 class FileSaveDialogImpl : public FileSaveDialog, public FileDialogBase
1123 public:
1124     FileSaveDialogImpl(const Glib::ustring &dir,
1125                        FileDialogType fileTypes,
1126                        const Glib::ustring &title,
1127                        const Glib::ustring &default_key);
1129     virtual ~FileSaveDialogImpl();
1131     bool show();
1133     Inkscape::Extension::Extension *getSelectionType();
1135     Glib::ustring getFilename();
1138 private:
1140     /**
1141      * What type of 'open' are we? (save, export, etc)
1142      */
1143     FileDialogType dialogType;
1145     /**
1146      * Our svg preview widget
1147      */
1148     SVGPreview svgPreview;
1150     /**
1151      * Fix to allow the user to type the file name
1152      */
1153     Gtk::Entry *fileNameEntry;
1155     /**
1156      * Callback for seeing if the preview needs to be drawn
1157      */
1158     void updatePreviewCallback();
1162     /**
1163      * Allow the specification of the output file type
1164      */
1165     Gtk::HBox fileTypeBox;
1167     /**
1168      * Allow the specification of the output file type
1169      */
1170     Gtk::ComboBoxText fileTypeComboBox;
1173     /**
1174      *  Data mirror of the combo box
1175      */
1176     std::vector<FileType> fileTypes;
1178     //# Child widgets
1179     Gtk::CheckButton fileTypeCheckbox;
1182     /**
1183      * Callback for user input into fileNameEntry
1184      */
1185     void fileTypeChangedCallback();
1187     /**
1188      *  Create a filter menu for this type of dialog
1189      */
1190     void createFileTypeMenu();
1193     bool append_extension;
1195     /**
1196      * The extension to use to write this file
1197      */
1198     Inkscape::Extension::Extension *extension;
1200     /**
1201      * Callback for user input into fileNameEntry
1202      */
1203     void fileNameEntryChangedCallback();
1205     /**
1206      * Filename that was given
1207      */
1208     Glib::ustring myFilename;
1209 };
1216 /**
1217  * Callback for checking if the preview needs to be redrawn
1218  */
1219 void FileSaveDialogImpl::updatePreviewCallback()
1221     Glib::ustring fileName = get_preview_filename();
1222     if (!fileName.c_str())
1223         return;
1224     bool retval = svgPreview.set(fileName, dialogType);
1225     set_preview_widget_active(retval);
1230 /**
1231  * Callback for fileNameEntry widget
1232  */
1233 void FileSaveDialogImpl::fileNameEntryChangedCallback()
1235     if (!fileNameEntry)
1236         return;
1238     Glib::ustring fileName = fileNameEntry->get_text();
1239     if (!Glib::get_charset()) //If we are not utf8
1240         fileName = Glib::filename_to_utf8(fileName);
1242     //g_message("User hit return.  Text is '%s'\n", fileName.c_str());
1244     if (!Glib::path_is_absolute(fileName)) {
1245         //try appending to the current path
1246         // not this way: fileName = get_current_folder() + "/" + fileName;
1247         std::vector<Glib::ustring> pathSegments;
1248         pathSegments.push_back( get_current_folder() );
1249         pathSegments.push_back( fileName );
1250         fileName = Glib::build_filename(pathSegments);
1251     }
1253     //g_message("path:'%s'\n", fileName.c_str());
1255     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
1256         set_current_folder(fileName);
1257     } else if (/*Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)*/1) {
1258         //dialog with either (1) select a regular file or (2) cd to dir
1259         //simulate an 'OK'
1260         set_filename(fileName);
1261         response(Gtk::RESPONSE_OK);
1262     }
1267 /**
1268  * Callback for fileNameEntry widget
1269  */
1270 void FileSaveDialogImpl::fileTypeChangedCallback()
1272     int sel = fileTypeComboBox.get_active_row_number();
1273     if (sel<0 || sel >= (int)fileTypes.size())
1274         return;
1275     FileType type = fileTypes[sel];
1276     //g_message("selected: %s\n", type.name.c_str());
1277     Gtk::FileFilter filter;
1278     filter.add_pattern(type.pattern);
1279     set_filter(filter);
1284 void FileSaveDialogImpl::createFileTypeMenu()
1286     Inkscape::Extension::DB::OutputList extension_list;
1287     Inkscape::Extension::db.get_output_list(extension_list);
1289     for (Inkscape::Extension::DB::OutputList::iterator current_item = extension_list.begin();
1290          current_item != extension_list.end(); current_item++)
1291     {
1292         Inkscape::Extension::Output * omod = *current_item;
1294         // FIXME: would be nice to grey them out instead of not listing them
1295         if (omod->deactivated()) continue;
1297         FileType type;
1298         type.name     = (_(omod->get_filetypename()));
1299         type.pattern  = "*";
1300         Glib::ustring extension = omod->get_extension();
1301         fileDialogExtensionToPattern (type.pattern, extension);
1302         type.extension= omod;
1303         fileTypeComboBox.append_text(type.name);
1304         fileTypes.push_back(type);
1305     }
1307     //#Let user choose
1308     FileType guessType;
1309     guessType.name = _("Guess from extension");
1310     guessType.pattern = "*";
1311     guessType.extension = NULL;
1312     fileTypeComboBox.append_text(guessType.name);
1313     fileTypes.push_back(guessType);
1316     fileTypeComboBox.set_active(0);
1317     fileTypeChangedCallback(); //call at least once to set the filter
1322 /**
1323  * Constructor
1324  */
1325 FileSaveDialogImpl::FileSaveDialogImpl(const Glib::ustring &dir,
1326             FileDialogType fileTypes,
1327             const Glib::ustring &title,
1328             const Glib::ustring &default_key) :
1329             FileDialogBase(title, Gtk::FILE_CHOOSER_ACTION_SAVE)
1331     append_extension = (bool)prefs_get_int_attribute("dialogs.save_as",
1332                                                   "append_extension", 1);
1334     /* One file at a time */
1335     set_select_multiple(false);
1337     /* Initalize to Autodetect */
1338     extension = NULL;
1339     /* No filename to start out with */
1340     myFilename = "";
1342     /* Set our dialog type (save, export, etc...)*/
1343     dialogType = fileTypes;
1345     /* Set the pwd and/or the filename */
1346     if (dir.size() > 0)
1347         {
1348         Glib::ustring udir(dir);
1349         Glib::ustring::size_type len = udir.length();
1350         // leaving a trailing backslash on the directory name leads to the infamous
1351         // double-directory bug on win32
1352         if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
1353         set_current_folder(udir.c_str());
1354         }
1356     //###### Add the file types menu
1357     //createFilterMenu();
1359     //###### Do we want the .xxx extension automatically added?
1360     fileTypeCheckbox.set_label(Glib::ustring(_("Append filename extension automatically")));
1361     fileTypeCheckbox.set_active(append_extension);
1363     fileTypeBox.pack_start(fileTypeCheckbox);
1364     createFileTypeMenu();
1365     fileTypeComboBox.set_size_request(200,40);
1366     fileTypeComboBox.signal_changed().connect(
1367          sigc::mem_fun(*this, &FileSaveDialogImpl::fileTypeChangedCallback) );
1369     fileTypeBox.pack_start(fileTypeComboBox);
1371     set_extra_widget(fileTypeBox);
1372     //get_vbox()->pack_start(fileTypeBox, false, false, 0);
1373     //get_vbox()->reorder_child(fileTypeBox, 2);
1375     //###### Add a preview widget
1376     set_preview_widget(svgPreview);
1377     set_preview_widget_active(true);
1378     set_use_preview_label (false);
1380     //Catch selection-changed events, so we can adjust the text widget
1381     signal_update_preview().connect(
1382          sigc::mem_fun(*this, &FileSaveDialogImpl::updatePreviewCallback) );
1385     //Let's do some customization
1386     fileNameEntry = NULL;
1387     Gtk::Container *cont = get_toplevel();
1388     std::vector<Gtk::Entry *> entries;
1389     findEntryWidgets(cont, entries);
1390     //g_message("Found %d entry widgets\n", entries.size());
1391     if (entries.size() >=1 )
1392         {
1393         //Catch when user hits [return] on the text field
1394         fileNameEntry = entries[0];
1395         fileNameEntry->signal_activate().connect(
1396              sigc::mem_fun(*this, &FileSaveDialogImpl::fileNameEntryChangedCallback) );
1397         }
1399     //Let's do more customization
1400     std::vector<Gtk::Expander *> expanders;
1401     findExpanderWidgets(cont, expanders);
1402     //g_message("Found %d expander widgets\n", expanders.size());
1403     if (expanders.size() >=1 )
1404         {
1405         //Always show the file list
1406         Gtk::Expander *expander = expanders[0];
1407         expander->set_expanded(true);
1408         }
1411     //if (extension == NULL)
1412     //    checkbox.set_sensitive(FALSE);
1414     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1415     add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_OK);
1417     show_all_children();
1422 /**
1423  * Public factory method.  Used in file.cpp
1424  */
1425 FileSaveDialog *FileSaveDialog::create(const Glib::ustring &path,
1426                                        FileDialogType fileTypes,
1427                                        const Glib::ustring &title,
1428                                        const Glib::ustring &default_key)
1430     FileSaveDialog *dialog = new FileSaveDialogImpl(path, fileTypes, title, default_key);
1431     return dialog;
1438 /**
1439  * Destructor
1440  */
1441 FileSaveDialogImpl::~FileSaveDialogImpl()
1447 /**
1448  * Show this dialog modally.  Return true if user hits [OK]
1449  */
1450 bool
1451 FileSaveDialogImpl::show()
1453     Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());
1454     if (s.length() == 0) 
1455         s = getcwd (NULL, 0);
1456     set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing
1457     set_modal (TRUE);                      //Window
1458     sp_transientize((GtkWidget *)gobj());  //Make transient
1459     gint b = run();                        //Dialog
1460     svgPreview.showNoPreview();
1461     hide();
1463     if (b == Gtk::RESPONSE_OK)
1464         {
1465         int sel = fileTypeComboBox.get_active_row_number ();
1466         if (sel>=0 && sel< (int)fileTypes.size())
1467             {
1468             FileType &type = fileTypes[sel];
1469             extension = type.extension;
1470             }
1471         myFilename = get_filename();
1473         /*
1475         // FIXME: Why do we have more code
1477         append_extension = checkbox.get_active();
1478         prefs_set_int_attribute("dialogs.save_as", "append_extension", append_extension);
1479         prefs_set_string_attribute("dialogs.save_as", "default",
1480                   ( extension != NULL ? extension->get_id() : "" ));
1481         */
1482         return TRUE;
1483         }
1484     else
1485         {
1486         return FALSE;
1487         }
1491 /**
1492  * Get the file extension type that was selected by the user. Valid after an [OK]
1493  */
1494 Inkscape::Extension::Extension *
1495 FileSaveDialogImpl::getSelectionType()
1497     return extension;
1501 /**
1502  * Get the file name chosen by the user.   Valid after an [OK]
1503  */
1504 Glib::ustring
1505 FileSaveDialogImpl::getFilename()
1507     return myFilename;
1517 //########################################################################
1518 //# F I L E     E X P O R T
1519 //########################################################################
1522 /**
1523  * Our implementation of the FileExportDialog interface.
1524  */
1525 class FileExportDialogImpl : public FileExportDialog, public FileDialogBase
1528 public:
1529     FileExportDialogImpl(const Glib::ustring &dir,
1530                        FileDialogType fileTypes,
1531                        const Glib::ustring &title,
1532                        const Glib::ustring &default_key);
1534     virtual ~FileExportDialogImpl();
1536     bool show();
1538     Inkscape::Extension::Extension *getSelectionType();
1540     Glib::ustring getFilename();
1543 private:
1545     /**
1546      * What type of 'open' are we? (save, export, etc)
1547      */
1548     FileDialogType dialogType;
1550     /**
1551      * Our svg preview widget
1552      */
1553     SVGPreview svgPreview;
1555     /**
1556      * Fix to allow the user to type the file name
1557      */
1558     Gtk::Entry *fileNameEntry;
1560     /**
1561      * Callback for seeing if the preview needs to be drawn
1562      */
1563     void updatePreviewCallback();
1565     //##########################################
1566     //# EXTRA WIDGET -- SOURCE SIDE
1567     //##########################################
1569     Gtk::Frame            sourceFrame;
1570     Gtk::VBox             sourceBox;
1572     Gtk::HBox             scopeBox;
1573     Gtk::RadioButtonGroup scopeGroup;
1574     Gtk::RadioButton      documentButton;
1575     Gtk::RadioButton      pageButton;
1576     Gtk::RadioButton      selectionButton;
1578     Gtk::Table                      sourceTable;
1579     Inkscape::UI::Widget::Scalar    sourceX0;
1580     Inkscape::UI::Widget::Scalar    sourceY0;
1581     Inkscape::UI::Widget::Scalar    sourceX1;
1582     Inkscape::UI::Widget::Scalar    sourceY1;
1583     Inkscape::UI::Widget::Scalar    sourceWidth;
1584     Inkscape::UI::Widget::Scalar    sourceHeight;
1585     Inkscape::UI::Widget::UnitMenu  sourceUnits;
1588     //##########################################
1589     //# EXTRA WIDGET -- DESTINATION SIDE
1590     //##########################################
1592     Gtk::Frame       destFrame;
1593     Gtk::VBox        destBox;
1595     Gtk::Table                      destTable;
1596     Inkscape::UI::Widget::Scalar    destWidth;
1597     Inkscape::UI::Widget::Scalar    destHeight;
1598     Inkscape::UI::Widget::Scalar    destDPI;
1599     Inkscape::UI::Widget::UnitMenu  destUnits;
1601     Gtk::HBox        otherOptionBox;
1602     Gtk::CheckButton cairoButton;
1603     Gtk::CheckButton antiAliasButton;
1604     Gtk::ColorButton backgroundButton;
1607     /**
1608      * 'Extra' widget that holds two boxes above
1609      */
1610     Gtk::HBox exportOptionsBox;
1613     //# Child widgets
1614     Gtk::CheckButton fileTypeCheckbox;
1616     /**
1617      * Allow the specification of the output file type
1618      */
1619     Gtk::ComboBoxText fileTypeComboBox;
1622     /**
1623      *  Data mirror of the combo box
1624      */
1625     std::vector<FileType> fileTypes;
1629     /**
1630      * Callback for user input into fileNameEntry
1631      */
1632     void fileTypeChangedCallback();
1634     /**
1635      *  Create a filter menu for this type of dialog
1636      */
1637     void createFileTypeMenu();
1640     bool append_extension;
1642     /**
1643      * The extension to use to write this file
1644      */
1645     Inkscape::Extension::Extension *extension;
1647     /**
1648      * Callback for user input into fileNameEntry
1649      */
1650     void fileNameEntryChangedCallback();
1652     /**
1653      * Filename that was given
1654      */
1655     Glib::ustring myFilename;
1656 };
1663 /**
1664  * Callback for checking if the preview needs to be redrawn
1665  */
1666 void FileExportDialogImpl::updatePreviewCallback()
1668     Glib::ustring fileName = get_preview_filename();
1669     if (!fileName.c_str())
1670         return;
1671     bool retval = svgPreview.set(fileName, dialogType);
1672     set_preview_widget_active(retval);
1677 /**
1678  * Callback for fileNameEntry widget
1679  */
1680 void FileExportDialogImpl::fileNameEntryChangedCallback()
1682     if (!fileNameEntry)
1683         return;
1685     Glib::ustring fileName = fileNameEntry->get_text();
1686     if (!Glib::get_charset()) //If we are not utf8
1687         fileName = Glib::filename_to_utf8(fileName);
1689     //g_message("User hit return.  Text is '%s'\n", fileName.c_str());
1691     if (!Glib::path_is_absolute(fileName)) {
1692         //try appending to the current path
1693         // not this way: fileName = get_current_folder() + "/" + fileName;
1694         std::vector<Glib::ustring> pathSegments;
1695         pathSegments.push_back( get_current_folder() );
1696         pathSegments.push_back( fileName );
1697         fileName = Glib::build_filename(pathSegments);
1698     }
1700     //g_message("path:'%s'\n", fileName.c_str());
1702     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
1703         set_current_folder(fileName);
1704     } else if (/*Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)*/1) {
1705         //dialog with either (1) select a regular file or (2) cd to dir
1706         //simulate an 'OK'
1707         set_filename(fileName);
1708         response(Gtk::RESPONSE_OK);
1709     }
1714 /**
1715  * Callback for fileNameEntry widget
1716  */
1717 void FileExportDialogImpl::fileTypeChangedCallback()
1719     int sel = fileTypeComboBox.get_active_row_number();
1720     if (sel<0 || sel >= (int)fileTypes.size())
1721         return;
1722     FileType type = fileTypes[sel];
1723     //g_message("selected: %s\n", type.name.c_str());
1724     Gtk::FileFilter filter;
1725     filter.add_pattern(type.pattern);
1726     set_filter(filter);
1731 void FileExportDialogImpl::createFileTypeMenu()
1733     Inkscape::Extension::DB::OutputList extension_list;
1734     Inkscape::Extension::db.get_output_list(extension_list);
1736     for (Inkscape::Extension::DB::OutputList::iterator current_item = extension_list.begin();
1737          current_item != extension_list.end(); current_item++)
1738     {
1739         Inkscape::Extension::Output * omod = *current_item;
1741         // FIXME: would be nice to grey them out instead of not listing them
1742         if (omod->deactivated()) continue;
1744         FileType type;
1745         type.name     = (_(omod->get_filetypename()));
1746         type.pattern  = "*";
1747         Glib::ustring extension = omod->get_extension();
1748         fileDialogExtensionToPattern (type.pattern, extension);
1749         type.extension= omod;
1750         fileTypeComboBox.append_text(type.name);
1751         fileTypes.push_back(type);
1752     }
1754     //#Let user choose
1755     FileType guessType;
1756     guessType.name = _("Guess from extension");
1757     guessType.pattern = "*";
1758     guessType.extension = NULL;
1759     fileTypeComboBox.append_text(guessType.name);
1760     fileTypes.push_back(guessType);
1763     fileTypeComboBox.set_active(0);
1764     fileTypeChangedCallback(); //call at least once to set the filter
1768 /**
1769  * Constructor
1770  */
1771 FileExportDialogImpl::FileExportDialogImpl(const Glib::ustring &dir,
1772             FileDialogType fileTypes,
1773             const Glib::ustring &title,
1774             const Glib::ustring &default_key) :
1775             FileDialogBase(title, Gtk::FILE_CHOOSER_ACTION_SAVE),
1776             sourceX0("X0",         _("Source left bound")),
1777             sourceY0("Y0",         _("Source top bound")),
1778             sourceX1("X1",         _("Source right bound")),
1779             sourceY1("Y1",         _("Source bottom bound")),
1780             sourceWidth("Width",   _("Source width")),
1781             sourceHeight("Height", _("Source height")),
1782             destWidth("Width",   _("Destination width")),
1783             destHeight("Height", _("Destination height")),
1784             destDPI("DPI", _("Dots per inch resolution"))
1786     append_extension = (bool)prefs_get_int_attribute("dialogs.save_as", "append_extension", 1);
1788     /* One file at a time */
1789     set_select_multiple(false);
1791     /* Initalize to Autodetect */
1792     extension = NULL;
1793     /* No filename to start out with */
1794     myFilename = "";
1796     /* Set our dialog type (save, export, etc...)*/
1797     dialogType = fileTypes;
1799     /* Set the pwd and/or the filename */
1800     if (dir.size()>0)
1801         {
1802         Glib::ustring udir(dir);
1803         Glib::ustring::size_type len = udir.length();
1804         // leaving a trailing backslash on the directory name leads to the infamous
1805         // double-directory bug on win32
1806         if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
1807         set_current_folder(udir.c_str());
1808         }
1810     //#########################################
1811     //## EXTRA WIDGET -- SOURCE SIDE
1812     //#########################################
1814     //##### Export options buttons/spinners, etc
1815     documentButton.set_label(_("Document"));
1816     scopeBox.pack_start(documentButton);
1817     scopeGroup = documentButton.get_group();
1819     pageButton.set_label(_("Page"));
1820     pageButton.set_group(scopeGroup);
1821     scopeBox.pack_start(pageButton);
1823     selectionButton.set_label(_("Selection"));
1824     selectionButton.set_group(scopeGroup);
1825     scopeBox.pack_start(selectionButton);
1827     sourceBox.pack_start(scopeBox);
1831     //dimension buttons
1832     sourceTable.resize(3,3);
1833     sourceTable.attach(sourceX0,     0,1,0,1);
1834     sourceTable.attach(sourceY0,     1,2,0,1);
1835     sourceUnits.setUnitType(UNIT_TYPE_LINEAR);
1836     sourceTable.attach(sourceUnits,  2,3,0,1);
1837     sourceTable.attach(sourceX1,     0,1,1,2);
1838     sourceTable.attach(sourceY1,     1,2,1,2);
1839     sourceTable.attach(sourceWidth,  0,1,2,3);
1840     sourceTable.attach(sourceHeight, 1,2,2,3);
1842     sourceBox.pack_start(sourceTable);
1843     sourceFrame.set_label(_("Source"));
1844     sourceFrame.add(sourceBox);
1845     exportOptionsBox.pack_start(sourceFrame);
1848     //#########################################
1849     //## EXTRA WIDGET -- SOURCE SIDE
1850     //#########################################
1853     destTable.resize(3,3);
1854     destTable.attach(destWidth,    0,1,0,1);
1855     destTable.attach(destHeight,   1,2,0,1);
1856     destUnits.setUnitType(UNIT_TYPE_LINEAR);
1857     destTable.attach(destUnits,    2,3,0,1);
1858     destTable.attach(destDPI,      0,1,1,2);
1860     destBox.pack_start(destTable);
1863     cairoButton.set_label(_("Cairo"));
1864     otherOptionBox.pack_start(cairoButton);
1866     antiAliasButton.set_label(_("Antialias"));
1867     otherOptionBox.pack_start(antiAliasButton);
1869     backgroundButton.set_label(_("Background"));
1870     otherOptionBox.pack_start(backgroundButton);
1872     destBox.pack_start(otherOptionBox);
1878     //###### File options
1879     //###### Do we want the .xxx extension automatically added?
1880     fileTypeCheckbox.set_label(Glib::ustring(_("Append filename extension automatically")));
1881     fileTypeCheckbox.set_active(append_extension);
1882     destBox.pack_start(fileTypeCheckbox);
1884     //###### File type menu
1885     createFileTypeMenu();
1886     fileTypeComboBox.set_size_request(200,40);
1887     fileTypeComboBox.signal_changed().connect(
1888          sigc::mem_fun(*this, &FileExportDialogImpl::fileTypeChangedCallback) );
1890     destBox.pack_start(fileTypeComboBox);
1892     destFrame.set_label(_("Destination"));
1893     destFrame.add(destBox);
1894     exportOptionsBox.pack_start(destFrame);
1896     //##### Put the two boxes and their parent onto the dialog    
1897     exportOptionsBox.pack_start(sourceFrame);
1898     exportOptionsBox.pack_start(destFrame);
1900     set_extra_widget(exportOptionsBox);
1905     //###### PREVIEW WIDGET
1906     set_preview_widget(svgPreview);
1907     set_preview_widget_active(true);
1908     set_use_preview_label (false);
1910     //Catch selection-changed events, so we can adjust the text widget
1911     signal_update_preview().connect(
1912          sigc::mem_fun(*this, &FileExportDialogImpl::updatePreviewCallback) );
1915     //Let's do some customization
1916     fileNameEntry = NULL;
1917     Gtk::Container *cont = get_toplevel();
1918     std::vector<Gtk::Entry *> entries;
1919     findEntryWidgets(cont, entries);
1920     //g_message("Found %d entry widgets\n", entries.size());
1921     if (entries.size() >=1 )
1922         {
1923         //Catch when user hits [return] on the text field
1924         fileNameEntry = entries[0];
1925         fileNameEntry->signal_activate().connect(
1926              sigc::mem_fun(*this, &FileExportDialogImpl::fileNameEntryChangedCallback) );
1927         }
1929     //Let's do more customization
1930     std::vector<Gtk::Expander *> expanders;
1931     findExpanderWidgets(cont, expanders);
1932     //g_message("Found %d expander widgets\n", expanders.size());
1933     if (expanders.size() >=1 )
1934         {
1935         //Always show the file list
1936         Gtk::Expander *expander = expanders[0];
1937         expander->set_expanded(true);
1938         }
1941     //if (extension == NULL)
1942     //    checkbox.set_sensitive(FALSE);
1944     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1945     add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_OK);
1947     show_all_children();
1952 /**
1953  * Public factory method.  Used in file.cpp
1954  */
1955 FileExportDialog *FileExportDialog::create(const Glib::ustring &path,
1956                                        FileDialogType fileTypes,
1957                                        const Glib::ustring &title,
1958                                        const Glib::ustring &default_key)
1960     FileExportDialog *dialog = new FileExportDialogImpl(path, fileTypes, title, default_key);
1961     return dialog;
1968 /**
1969  * Destructor
1970  */
1971 FileExportDialogImpl::~FileExportDialogImpl()
1977 /**
1978  * Show this dialog modally.  Return true if user hits [OK]
1979  */
1980 bool
1981 FileExportDialogImpl::show()
1983     Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());
1984     if (s.length() == 0) 
1985         s = getcwd (NULL, 0);
1986     set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing
1987     set_modal (TRUE);                      //Window
1988     sp_transientize((GtkWidget *)gobj());  //Make transient
1989     gint b = run();                        //Dialog
1990     svgPreview.showNoPreview();
1991     hide();
1993     if (b == Gtk::RESPONSE_OK)
1994         {
1995         int sel = fileTypeComboBox.get_active_row_number ();
1996         if (sel>=0 && sel< (int)fileTypes.size())
1997             {
1998             FileType &type = fileTypes[sel];
1999             extension = type.extension;
2000             }
2001         myFilename = get_filename();
2003         /*
2005         // FIXME: Why do we have more code
2007         append_extension = checkbox.get_active();
2008         prefs_set_int_attribute("dialogs.save_as", "append_extension", append_extension);
2009         prefs_set_string_attribute("dialogs.save_as", "default",
2010                   ( extension != NULL ? extension->get_id() : "" ));
2011         */
2012         return TRUE;
2013         }
2014     else
2015         {
2016         return FALSE;
2017         }
2021 /**
2022  * Get the file extension type that was selected by the user. Valid after an [OK]
2023  */
2024 Inkscape::Extension::Extension *
2025 FileExportDialogImpl::getSelectionType()
2027     return extension;
2031 /**
2032  * Get the file name chosen by the user.   Valid after an [OK]
2033  */
2034 Glib::ustring
2035 FileExportDialogImpl::getFilename()
2037     return myFilename;
2043 } //namespace Dialog
2044 } //namespace UI
2045 } //namespace Inkscape
2048 /*
2049   Local Variables:
2050   mode:c++
2051   c-file-style:"stroustrup"
2052   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2053   indent-tabs-mode:nil
2054   fill-column:99
2055   End:
2056 */
2057 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :