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