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