Code

copyedit
[inkscape.git] / src / ui / dialog / filedialogimpl-gtkmm.cpp
1 /**\r
2  * Implementation of the file dialog interfaces defined in filedialogimpl.h\r
3  *\r
4  * Authors:\r
5  *   Bob Jamison\r
6  *   Joel Holdsworth\r
7  *   Bruno Dilly\r
8  *   Other dudes from The Inkscape Organization\r
9  *\r
10  * Copyright (C) 2004-2007 Bob Jamison\r
11  * Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl>\r
12  * Copyright (C) 2004-2007 The Inkscape Organization\r
13  * Copyright (C) 2007 Joel Holdsworth\r
14  *\r
15  * Released under GNU GPL, read the file 'COPYING' for more information\r
16  */\r
17 \r
18 #ifdef HAVE_CONFIG_H\r
19 # include <config.h>\r
20 #endif\r
21 \r
22 #include "filedialogimpl-gtkmm.h"\r
23 #include "dialogs/dialog-events.h"\r
24 #include "interface.h"\r
25 #include "io/sys.h"\r
26 #include "path-prefix.h"\r
27 \r
28 #ifdef WITH_GNOME_VFS\r
29 # include <libgnomevfs/gnome-vfs.h>\r
30 #endif\r
31 \r
32 //Routines from file.cpp\r
33 #undef INK_DUMP_FILENAME_CONV\r
34 \r
35 #ifdef INK_DUMP_FILENAME_CONV\r
36 void dump_str( const gchar* str, const gchar* prefix );\r
37 void dump_ustr( const Glib::ustring& ustr );\r
38 #endif\r
39 \r
40 \r
41 \r
42 namespace Inkscape\r
43 {\r
44 namespace UI\r
45 {\r
46 namespace Dialog\r
47 {\r
48 \r
49 \r
50 \r
51 \r
52 \r
53 //########################################################################\r
54 //### U T I L I T Y\r
55 //########################################################################\r
56 \r
57 void\r
58 fileDialogExtensionToPattern(Glib::ustring &pattern,\r
59                       Glib::ustring &extension)\r
60 {\r
61     for (unsigned int i = 0; i < extension.length(); i++ )\r
62         {\r
63         Glib::ustring::value_type ch = extension[i];\r
64         if ( Glib::Unicode::isalpha(ch) )\r
65             {\r
66             pattern += '[';\r
67             pattern += Glib::Unicode::toupper(ch);\r
68             pattern += Glib::Unicode::tolower(ch);\r
69             pattern += ']';\r
70             }\r
71         else\r
72             {\r
73             pattern += ch;\r
74             }\r
75         }\r
76 }\r
77 \r
78 \r
79 void\r
80 findEntryWidgets(Gtk::Container *parent,\r
81                  std::vector<Gtk::Entry *> &result)\r
82 {\r
83     if (!parent)\r
84         return;\r
85     std::vector<Gtk::Widget *> children = parent->get_children();\r
86     for (unsigned int i=0; i<children.size() ; i++)\r
87         {\r
88         Gtk::Widget *child = children[i];\r
89         GtkWidget *wid = child->gobj();\r
90         if (GTK_IS_ENTRY(wid))\r
91            result.push_back((Gtk::Entry *)child);\r
92         else if (GTK_IS_CONTAINER(wid))\r
93             findEntryWidgets((Gtk::Container *)child, result);\r
94         }\r
95 \r
96 }\r
97 \r
98 void\r
99 findExpanderWidgets(Gtk::Container *parent,\r
100                     std::vector<Gtk::Expander *> &result)\r
101 {\r
102     if (!parent)\r
103         return;\r
104     std::vector<Gtk::Widget *> children = parent->get_children();\r
105     for (unsigned int i=0; i<children.size() ; i++)\r
106         {\r
107         Gtk::Widget *child = children[i];\r
108         GtkWidget *wid = child->gobj();\r
109         if (GTK_IS_EXPANDER(wid))\r
110            result.push_back((Gtk::Expander *)child);\r
111         else if (GTK_IS_CONTAINER(wid))\r
112             findExpanderWidgets((Gtk::Container *)child, result);\r
113         }\r
114 \r
115 }\r
116 \r
117 \r
118 /*#########################################################################\r
119 ### SVG Preview Widget\r
120 #########################################################################*/\r
121 \r
122 bool SVGPreview::setDocument(SPDocument *doc)\r
123 {\r
124     if (document)\r
125         sp_document_unref(document);\r
126 \r
127     sp_document_ref(doc);\r
128     document = doc;\r
129 \r
130     //This should remove it from the box, and free resources\r
131     if (viewerGtk)\r
132         gtk_widget_destroy(viewerGtk);\r
133 \r
134     viewerGtk  = sp_svg_view_widget_new(doc);\r
135     GtkWidget *vbox = (GtkWidget *)gobj();\r
136     gtk_box_pack_start(GTK_BOX(vbox), viewerGtk, TRUE, TRUE, 0);\r
137     gtk_widget_show(viewerGtk);\r
138 \r
139     return true;\r
140 }\r
141 \r
142 \r
143 bool SVGPreview::setFileName(Glib::ustring &theFileName)\r
144 {\r
145     Glib::ustring fileName = theFileName;\r
146 \r
147     fileName = Glib::filename_to_utf8(fileName);\r
148 \r
149     /**\r
150      * I don't know why passing false to keepalive is bad.  But it\r
151      * prevents the display of an svg with a non-ascii filename\r
152      */\r
153     SPDocument *doc = sp_document_new (fileName.c_str(), true);\r
154     if (!doc) {\r
155         g_warning("SVGView: error loading document '%s'\n", fileName.c_str());\r
156         return false;\r
157     }\r
158 \r
159     setDocument(doc);\r
160 \r
161     sp_document_unref(doc);\r
162 \r
163     return true;\r
164 }\r
165 \r
166 \r
167 \r
168 bool SVGPreview::setFromMem(char const *xmlBuffer)\r
169 {\r
170     if (!xmlBuffer)\r
171         return false;\r
172 \r
173     gint len = (gint)strlen(xmlBuffer);\r
174     SPDocument *doc = sp_document_new_from_mem(xmlBuffer, len, 0);\r
175     if (!doc) {\r
176         g_warning("SVGView: error loading buffer '%s'\n",xmlBuffer);\r
177         return false;\r
178     }\r
179 \r
180     setDocument(doc);\r
181 \r
182     sp_document_unref(doc);\r
183 \r
184     Inkscape::GC::request_early_collection();\r
185 \r
186     return true;\r
187 }\r
188 \r
189 \r
190 \r
191 void SVGPreview::showImage(Glib::ustring &theFileName)\r
192 {\r
193     Glib::ustring fileName = theFileName;\r
194 \r
195 \r
196     /*#####################################\r
197     # LET'S HAVE SOME FUN WITH SVG!\r
198     # Instead of just loading an image, why\r
199     # don't we make a lovely little svg and\r
200     # display it nicely?\r
201     #####################################*/\r
202 \r
203     //Arbitrary size of svg doc -- rather 'portrait' shaped\r
204     gint previewWidth  = 400;\r
205     gint previewHeight = 600;\r
206 \r
207     //Get some image info. Smart pointer does not need to be deleted\r
208     Glib::RefPtr<Gdk::Pixbuf> img = Gdk::Pixbuf::create_from_file(fileName);\r
209     gint imgWidth  = img->get_width();\r
210     gint imgHeight = img->get_height();\r
211 \r
212     //Find the minimum scale to fit the image inside the preview area\r
213     double scaleFactorX = (0.9 *(double)previewWidth)  / ((double)imgWidth);\r
214     double scaleFactorY = (0.9 *(double)previewHeight) / ((double)imgHeight);\r
215     double scaleFactor = scaleFactorX;\r
216     if (scaleFactorX > scaleFactorY)\r
217         scaleFactor = scaleFactorY;\r
218 \r
219     //Now get the resized values\r
220     gint scaledImgWidth  = (int) (scaleFactor * (double)imgWidth);\r
221     gint scaledImgHeight = (int) (scaleFactor * (double)imgHeight);\r
222 \r
223     //center the image on the area\r
224     gint imgX = (previewWidth  - scaledImgWidth)  / 2;\r
225     gint imgY = (previewHeight - scaledImgHeight) / 2;\r
226 \r
227     //wrap a rectangle around the image\r
228     gint rectX      = imgX-1;\r
229     gint rectY      = imgY-1;\r
230     gint rectWidth  = scaledImgWidth +2;\r
231     gint rectHeight = scaledImgHeight+2;\r
232 \r
233     //Our template.  Modify to taste\r
234     gchar const *xformat =\r
235           "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"\r
236           "<svg\n"\r
237           "xmlns=\"http://www.w3.org/2000/svg\"\n"\r
238           "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"\r
239           "width=\"%d\" height=\"%d\">\n"  //# VALUES HERE\r
240           "<rect\n"\r
241           "  style=\"fill:#eeeeee;stroke:none\"\n"\r
242           "  x=\"-100\" y=\"-100\" width=\"4000\" height=\"4000\"/>\n"\r
243           "<image x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"\n"\r
244           "xlink:href=\"%s\"/>\n"\r
245           "<rect\n"\r
246           "  style=\"fill:none;"\r
247           "    stroke:#000000;stroke-width:1.0;"\r
248           "    stroke-linejoin:miter;stroke-opacity:1.0000000;"\r
249           "    stroke-miterlimit:4.0000000;stroke-dasharray:none\"\n"\r
250           "  x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"/>\n"\r
251           "<text\n"\r
252           "  style=\"font-size:24.000000;font-style:normal;font-weight:normal;"\r
253           "    fill:#000000;fill-opacity:1.0000000;stroke:none;"\r
254           "    font-family:Bitstream Vera Sans\"\n"\r
255           "  x=\"10\" y=\"26\">%d x %d</text>\n" //# VALUES HERE\r
256           "</svg>\n\n";\r
257 \r
258     //if (!Glib::get_charset()) //If we are not utf8\r
259     fileName = Glib::filename_to_utf8(fileName);\r
260 \r
261     //Fill in the template\r
262     /* FIXME: Do proper XML quoting for fileName. */\r
263     gchar *xmlBuffer = g_strdup_printf(xformat,\r
264            previewWidth, previewHeight,\r
265            imgX, imgY, scaledImgWidth, scaledImgHeight,\r
266            fileName.c_str(),\r
267            rectX, rectY, rectWidth, rectHeight,\r
268            imgWidth, imgHeight);\r
269 \r
270     //g_message("%s\n", xmlBuffer);\r
271 \r
272     //now show it!\r
273     setFromMem(xmlBuffer);\r
274     g_free(xmlBuffer);\r
275 }\r
276 \r
277 \r
278 \r
279 void SVGPreview::showNoPreview()\r
280 {\r
281     //Are we already showing it?\r
282     if (showingNoPreview)\r
283         return;\r
284 \r
285     //Arbitrary size of svg doc -- rather 'portrait' shaped\r
286     gint previewWidth  = 300;\r
287     gint previewHeight = 600;\r
288 \r
289     //Our template.  Modify to taste\r
290     gchar const *xformat =\r
291           "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"\r
292           "<svg\n"\r
293           "xmlns=\"http://www.w3.org/2000/svg\"\n"\r
294           "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"\r
295           "width=\"%d\" height=\"%d\">\n" //# VALUES HERE\r
296           "<g transform=\"translate(-190,24.27184)\" style=\"opacity:0.12\">\n"\r
297           "<path\n"\r
298           "style=\"font-size:12;fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.936193pt\"\n"\r
299           "d=\"M 397.64309 320.25301 L 280.39197 282.517 L 250.74227 124.83447 L 345.08225 "\r
300           "29.146783 L 393.59996 46.667064 L 483.89679 135.61619 L 397.64309 320.25301 z \"\n"\r
301           "id=\"whiteSpace\" />\n"\r
302           "<path\n"\r
303           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"\r
304           "d=\"M 476.95792 339.17168 C 495.78197 342.93607 499.54842 356.11361 495.78197 359.87802 "\r
305           "C 492.01856 363.6434 482.6065 367.40781 475.07663 361.76014 C 467.54478 "\r
306           "356.11361 467.54478 342.93607 476.95792 339.17168 z \"\n"\r
307           "id=\"droplet01\" />\n"\r
308           "<path\n"\r
309           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"\r
310           "d=\"M 286.46194 340.42914 C 284.6277 340.91835 269.30405 327.71337 257.16909 333.8338 "\r
311           "C 245.03722 339.95336 236.89276 353.65666 248.22676 359.27982 C 259.56184 364.90298 "\r
312           "267.66433 358.41867 277.60113 351.44119 C 287.53903 344.46477 "\r
313           "287.18046 343.1206 286.46194 340.42914 z \"\n"\r
314           "id=\"droplet02\" />\n"\r
315           "<path\n"\r
316           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"\r
317           "d=\"M 510.35756 306.92856 C 520.59494 304.36879 544.24333 306.92856 540.47688 321.98634 "\r
318           "C 536.71354 337.04806 504.71297 331.39827 484.00371 323.87156 C 482.12141 "\r
319           "308.81083 505.53237 308.13423 510.35756 306.92856 z \"\n"\r
320           "id=\"droplet03\" />\n"\r
321           "<path\n"\r
322           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"\r
323           "d=\"M 359.2403 21.362537 C 347.92693 21.362537 336.6347 25.683095 327.96556 34.35223 "\r
324           "L 173.87387 188.41466 C 165.37697 196.9114 161.1116 207.95813 160.94269 219.04577 "\r
325           "L 160.88418 219.04577 C 160.88418 219.08524 160.94076 219.12322 160.94269 219.16279 "\r
326           "C 160.94033 219.34888 160.88418 219.53256 160.88418 219.71865 L 161.14748 219.71865 "\r
327           "C 164.0966 230.93917 240.29699 245.24198 248.79866 253.74346 C 261.63771 266.58263 "\r
328           "199.5652 276.01151 212.4041 288.85074 C 225.24316 301.68979 289.99433 313.6933 302.8346 "\r
329           "326.53254 C 315.67368 339.37161 276.5961 353.04289 289.43532 365.88196 C 302.27439 "\r
330           "378.72118 345.40201 362.67257 337.5908 396.16198 C 354.92909 413.50026 391.10302 "\r
331           "405.2208 415.32417 387.88252 C 428.16323 375.04345 390.6948 376.17577 403.53397 "\r
332           "363.33668 C 416.37304 350.49745 448.78128 350.4282 476.08902 319.71589 C 465.09739 "\r
333           "302.62116 429.10801 295.34136 441.94719 282.50217 C 454.78625 269.66311 479.74708 "\r
334           "276.18423 533.60644 251.72479 C 559.89837 239.78398 557.72636 230.71459 557.62567 "\r
335           "219.71865 C 557.62356 219.48727 557.62567 219.27892 557.62567 219.04577 L 557.56716 "\r
336           "219.04577 C 557.3983 207.95812 553.10345 196.9114 544.60673 188.41466 L 390.54428 "\r
337           "34.35223 C 381.87515 25.683095 370.55366 21.362537 359.2403 21.362537 z M 357.92378 "\r
338           "41.402939 C 362.95327 41.533963 367.01541 45.368018 374.98006 50.530832 L 447.76915 "\r
339           "104.50827 C 448.56596 105.02498 449.32484 105.564 450.02187 106.11735 C 450.7189 106.67062 "\r
340           "451.3556 107.25745 451.95277 107.84347 C 452.54997 108.42842 453.09281 109.01553 453.59111 "\r
341           "109.62808 C 454.08837 110.24052 454.53956 110.86661 454.93688 111.50048 C 455.33532 112.13538 "\r
342           "455.69164 112.78029 455.9901 113.43137 C 456.28877 114.08363 456.52291 114.75639 456.7215 "\r
343           "115.42078 C 456.92126 116.08419 457.08982 116.73973 457.18961 117.41019 C 457.28949 "\r
344           "118.08184 457.33588 118.75535 457.33588 119.42886 L 414.21245 98.598549 L 409.9118 "\r
345           "131.16055 L 386.18512 120.04324 L 349.55654 144.50131 L 335.54288 96.1703 L 317.4919 "\r
346           "138.4453 L 267.08369 143.47735 L 267.63956 121.03795 C 267.63956 115.64823 296.69685 "\r
347           "77.915899 314.39075 68.932902 L 346.77721 45.674327 C 351.55594 42.576634 354.90608 "\r
348           "41.324327 357.92378 41.402939 z M 290.92738 261.61333 C 313.87149 267.56365 339.40299 "\r
349           "275.37038 359.88393 275.50997 L 360.76161 284.72563 C 343.2235 282.91785 306.11346 "\r
350           "274.45012 297.36372 269.98057 L 290.92738 261.61333 z \"\n"\r
351           "id=\"mountainDroplet\" />\n"\r
352           "</g> <g transform=\"translate(-20,0)\">\n"\r
353           "<text xml:space=\"preserve\"\n"\r
354           "style=\"font-size:32.000000;font-style:normal;font-variant:normal;font-weight:bold;"\r
355           "font-stretch:normal;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000pt;"\r
356           "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"\r
357           "font-family:Bitstream Vera Sans;text-anchor:middle;writing-mode:lr\"\n"\r
358           "x=\"190\" y=\"240\">%s</text></g>\n"  //# VALUE HERE\r
359           "</svg>\n\n";\r
360 \r
361     //Fill in the template\r
362     gchar *xmlBuffer = g_strdup_printf(xformat,\r
363            previewWidth, previewHeight, _("No preview"));\r
364 \r
365     //g_message("%s\n", xmlBuffer);\r
366 \r
367     //now show it!\r
368     setFromMem(xmlBuffer);\r
369     g_free(xmlBuffer);\r
370     showingNoPreview = true;\r
371 \r
372 }\r
373 \r
374 \r
375 /**\r
376  * Inform the user that the svg file is too large to be displayed.\r
377  * This does not check for sizes of embedded images (yet)\r
378  */\r
379 void SVGPreview::showTooLarge(long fileLength)\r
380 {\r
381 \r
382     //Arbitrary size of svg doc -- rather 'portrait' shaped\r
383     gint previewWidth  = 300;\r
384     gint previewHeight = 600;\r
385 \r
386     //Our template.  Modify to taste\r
387     gchar const *xformat =\r
388           "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"\r
389           "<svg\n"\r
390           "xmlns=\"http://www.w3.org/2000/svg\"\n"\r
391           "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"\r
392           "width=\"%d\" height=\"%d\">\n"  //# VALUES HERE\r
393           "<g transform=\"translate(-170,24.27184)\" style=\"opacity:0.12\">\n"\r
394           "<path\n"\r
395           "style=\"font-size:12;fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.936193pt\"\n"\r
396           "d=\"M 397.64309 320.25301 L 280.39197 282.517 L 250.74227 124.83447 L 345.08225 "\r
397           "29.146783 L 393.59996 46.667064 L 483.89679 135.61619 L 397.64309 320.25301 z \"\n"\r
398           "id=\"whiteSpace\" />\n"\r
399           "<path\n"\r
400           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"\r
401           "d=\"M 476.95792 339.17168 C 495.78197 342.93607 499.54842 356.11361 495.78197 359.87802 "\r
402           "C 492.01856 363.6434 482.6065 367.40781 475.07663 361.76014 C 467.54478 "\r
403           "356.11361 467.54478 342.93607 476.95792 339.17168 z \"\n"\r
404           "id=\"droplet01\" />\n"\r
405           "<path\n"\r
406           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"\r
407           "d=\"M 286.46194 340.42914 C 284.6277 340.91835 269.30405 327.71337 257.16909 333.8338 "\r
408           "C 245.03722 339.95336 236.89276 353.65666 248.22676 359.27982 C 259.56184 364.90298 "\r
409           "267.66433 358.41867 277.60113 351.44119 C 287.53903 344.46477 "\r
410           "287.18046 343.1206 286.46194 340.42914 z \"\n"\r
411           "id=\"droplet02\" />\n"\r
412           "<path\n"\r
413           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"\r
414           "d=\"M 510.35756 306.92856 C 520.59494 304.36879 544.24333 306.92856 540.47688 321.98634 "\r
415           "C 536.71354 337.04806 504.71297 331.39827 484.00371 323.87156 C 482.12141 "\r
416           "308.81083 505.53237 308.13423 510.35756 306.92856 z \"\n"\r
417           "id=\"droplet03\" />\n"\r
418           "<path\n"\r
419           "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"\r
420           "d=\"M 359.2403 21.362537 C 347.92693 21.362537 336.6347 25.683095 327.96556 34.35223 "\r
421           "L 173.87387 188.41466 C 165.37697 196.9114 161.1116 207.95813 160.94269 219.04577 "\r
422           "L 160.88418 219.04577 C 160.88418 219.08524 160.94076 219.12322 160.94269 219.16279 "\r
423           "C 160.94033 219.34888 160.88418 219.53256 160.88418 219.71865 L 161.14748 219.71865 "\r
424           "C 164.0966 230.93917 240.29699 245.24198 248.79866 253.74346 C 261.63771 266.58263 "\r
425           "199.5652 276.01151 212.4041 288.85074 C 225.24316 301.68979 289.99433 313.6933 302.8346 "\r
426           "326.53254 C 315.67368 339.37161 276.5961 353.04289 289.43532 365.88196 C 302.27439 "\r
427           "378.72118 345.40201 362.67257 337.5908 396.16198 C 354.92909 413.50026 391.10302 "\r
428           "405.2208 415.32417 387.88252 C 428.16323 375.04345 390.6948 376.17577 403.53397 "\r
429           "363.33668 C 416.37304 350.49745 448.78128 350.4282 476.08902 319.71589 C 465.09739 "\r
430           "302.62116 429.10801 295.34136 441.94719 282.50217 C 454.78625 269.66311 479.74708 "\r
431           "276.18423 533.60644 251.72479 C 559.89837 239.78398 557.72636 230.71459 557.62567 "\r
432           "219.71865 C 557.62356 219.48727 557.62567 219.27892 557.62567 219.04577 L 557.56716 "\r
433           "219.04577 C 557.3983 207.95812 553.10345 196.9114 544.60673 188.41466 L 390.54428 "\r
434           "34.35223 C 381.87515 25.683095 370.55366 21.362537 359.2403 21.362537 z M 357.92378 "\r
435           "41.402939 C 362.95327 41.533963 367.01541 45.368018 374.98006 50.530832 L 447.76915 "\r
436           "104.50827 C 448.56596 105.02498 449.32484 105.564 450.02187 106.11735 C 450.7189 106.67062 "\r
437           "451.3556 107.25745 451.95277 107.84347 C 452.54997 108.42842 453.09281 109.01553 453.59111 "\r
438           "109.62808 C 454.08837 110.24052 454.53956 110.86661 454.93688 111.50048 C 455.33532 112.13538 "\r
439           "455.69164 112.78029 455.9901 113.43137 C 456.28877 114.08363 456.52291 114.75639 456.7215 "\r
440           "115.42078 C 456.92126 116.08419 457.08982 116.73973 457.18961 117.41019 C 457.28949 "\r
441           "118.08184 457.33588 118.75535 457.33588 119.42886 L 414.21245 98.598549 L 409.9118 "\r
442           "131.16055 L 386.18512 120.04324 L 349.55654 144.50131 L 335.54288 96.1703 L 317.4919 "\r
443           "138.4453 L 267.08369 143.47735 L 267.63956 121.03795 C 267.63956 115.64823 296.69685 "\r
444           "77.915899 314.39075 68.932902 L 346.77721 45.674327 C 351.55594 42.576634 354.90608 "\r
445           "41.324327 357.92378 41.402939 z M 290.92738 261.61333 C 313.87149 267.56365 339.40299 "\r
446           "275.37038 359.88393 275.50997 L 360.76161 284.72563 C 343.2235 282.91785 306.11346 "\r
447           "274.45012 297.36372 269.98057 L 290.92738 261.61333 z \"\n"\r
448           "id=\"mountainDroplet\" />\n"\r
449           "</g>\n"\r
450           "<text xml:space=\"preserve\"\n"\r
451           "style=\"font-size:32.000000;font-style:normal;font-variant:normal;font-weight:bold;"\r
452           "font-stretch:normal;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000pt;"\r
453           "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"\r
454           "font-family:Bitstream Vera Sans;text-anchor:middle;writing-mode:lr\"\n"\r
455           "x=\"170\" y=\"215\">%5.1f MB</text>\n" //# VALUE HERE\r
456           "<text xml:space=\"preserve\"\n"\r
457           "style=\"font-size:24.000000;font-style:normal;font-variant:normal;font-weight:bold;"\r
458           "font-stretch:normal;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000pt;"\r
459           "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"\r
460           "font-family:Bitstream Vera Sans;text-anchor:middle;writing-mode:lr\"\n"\r
461           "x=\"180\" y=\"245\">%s</text>\n" //# VALUE HERE\r
462           "</svg>\n\n";\r
463 \r
464     //Fill in the template\r
465     double floatFileLength = ((double)fileLength) / 1048576.0;\r
466     //printf("%ld %f\n", fileLength, floatFileLength);\r
467     gchar *xmlBuffer = g_strdup_printf(xformat,\r
468            previewWidth, previewHeight, floatFileLength,\r
469            _("too large for preview"));\r
470 \r
471     //g_message("%s\n", xmlBuffer);\r
472 \r
473     //now show it!\r
474     setFromMem(xmlBuffer);\r
475     g_free(xmlBuffer);\r
476 \r
477 }\r
478 \r
479 \r
480 /**\r
481  * Return true if the string ends with the given suffix\r
482  */\r
483 static bool\r
484 hasSuffix(Glib::ustring &str, Glib::ustring &ext)\r
485 {\r
486     int strLen = str.length();\r
487     int extLen = ext.length();\r
488     if (extLen > strLen)\r
489         return false;\r
490     int strpos = strLen-1;\r
491     for (int extpos = extLen-1 ; extpos>=0 ; extpos--, strpos--)\r
492         {\r
493         Glib::ustring::value_type ch = str[strpos];\r
494         if (ch != ext[extpos])\r
495             {\r
496             if ( ((ch & 0xff80) != 0) ||\r
497                  static_cast<Glib::ustring::value_type>( g_ascii_tolower( static_cast<gchar>(0x07f & ch) ) ) != ext[extpos] )\r
498                 {\r
499                 return false;\r
500                 }\r
501             }\r
502         }\r
503     return true;\r
504 }\r
505 \r
506 \r
507 /**\r
508  * Return true if the image is loadable by Gdk, else false\r
509  */\r
510 static bool\r
511 isValidImageFile(Glib::ustring &fileName)\r
512 {\r
513     std::vector<Gdk::PixbufFormat>formats = Gdk::Pixbuf::get_formats();\r
514     for (unsigned int i=0; i<formats.size(); i++)\r
515         {\r
516         Gdk::PixbufFormat format = formats[i];\r
517         std::vector<Glib::ustring>extensions = format.get_extensions();\r
518         for (unsigned int j=0; j<extensions.size(); j++)\r
519             {\r
520             Glib::ustring ext = extensions[j];\r
521             if (hasSuffix(fileName, ext))\r
522                 return true;\r
523             }\r
524         }\r
525     return false;\r
526 }\r
527 \r
528 bool SVGPreview::set(Glib::ustring &fileName, int dialogType)\r
529 {\r
530 \r
531     if (!Glib::file_test(fileName, Glib::FILE_TEST_EXISTS))\r
532         return false;\r
533 \r
534     //g_message("fname:%s", fileName.c_str());\r
535 \r
536     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {\r
537         showNoPreview();\r
538         return false;\r
539     }\r
540 \r
541     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR))\r
542         {\r
543         Glib::ustring fileNameUtf8 = Glib::filename_to_utf8(fileName);\r
544         gchar *fName = (gchar *)fileNameUtf8.c_str();\r
545         struct stat info;\r
546         if (g_stat(fName, &info))\r
547             {\r
548             g_warning("SVGPreview::set() : %s : %s",\r
549                            fName, strerror(errno));\r
550             return FALSE;\r
551             }\r
552         long fileLen = info.st_size;\r
553         if (fileLen > 0x150000L)\r
554             {\r
555             showingNoPreview = false;\r
556             showTooLarge(fileLen);\r
557             return FALSE;\r
558             }\r
559         }\r
560 \r
561     Glib::ustring svg = ".svg";\r
562     Glib::ustring svgz = ".svgz";\r
563 \r
564     if ((dialogType == SVG_TYPES || dialogType == IMPORT_TYPES) &&\r
565            (hasSuffix(fileName, svg) || hasSuffix(fileName, svgz)   )\r
566         ) {\r
567         bool retval = setFileName(fileName);\r
568         showingNoPreview = false;\r
569         return retval;\r
570     } else if (isValidImageFile(fileName)) {\r
571         showImage(fileName);\r
572         showingNoPreview = false;\r
573         return true;\r
574     } else {\r
575         showNoPreview();\r
576         return false;\r
577     }\r
578 }\r
579 \r
580 \r
581 SVGPreview::SVGPreview()\r
582 {\r
583     if (!INKSCAPE)\r
584         inkscape_application_init("",false);\r
585     document = NULL;\r
586     viewerGtk = NULL;\r
587     set_size_request(150,150);\r
588     showingNoPreview = false;\r
589 }\r
590 \r
591 SVGPreview::~SVGPreview()\r
592 {\r
593 \r
594 }\r
595 \r
596 \r
597 /*#########################################################################\r
598 ### F I L E     D I A L O G    B A S E    C L A S S\r
599 #########################################################################*/\r
600 \r
601 void FileDialogBaseGtk::internalSetup()\r
602 {\r
603     bool enablePreview =\r
604         (bool)prefs_get_int_attribute( preferenceBase.c_str(),\r
605              "enable_preview", 1 );\r
606 \r
607     previewCheckbox.set_label( Glib::ustring(_("Enable preview")) );\r
608     previewCheckbox.set_active( enablePreview );\r
609 \r
610     previewCheckbox.signal_toggled().connect(\r
611         sigc::mem_fun(*this, &FileDialogBaseGtk::_previewEnabledCB) );\r
612 \r
613     //Catch selection-changed events, so we can adjust the text widget\r
614     signal_update_preview().connect(\r
615          sigc::mem_fun(*this, &FileDialogBaseGtk::_updatePreviewCallback) );\r
616 \r
617     //###### Add a preview widget\r
618     set_preview_widget(svgPreview);\r
619     set_preview_widget_active( enablePreview );\r
620     set_use_preview_label (false);\r
621 \r
622 }\r
623 \r
624 \r
625 void FileDialogBaseGtk::cleanup( bool showConfirmed )\r
626 {\r
627     if ( showConfirmed )\r
628         prefs_set_int_attribute( preferenceBase.c_str(),\r
629                "enable_preview", previewCheckbox.get_active() );\r
630 }\r
631 \r
632 \r
633 void FileDialogBaseGtk::_previewEnabledCB()\r
634 {\r
635     bool enabled = previewCheckbox.get_active();\r
636     set_preview_widget_active(enabled);\r
637     if ( enabled ) {\r
638         _updatePreviewCallback();\r
639     }\r
640 }\r
641 \r
642 \r
643 \r
644 /**\r
645  * Callback for checking if the preview needs to be redrawn\r
646  */\r
647 void FileDialogBaseGtk::_updatePreviewCallback()\r
648 {\r
649     Glib::ustring fileName = get_preview_filename();\r
650 \r
651 #ifdef WITH_GNOME_VFS\r
652     if ( fileName.empty() && gnome_vfs_initialized() ) {\r
653         fileName = get_preview_uri();\r
654     }\r
655 #endif\r
656 \r
657     if (fileName.empty()) {\r
658         return;\r
659     }\r
660 \r
661     svgPreview.set(fileName, dialogType);\r
662 }\r
663 \r
664 \r
665 /*#########################################################################\r
666 ### F I L E    O P E N\r
667 #########################################################################*/\r
668 \r
669 /**\r
670  * Constructor.  Not called directly.  Use the factory.\r
671  */\r
672 FileOpenDialogImplGtk::FileOpenDialogImplGtk(Gtk::Window& parentWindow,\r
673                                        const Glib::ustring &dir,\r
674                                        FileDialogType fileTypes,\r
675                                        const Glib::ustring &title) :\r
676     FileDialogBaseGtk(parentWindow, title, fileTypes, "dialogs.open")\r
677 {\r
678 \r
679 \r
680     /* One file at a time */\r
681     /* And also Multiple Files */\r
682     set_select_multiple(true);\r
683 \r
684 #ifdef WITH_GNOME_VFS\r
685     if (gnome_vfs_initialized()) {\r
686         set_local_only(false);\r
687     }\r
688 #endif\r
689 \r
690     /* Initalize to Autodetect */\r
691     extension = NULL;\r
692     /* No filename to start out with */\r
693     myFilename = "";\r
694 \r
695     /* Set our dialog type (open, import, etc...)*/\r
696     dialogType = fileTypes;\r
697 \r
698 \r
699     /* Set the pwd and/or the filename */\r
700     if (dir.size() > 0)\r
701         {\r
702         Glib::ustring udir(dir);\r
703         Glib::ustring::size_type len = udir.length();\r
704         // leaving a trailing backslash on the directory name leads to the infamous\r
705         // double-directory bug on win32\r
706         if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);\r
707         set_current_folder(udir.c_str());\r
708         }\r
709 \r
710 \r
711     set_extra_widget( previewCheckbox );\r
712 \r
713 \r
714     //###### Add the file types menu\r
715     createFilterMenu();\r
716 \r
717     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);\r
718     set_default(*add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_OK));\r
719         \r
720         //###### Allow easy access to our examples folder                \r
721     if(Inkscape::IO::file_test(INKSCAPE_EXAMPLESDIR,\r
722                 (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))\r
723         {\r
724         add_shortcut_folder(INKSCAPE_EXAMPLESDIR);\r
725     }\r
726 }\r
727 \r
728 /**\r
729  * Destructor\r
730  */\r
731 FileOpenDialogImplGtk::~FileOpenDialogImplGtk()\r
732 {\r
733 \r
734 }\r
735 \r
736 void FileOpenDialogImplGtk::createFilterMenu()\r
737 {\r
738     Gtk::FileFilter allInkscapeFilter;\r
739     allInkscapeFilter.set_name(_("All Inkscape Files"));\r
740     extensionMap[Glib::ustring(_("All Inkscape Files"))]=NULL;\r
741     add_filter(allInkscapeFilter);\r
742 \r
743     Gtk::FileFilter allFilter;\r
744     allFilter.set_name(_("All Files"));\r
745     extensionMap[Glib::ustring(_("All Files"))]=NULL;\r
746     allFilter.add_pattern("*");\r
747     add_filter(allFilter);\r
748 \r
749     Gtk::FileFilter allImageFilter;\r
750     allImageFilter.set_name(_("All Images"));\r
751     extensionMap[Glib::ustring(_("All Images"))]=NULL;\r
752     add_filter(allImageFilter);\r
753 \r
754     //patterns added dynamically below\r
755     Inkscape::Extension::DB::InputList extension_list;\r
756     Inkscape::Extension::db.get_input_list(extension_list);\r
757 \r
758     for (Inkscape::Extension::DB::InputList::iterator current_item = extension_list.begin();\r
759          current_item != extension_list.end(); current_item++)\r
760     {\r
761         Inkscape::Extension::Input * imod = *current_item;\r
762 \r
763         // FIXME: would be nice to grey them out instead of not listing them\r
764         if (imod->deactivated()) continue;\r
765 \r
766         Glib::ustring upattern("*");\r
767         Glib::ustring extension = imod->get_extension();\r
768         fileDialogExtensionToPattern(upattern, extension);\r
769 \r
770         Gtk::FileFilter filter;\r
771         Glib::ustring uname(_(imod->get_filetypename()));\r
772         filter.set_name(uname);\r
773         filter.add_pattern(upattern);\r
774         add_filter(filter);\r
775         extensionMap[uname] = imod;\r
776 \r
777         //g_message("ext %s:%s '%s'\n", ioext->name, ioext->mimetype, upattern.c_str());\r
778         allInkscapeFilter.add_pattern(upattern);\r
779         if ( strncmp("image", imod->get_mimetype(), 5)==0 )\r
780             allImageFilter.add_pattern(upattern);\r
781     }\r
782 \r
783     return;\r
784 }\r
785 \r
786 /**\r
787  * Show this dialog modally.  Return true if user hits [OK]\r
788  */\r
789 bool\r
790 FileOpenDialogImplGtk::show()\r
791 {\r
792     Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());\r
793     if (s.length() == 0)\r
794         s = getcwd (NULL, 0);\r
795     set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing\r
796     set_modal (TRUE);                      //Window\r
797     sp_transientize((GtkWidget *)gobj());  //Make transient\r
798     gint b = run();                        //Dialog\r
799     svgPreview.showNoPreview();\r
800     hide();\r
801 \r
802     if (b == Gtk::RESPONSE_OK)\r
803         {\r
804         //This is a hack, to avoid the warning messages that\r
805         //Gtk::FileChooser::get_filter() returns\r
806         //should be:  Gtk::FileFilter *filter = get_filter();\r
807         GtkFileChooser *gtkFileChooser = Gtk::FileChooser::gobj();\r
808         GtkFileFilter *filter = gtk_file_chooser_get_filter(gtkFileChooser);\r
809         if (filter)\r
810             {\r
811             //Get which extension was chosen, if any\r
812             extension = extensionMap[gtk_file_filter_get_name(filter)];\r
813             }\r
814         myFilename = get_filename();\r
815 #ifdef WITH_GNOME_VFS\r
816         if (myFilename.empty() && gnome_vfs_initialized())\r
817             myFilename = get_uri();\r
818 #endif\r
819         cleanup( true );\r
820         return TRUE;\r
821         }\r
822     else\r
823        {\r
824        cleanup( false );\r
825        return FALSE;\r
826        }\r
827 }\r
828 \r
829 \r
830 \r
831 \r
832 /**\r
833  * Get the file extension type that was selected by the user. Valid after an [OK]\r
834  */\r
835 Inkscape::Extension::Extension *\r
836 FileOpenDialogImplGtk::getSelectionType()\r
837 {\r
838     return extension;\r
839 }\r
840 \r
841 \r
842 /**\r
843  * Get the file name chosen by the user.   Valid after an [OK]\r
844  */\r
845 Glib::ustring\r
846 FileOpenDialogImplGtk::getFilename (void)\r
847 {\r
848     return g_strdup(myFilename.c_str());\r
849 }\r
850 \r
851 \r
852 /**\r
853  * To Get Multiple filenames selected at-once.\r
854  */\r
855 std::vector<Glib::ustring>FileOpenDialogImplGtk::getFilenames()\r
856 {\r
857     std::vector<Glib::ustring> result = get_filenames();\r
858 #ifdef WITH_GNOME_VFS\r
859     if (result.empty() && gnome_vfs_initialized())\r
860         result = get_uris();\r
861 #endif\r
862     return result;\r
863 }\r
864 \r
865 \r
866 \r
867 \r
868 \r
869 \r
870 //########################################################################\r
871 //# F I L E    S A V E\r
872 //########################################################################\r
873 \r
874 /**\r
875  * Constructor\r
876  */\r
877 FileSaveDialogImplGtk::FileSaveDialogImplGtk(Gtk::Window &parentWindow,\r
878             const Glib::ustring &dir,\r
879             FileDialogType fileTypes,\r
880             const Glib::ustring &title,\r
881             const Glib::ustring &default_key) :\r
882     FileDialogBaseGtk(parentWindow, title, Gtk::FILE_CHOOSER_ACTION_SAVE, fileTypes, "dialogs.save_as")\r
883 {\r
884     /* One file at a time */\r
885     set_select_multiple(false);\r
886 \r
887 #ifdef WITH_GNOME_VFS\r
888     if (gnome_vfs_initialized()) {\r
889         set_local_only(false);\r
890     }\r
891 #endif\r
892 \r
893     /* Initalize to Autodetect */\r
894     extension = NULL;\r
895     /* No filename to start out with */\r
896     myFilename = "";\r
897 \r
898     /* Set our dialog type (save, export, etc...)*/\r
899     dialogType = fileTypes;\r
900 \r
901     /* Set the pwd and/or the filename */\r
902     if (dir.size() > 0)\r
903         {\r
904         Glib::ustring udir(dir);\r
905         Glib::ustring::size_type len = udir.length();\r
906         // leaving a trailing backslash on the directory name leads to the infamous\r
907         // double-directory bug on win32\r
908         if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);\r
909         myFilename = udir;\r
910         }\r
911 \r
912     //###### Add the file types menu\r
913     //createFilterMenu();\r
914 \r
915     //###### Do we want the .xxx extension automatically added?\r
916     fileTypeCheckbox.set_label(Glib::ustring(_("Append filename extension automatically")));\r
917     fileTypeCheckbox.set_active( (bool)prefs_get_int_attribute("dialogs.save_as",\r
918                                                                "append_extension", 1) );\r
919 \r
920     createFileTypeMenu();\r
921     fileTypeComboBox.set_size_request(200,40);\r
922     fileTypeComboBox.signal_changed().connect(\r
923          sigc::mem_fun(*this, &FileSaveDialogImplGtk::fileTypeChangedCallback) );\r
924 \r
925 \r
926     childBox.pack_start( checksBox );\r
927     childBox.pack_end( fileTypeComboBox );\r
928     checksBox.pack_start( fileTypeCheckbox );\r
929     checksBox.pack_start( previewCheckbox );\r
930 \r
931     set_extra_widget( childBox );\r
932 \r
933     //Let's do some customization\r
934     fileNameEntry = NULL;\r
935     Gtk::Container *cont = get_toplevel();\r
936     std::vector<Gtk::Entry *> entries;\r
937     findEntryWidgets(cont, entries);\r
938     //g_message("Found %d entry widgets\n", entries.size());\r
939     if (entries.size() >=1 )\r
940         {\r
941         //Catch when user hits [return] on the text field\r
942         fileNameEntry = entries[0];\r
943         fileNameEntry->signal_activate().connect(\r
944              sigc::mem_fun(*this, &FileSaveDialogImplGtk::fileNameEntryChangedCallback) );\r
945         }\r
946 \r
947     //Let's do more customization\r
948     std::vector<Gtk::Expander *> expanders;\r
949     findExpanderWidgets(cont, expanders);\r
950     //g_message("Found %d expander widgets\n", expanders.size());\r
951     if (expanders.size() >=1 )\r
952         {\r
953         //Always show the file list\r
954         Gtk::Expander *expander = expanders[0];\r
955         expander->set_expanded(true);\r
956         }\r
957 \r
958 \r
959     //if (extension == NULL)\r
960     //    checkbox.set_sensitive(FALSE);\r
961 \r
962     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);\r
963     set_default(*add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_OK));\r
964 \r
965     show_all_children();\r
966 }\r
967 \r
968 /**\r
969  * Destructor\r
970  */\r
971 FileSaveDialogImplGtk::~FileSaveDialogImplGtk()\r
972 {\r
973 }\r
974 \r
975 /**\r
976  * Callback for fileNameEntry widget\r
977  */\r
978 void FileSaveDialogImplGtk::fileNameEntryChangedCallback()\r
979 {\r
980     if (!fileNameEntry)\r
981         return;\r
982 \r
983     Glib::ustring fileName = fileNameEntry->get_text();\r
984     if (!Glib::get_charset()) //If we are not utf8\r
985         fileName = Glib::filename_to_utf8(fileName);\r
986 \r
987     //g_message("User hit return.  Text is '%s'\n", fileName.c_str());\r
988 \r
989     if (!Glib::path_is_absolute(fileName)) {\r
990         //try appending to the current path\r
991         // not this way: fileName = get_current_folder() + "/" + fileName;\r
992         std::vector<Glib::ustring> pathSegments;\r
993         pathSegments.push_back( get_current_folder() );\r
994         pathSegments.push_back( fileName );\r
995         fileName = Glib::build_filename(pathSegments);\r
996     }\r
997 \r
998     //g_message("path:'%s'\n", fileName.c_str());\r
999 \r
1000     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {\r
1001         set_current_folder(fileName);\r
1002     } else if (/*Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)*/1) {\r
1003         //dialog with either (1) select a regular file or (2) cd to dir\r
1004         //simulate an 'OK'\r
1005         set_filename(fileName);\r
1006         response(Gtk::RESPONSE_OK);\r
1007     }\r
1008 }\r
1009 \r
1010 \r
1011 \r
1012 /**\r
1013  * Callback for fileNameEntry widget\r
1014  */\r
1015 void FileSaveDialogImplGtk::fileTypeChangedCallback()\r
1016 {\r
1017     int sel = fileTypeComboBox.get_active_row_number();\r
1018     if (sel<0 || sel >= (int)fileTypes.size())\r
1019         return;\r
1020     FileType type = fileTypes[sel];\r
1021     //g_message("selected: %s\n", type.name.c_str());\r
1022 \r
1023     extension = type.extension;\r
1024     Gtk::FileFilter filter;\r
1025     filter.add_pattern(type.pattern);\r
1026     set_filter(filter);\r
1027 \r
1028     updateNameAndExtension();\r
1029 }\r
1030 \r
1031 \r
1032 \r
1033 void FileSaveDialogImplGtk::createFileTypeMenu()\r
1034 {\r
1035     Inkscape::Extension::DB::OutputList extension_list;\r
1036     Inkscape::Extension::db.get_output_list(extension_list);\r
1037     knownExtensions.clear();\r
1038 \r
1039     for (Inkscape::Extension::DB::OutputList::iterator current_item = extension_list.begin();\r
1040          current_item != extension_list.end(); current_item++)\r
1041     {\r
1042         Inkscape::Extension::Output * omod = *current_item;\r
1043 \r
1044         // FIXME: would be nice to grey them out instead of not listing them\r
1045         if (omod->deactivated()) continue;\r
1046 \r
1047         FileType type;\r
1048         type.name     = (_(omod->get_filetypename()));\r
1049         type.pattern  = "*";\r
1050         Glib::ustring extension = omod->get_extension();\r
1051         knownExtensions.insert( extension.casefold() );\r
1052         fileDialogExtensionToPattern (type.pattern, extension);\r
1053         type.extension= omod;\r
1054         fileTypeComboBox.append_text(type.name);\r
1055         fileTypes.push_back(type);\r
1056     }\r
1057 \r
1058     //#Let user choose\r
1059     FileType guessType;\r
1060     guessType.name = _("Guess from extension");\r
1061     guessType.pattern = "*";\r
1062     guessType.extension = NULL;\r
1063     fileTypeComboBox.append_text(guessType.name);\r
1064     fileTypes.push_back(guessType);\r
1065 \r
1066 \r
1067     fileTypeComboBox.set_active(0);\r
1068     fileTypeChangedCallback(); //call at least once to set the filter\r
1069 }\r
1070 \r
1071 \r
1072 \r
1073 \r
1074 \r
1075 /**\r
1076  * Show this dialog modally.  Return true if user hits [OK]\r
1077  */\r
1078 bool\r
1079 FileSaveDialogImplGtk::show()\r
1080 {\r
1081     change_path(myFilename);\r
1082     set_modal (TRUE);                      //Window\r
1083     sp_transientize((GtkWidget *)gobj());  //Make transient\r
1084     gint b = run();                        //Dialog\r
1085     svgPreview.showNoPreview();\r
1086     set_preview_widget_active(false);\r
1087     hide();\r
1088 \r
1089     if (b == Gtk::RESPONSE_OK)\r
1090         {\r
1091         updateNameAndExtension();\r
1092 \r
1093         // Store changes of the "Append filename automatically" checkbox back to preferences.\r
1094         prefs_set_int_attribute("dialogs.save_as", "append_extension", fileTypeCheckbox.get_active());\r
1095 \r
1096         // Store the last used save-as filetype to preferences.\r
1097         prefs_set_string_attribute("dialogs.save_as", "default",\r
1098                                    ( extension != NULL ? extension->get_id() : "" ));\r
1099 \r
1100         cleanup( true );\r
1101 \r
1102         return TRUE;\r
1103         }\r
1104     else\r
1105         {\r
1106         cleanup( false );\r
1107 \r
1108         return FALSE;\r
1109         }\r
1110 }\r
1111 \r
1112 \r
1113 /**\r
1114  * Get the file extension type that was selected by the user. Valid after an [OK]\r
1115  */\r
1116 Inkscape::Extension::Extension *\r
1117 FileSaveDialogImplGtk::getSelectionType()\r
1118 {\r
1119     return extension;\r
1120 }\r
1121 \r
1122 void FileSaveDialogImplGtk::setSelectionType( Inkscape::Extension::Extension * key )\r
1123 {\r
1124     // If no pointer to extension is passed in, look up based on filename extension.\r
1125     if ( !key ) {\r
1126         // Not quite UTF-8 here.\r
1127         gchar *filenameLower = g_ascii_strdown(myFilename.c_str(), -1);\r
1128         for ( int i = 0; !key && (i < (int)fileTypes.size()); i++ ) {\r
1129             Inkscape::Extension::Output *ext = dynamic_cast<Inkscape::Extension::Output*>(fileTypes[i].extension);\r
1130             if ( ext && ext->get_extension() ) {\r
1131                 gchar *extensionLower = g_ascii_strdown( ext->get_extension(), -1 );\r
1132                 if ( g_str_has_suffix(filenameLower, extensionLower) ) {\r
1133                     key = fileTypes[i].extension;\r
1134                 }\r
1135                 g_free(extensionLower);\r
1136             }\r
1137         }\r
1138         g_free(filenameLower);\r
1139     }\r
1140 \r
1141     // Ensure the proper entry in the combo box is selected.\r
1142     if ( key ) {\r
1143         extension = key;\r
1144         gchar const * extensionID = extension->get_id();\r
1145         if ( extensionID ) {\r
1146             for ( int i = 0; i < (int)fileTypes.size(); i++ ) {\r
1147                 Inkscape::Extension::Extension *ext = fileTypes[i].extension;\r
1148                 if ( ext ) {\r
1149                     gchar const * id = ext->get_id();\r
1150                     if ( id && ( strcmp(extensionID, id) == 0) ) {\r
1151                         int oldSel = fileTypeComboBox.get_active_row_number();\r
1152                         if ( i != oldSel ) {\r
1153                             fileTypeComboBox.set_active(i);\r
1154                         }\r
1155                         break;\r
1156                     }\r
1157                 }\r
1158             }\r
1159         }\r
1160     }\r
1161 }\r
1162 \r
1163 \r
1164 /**\r
1165  * Get the file name chosen by the user.   Valid after an [OK]\r
1166  */\r
1167 Glib::ustring\r
1168 FileSaveDialogImplGtk::getFilename()\r
1169 {\r
1170     return myFilename;\r
1171 }\r
1172 \r
1173 \r
1174 void\r
1175 FileSaveDialogImplGtk::change_title(const Glib::ustring& title)\r
1176 {\r
1177     this->set_title(title);\r
1178 }\r
1179 \r
1180 /**\r
1181   * Change the default save path location.\r
1182   */\r
1183 void\r
1184 FileSaveDialogImplGtk::change_path(const Glib::ustring& path)\r
1185 {\r
1186     myFilename = path;\r
1187     if (Glib::file_test(myFilename, Glib::FILE_TEST_IS_DIR)) {\r
1188         //fprintf(stderr,"set_current_folder(%s)\n",myFilename.c_str());\r
1189         set_current_folder(myFilename);\r
1190     } else {\r
1191         //fprintf(stderr,"set_filename(%s)\n",myFilename.c_str());\r
1192         if ( Glib::file_test( myFilename, Glib::FILE_TEST_EXISTS ) ) {\r
1193             set_filename(myFilename);\r
1194         } else {\r
1195             std::string dirName = Glib::path_get_dirname( myFilename  );\r
1196             if ( dirName != get_current_folder() ) {\r
1197                 set_current_folder(dirName);\r
1198             }\r
1199         }\r
1200         Glib::ustring basename = Glib::path_get_basename(myFilename);\r
1201         //fprintf(stderr,"set_current_name(%s)\n",basename.c_str());\r
1202         try {\r
1203             set_current_name( Glib::filename_to_utf8(basename) );\r
1204         } catch ( Glib::ConvertError& e ) {\r
1205             g_warning( "Error converting save filename to UTF-8." );\r
1206             // try a fallback.\r
1207             set_current_name( basename );\r
1208         }\r
1209     }\r
1210 }\r
1211 \r
1212 void FileSaveDialogImplGtk::updateNameAndExtension()\r
1213 {\r
1214     // Pick up any changes the user has typed in.\r
1215     Glib::ustring tmp = get_filename();\r
1216 #ifdef WITH_GNOME_VFS\r
1217     if ( tmp.empty() && gnome_vfs_initialized() ) {\r
1218         tmp = get_uri();\r
1219     }\r
1220 #endif\r
1221     if ( !tmp.empty() ) {\r
1222         myFilename = tmp;\r
1223     }\r
1224 \r
1225     Inkscape::Extension::Output* newOut = extension ? dynamic_cast<Inkscape::Extension::Output*>(extension) : 0;\r
1226     if ( fileTypeCheckbox.get_active() && newOut ) {\r
1227         try {\r
1228             bool appendExtension = true;\r
1229             Glib::ustring utf8Name = Glib::filename_to_utf8( myFilename );\r
1230             Glib::ustring::size_type pos = utf8Name.rfind('.');\r
1231             if ( pos != Glib::ustring::npos ) {\r
1232                 Glib::ustring trail = utf8Name.substr( pos );\r
1233                 Glib::ustring foldedTrail = trail.casefold();\r
1234                 if ( (trail == ".")\r
1235                      | (foldedTrail != Glib::ustring( newOut->get_extension() ).casefold()\r
1236                         && ( knownExtensions.find(foldedTrail) != knownExtensions.end() ) ) ) {\r
1237                     utf8Name = utf8Name.erase( pos );\r
1238                 } else {\r
1239                     appendExtension = false;\r
1240                 }\r
1241             }\r
1242 \r
1243             if (appendExtension) {\r
1244                 utf8Name = utf8Name + newOut->get_extension();\r
1245                 myFilename = Glib::filename_from_utf8( utf8Name );\r
1246                 change_path(myFilename);\r
1247             }\r
1248         } catch ( Glib::ConvertError& e ) {\r
1249             // ignore\r
1250         }\r
1251     }\r
1252 }\r
1253 \r
1254 \r
1255 //########################################################################\r
1256 //# F I L E     E X P O R T\r
1257 //########################################################################\r
1258 \r
1259 /**\r
1260  * Callback for fileNameEntry widget\r
1261  */\r
1262 void FileExportDialogImpl::fileNameEntryChangedCallback()\r
1263 {\r
1264     if (!fileNameEntry)\r
1265         return;\r
1266 \r
1267     Glib::ustring fileName = fileNameEntry->get_text();\r
1268     if (!Glib::get_charset()) //If we are not utf8\r
1269         fileName = Glib::filename_to_utf8(fileName);\r
1270 \r
1271     //g_message("User hit return.  Text is '%s'\n", fileName.c_str());\r
1272 \r
1273     if (!Glib::path_is_absolute(fileName)) {\r
1274         //try appending to the current path\r
1275         // not this way: fileName = get_current_folder() + "/" + fileName;\r
1276         std::vector<Glib::ustring> pathSegments;\r
1277         pathSegments.push_back( get_current_folder() );\r
1278         pathSegments.push_back( fileName );\r
1279         fileName = Glib::build_filename(pathSegments);\r
1280     }\r
1281 \r
1282     //g_message("path:'%s'\n", fileName.c_str());\r
1283 \r
1284     if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {\r
1285         set_current_folder(fileName);\r
1286     } else if (/*Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)*/1) {\r
1287         //dialog with either (1) select a regular file or (2) cd to dir\r
1288         //simulate an 'OK'\r
1289         set_filename(fileName);\r
1290         response(Gtk::RESPONSE_OK);\r
1291     }\r
1292 }\r
1293 \r
1294 \r
1295 \r
1296 /**\r
1297  * Callback for fileNameEntry widget\r
1298  */\r
1299 void FileExportDialogImpl::fileTypeChangedCallback()\r
1300 {\r
1301     int sel = fileTypeComboBox.get_active_row_number();\r
1302     if (sel<0 || sel >= (int)fileTypes.size())\r
1303         return;\r
1304     FileType type = fileTypes[sel];\r
1305     //g_message("selected: %s\n", type.name.c_str());\r
1306     Gtk::FileFilter filter;\r
1307     filter.add_pattern(type.pattern);\r
1308     set_filter(filter);\r
1309 }\r
1310 \r
1311 \r
1312 \r
1313 void FileExportDialogImpl::createFileTypeMenu()\r
1314 {\r
1315     Inkscape::Extension::DB::OutputList extension_list;\r
1316     Inkscape::Extension::db.get_output_list(extension_list);\r
1317 \r
1318     for (Inkscape::Extension::DB::OutputList::iterator current_item = extension_list.begin();\r
1319          current_item != extension_list.end(); current_item++)\r
1320     {\r
1321         Inkscape::Extension::Output * omod = *current_item;\r
1322 \r
1323         // FIXME: would be nice to grey them out instead of not listing them\r
1324         if (omod->deactivated()) continue;\r
1325 \r
1326         FileType type;\r
1327         type.name     = (_(omod->get_filetypename()));\r
1328         type.pattern  = "*";\r
1329         Glib::ustring extension = omod->get_extension();\r
1330         fileDialogExtensionToPattern (type.pattern, extension);\r
1331         type.extension= omod;\r
1332         fileTypeComboBox.append_text(type.name);\r
1333         fileTypes.push_back(type);\r
1334     }\r
1335 \r
1336     //#Let user choose\r
1337     FileType guessType;\r
1338     guessType.name = _("Guess from extension");\r
1339     guessType.pattern = "*";\r
1340     guessType.extension = NULL;\r
1341     fileTypeComboBox.append_text(guessType.name);\r
1342     fileTypes.push_back(guessType);\r
1343 \r
1344 \r
1345     fileTypeComboBox.set_active(0);\r
1346     fileTypeChangedCallback(); //call at least once to set the filter\r
1347 }\r
1348 \r
1349 \r
1350 /**\r
1351  * Constructor\r
1352  */\r
1353 FileExportDialogImpl::FileExportDialogImpl(Gtk::Window& parentWindow,\r
1354             const Glib::ustring &dir,\r
1355             FileDialogType fileTypes,\r
1356             const Glib::ustring &title,\r
1357             const Glib::ustring &default_key) :\r
1358             FileDialogBaseGtk(parentWindow, title, Gtk::FILE_CHOOSER_ACTION_SAVE, fileTypes, "dialogs.export"),\r
1359             sourceX0Spinner("X0",         _("Left edge of source")),\r
1360             sourceY0Spinner("Y0",         _("Top edge of source")),\r
1361             sourceX1Spinner("X1",         _("Right edge of source")),\r
1362             sourceY1Spinner("Y1",         _("Bottom edge of source")),\r
1363             sourceWidthSpinner("Width",   _("Source width")),\r
1364             sourceHeightSpinner("Height", _("Source height")),\r
1365             destWidthSpinner("Width",     _("Destination width")),\r
1366             destHeightSpinner("Height",   _("Destination height")),\r
1367             destDPISpinner("DPI",         _("Resolution (dots per inch)"))\r
1368 {\r
1369     append_extension = (bool)prefs_get_int_attribute("dialogs.save_as", "append_extension", 1);\r
1370 \r
1371     /* One file at a time */\r
1372     set_select_multiple(false);\r
1373 \r
1374 #ifdef WITH_GNOME_VFS\r
1375     if (gnome_vfs_initialized()) {\r
1376         set_local_only(false);\r
1377     }\r
1378 #endif\r
1379 \r
1380     /* Initalize to Autodetect */\r
1381     extension = NULL;\r
1382     /* No filename to start out with */\r
1383     myFilename = "";\r
1384 \r
1385     /* Set our dialog type (save, export, etc...)*/\r
1386     dialogType = fileTypes;\r
1387 \r
1388     /* Set the pwd and/or the filename */\r
1389     if (dir.size()>0)\r
1390         {\r
1391         Glib::ustring udir(dir);\r
1392         Glib::ustring::size_type len = udir.length();\r
1393         // leaving a trailing backslash on the directory name leads to the infamous\r
1394         // double-directory bug on win32\r
1395         if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);\r
1396         set_current_folder(udir.c_str());\r
1397         }\r
1398 \r
1399     //#########################################\r
1400     //## EXTRA WIDGET -- SOURCE SIDE\r
1401     //#########################################\r
1402 \r
1403     //##### Export options buttons/spinners, etc\r
1404     documentButton.set_label(_("Document"));\r
1405     scopeBox.pack_start(documentButton);\r
1406     scopeGroup = documentButton.get_group();\r
1407 \r
1408     pageButton.set_label(_("Page"));\r
1409     pageButton.set_group(scopeGroup);\r
1410     scopeBox.pack_start(pageButton);\r
1411 \r
1412     selectionButton.set_label(_("Selection"));\r
1413     selectionButton.set_group(scopeGroup);\r
1414     scopeBox.pack_start(selectionButton);\r
1415 \r
1416     customButton.set_label(_("Custom"));\r
1417     customButton.set_group(scopeGroup);\r
1418     scopeBox.pack_start(customButton);\r
1419 \r
1420     sourceBox.pack_start(scopeBox);\r
1421 \r
1422 \r
1423 \r
1424     //dimension buttons\r
1425     sourceTable.resize(3,3);\r
1426     sourceTable.attach(sourceX0Spinner,     0,1,0,1);\r
1427     sourceTable.attach(sourceY0Spinner,     1,2,0,1);\r
1428     sourceUnitsSpinner.setUnitType(UNIT_TYPE_LINEAR);\r
1429     sourceTable.attach(sourceUnitsSpinner,  2,3,0,1);\r
1430     sourceTable.attach(sourceX1Spinner,     0,1,1,2);\r
1431     sourceTable.attach(sourceY1Spinner,     1,2,1,2);\r
1432     sourceTable.attach(sourceWidthSpinner,  0,1,2,3);\r
1433     sourceTable.attach(sourceHeightSpinner, 1,2,2,3);\r
1434 \r
1435     sourceBox.pack_start(sourceTable);\r
1436     sourceFrame.set_label(_("Source"));\r
1437     sourceFrame.add(sourceBox);\r
1438     exportOptionsBox.pack_start(sourceFrame);\r
1439 \r
1440 \r
1441     //#########################################\r
1442     //## EXTRA WIDGET -- SOURCE SIDE\r
1443     //#########################################\r
1444 \r
1445 \r
1446     destTable.resize(3,3);\r
1447     destTable.attach(destWidthSpinner,    0,1,0,1);\r
1448     destTable.attach(destHeightSpinner,   1,2,0,1);\r
1449     destUnitsSpinner.setUnitType(UNIT_TYPE_LINEAR);\r
1450     destTable.attach(destUnitsSpinner,    2,3,0,1);\r
1451     destTable.attach(destDPISpinner,      0,1,1,2);\r
1452 \r
1453     destBox.pack_start(destTable);\r
1454 \r
1455 \r
1456     cairoButton.set_label(_("Cairo"));\r
1457     otherOptionBox.pack_start(cairoButton);\r
1458 \r
1459     antiAliasButton.set_label(_("Antialias"));\r
1460     otherOptionBox.pack_start(antiAliasButton);\r
1461 \r
1462     backgroundButton.set_label(_("Background"));\r
1463     otherOptionBox.pack_start(backgroundButton);\r
1464 \r
1465     destBox.pack_start(otherOptionBox);\r
1466 \r
1467 \r
1468 \r
1469 \r
1470 \r
1471     //###### File options\r
1472     //###### Do we want the .xxx extension automatically added?\r
1473     fileTypeCheckbox.set_label(Glib::ustring(_("Append filename extension automatically")));\r
1474     fileTypeCheckbox.set_active(append_extension);\r
1475     destBox.pack_start(fileTypeCheckbox);\r
1476 \r
1477     //###### File type menu\r
1478     createFileTypeMenu();\r
1479     fileTypeComboBox.set_size_request(200,40);\r
1480     fileTypeComboBox.signal_changed().connect(\r
1481          sigc::mem_fun(*this, &FileExportDialogImpl::fileTypeChangedCallback) );\r
1482 \r
1483     destBox.pack_start(fileTypeComboBox);\r
1484 \r
1485     destFrame.set_label(_("Destination"));\r
1486     destFrame.add(destBox);\r
1487     exportOptionsBox.pack_start(destFrame);\r
1488 \r
1489     //##### Put the two boxes and their parent onto the dialog\r
1490     exportOptionsBox.pack_start(sourceFrame);\r
1491     exportOptionsBox.pack_start(destFrame);\r
1492 \r
1493     set_extra_widget(exportOptionsBox);\r
1494 \r
1495 \r
1496 \r
1497 \r
1498     //Let's do some customization\r
1499     fileNameEntry = NULL;\r
1500     Gtk::Container *cont = get_toplevel();\r
1501     std::vector<Gtk::Entry *> entries;\r
1502     findEntryWidgets(cont, entries);\r
1503     //g_message("Found %d entry widgets\n", entries.size());\r
1504     if (entries.size() >=1 )\r
1505         {\r
1506         //Catch when user hits [return] on the text field\r
1507         fileNameEntry = entries[0];\r
1508         fileNameEntry->signal_activate().connect(\r
1509              sigc::mem_fun(*this, &FileExportDialogImpl::fileNameEntryChangedCallback) );\r
1510         }\r
1511 \r
1512     //Let's do more customization\r
1513     std::vector<Gtk::Expander *> expanders;\r
1514     findExpanderWidgets(cont, expanders);\r
1515     //g_message("Found %d expander widgets\n", expanders.size());\r
1516     if (expanders.size() >=1 )\r
1517         {\r
1518         //Always show the file list\r
1519         Gtk::Expander *expander = expanders[0];\r
1520         expander->set_expanded(true);\r
1521         }\r
1522 \r
1523 \r
1524     //if (extension == NULL)\r
1525     //    checkbox.set_sensitive(FALSE);\r
1526 \r
1527     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);\r
1528     set_default(*add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_OK));\r
1529 \r
1530     show_all_children();\r
1531 }\r
1532 \r
1533 /**\r
1534  * Destructor\r
1535  */\r
1536 FileExportDialogImpl::~FileExportDialogImpl()\r
1537 {\r
1538 }\r
1539 \r
1540 \r
1541 \r
1542 /**\r
1543  * Show this dialog modally.  Return true if user hits [OK]\r
1544  */\r
1545 bool\r
1546 FileExportDialogImpl::show()\r
1547 {\r
1548     Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());\r
1549     if (s.length() == 0)\r
1550         s = getcwd (NULL, 0);\r
1551     set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing\r
1552     set_modal (TRUE);                      //Window\r
1553     sp_transientize((GtkWidget *)gobj());  //Make transient\r
1554     gint b = run();                        //Dialog\r
1555     svgPreview.showNoPreview();\r
1556     hide();\r
1557 \r
1558     if (b == Gtk::RESPONSE_OK)\r
1559         {\r
1560         int sel = fileTypeComboBox.get_active_row_number ();\r
1561         if (sel>=0 && sel< (int)fileTypes.size())\r
1562             {\r
1563             FileType &type = fileTypes[sel];\r
1564             extension = type.extension;\r
1565             }\r
1566         myFilename = get_filename();\r
1567 #ifdef WITH_GNOME_VFS\r
1568         if ( myFilename.empty() && gnome_vfs_initialized() ) {\r
1569             myFilename = get_uri();\r
1570         }\r
1571 #endif\r
1572 \r
1573         /*\r
1574 \r
1575         // FIXME: Why do we have more code\r
1576 \r
1577         append_extension = checkbox.get_active();\r
1578         prefs_set_int_attribute("dialogs.save_as", "append_extension", append_extension);\r
1579         prefs_set_string_attribute("dialogs.save_as", "default",\r
1580                   ( extension != NULL ? extension->get_id() : "" ));\r
1581         */\r
1582         return TRUE;\r
1583         }\r
1584     else\r
1585         {\r
1586         return FALSE;\r
1587         }\r
1588 }\r
1589 \r
1590 \r
1591 /**\r
1592  * Get the file extension type that was selected by the user. Valid after an [OK]\r
1593  */\r
1594 Inkscape::Extension::Extension *\r
1595 FileExportDialogImpl::getSelectionType()\r
1596 {\r
1597     return extension;\r
1598 }\r
1599 \r
1600 \r
1601 /**\r
1602  * Get the file name chosen by the user.   Valid after an [OK]\r
1603  */\r
1604 Glib::ustring\r
1605 FileExportDialogImpl::getFilename()\r
1606 {\r
1607     return myFilename;\r
1608 }\r
1609 \r
1610 \r
1611 } //namespace Dialog\r
1612 } //namespace UI\r
1613 } //namespace Inkscape\r
1614 \r
1615 /*\r
1616   Local Variables:\r
1617   mode:c++\r
1618   c-file-style:"stroustrup"\r
1619   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
1620   indent-tabs-mode:nil\r
1621   fill-column:99\r
1622   End:\r
1623 */\r
1624 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r