cb72d17f5e7382b175a2a3ded0e165b9bf45a868
1 /*
2 * Implementation of the file dialog interfaces defined in filedialog.h
3 *
4 * Authors:
5 * Bob Jamison
6 * Other dudes from The Inkscape Organization
7 *
8 * Copyright (C) 2004-2006 The Inkscape Organization
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 #endif
19 //Temporary ugly hack
20 //Remove these after the get_filter() calls in
21 //show() on both classes are fixed
22 #include <gtk/gtkfilechooser.h>
24 //Another hack
25 #include <gtk/gtkentry.h>
26 #include <gtk/gtkexpander.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <glibmm/i18n.h>
31 #include <gtkmm/box.h>
32 #include <gtkmm/filechooserdialog.h>
33 #include <gtkmm/menubar.h>
34 #include <gtkmm/menu.h>
35 #include <gtkmm/entry.h>
36 #include <gtkmm/expander.h>
37 #include <gtkmm/comboboxtext.h>
38 #include <gtkmm/stock.h>
39 #include <gdkmm/pixbuf.h>
41 #include "prefs-utils.h"
42 #include <dialogs/dialog-events.h>
43 #include <extension/input.h>
44 #include <extension/output.h>
45 #include <extension/db.h>
46 #include "inkscape.h"
47 #include "svg-view-widget.h"
48 #include "filedialog.h"
49 #include "gc-core.h"
51 #undef INK_DUMP_FILENAME_CONV
53 #ifdef INK_DUMP_FILENAME_CONV
54 void dump_str( const gchar* str, const gchar* prefix );
55 void dump_ustr( const Glib::ustring& ustr );
56 #endif
58 namespace Inkscape
59 {
60 namespace UI
61 {
62 namespace Dialog
63 {
69 //########################################################################
70 //### U T I L I T Y
71 //########################################################################
73 /**
74 \brief A quick function to turn a standard extension into a searchable
75 pattern for the file dialogs
76 \param pattern The patter that the extension should be written to
77 \param in_file_extension The C string that represents the extension
79 This function just goes through the string, and takes all characters
80 and puts a [<upper><lower>] so that both are searched and shown in
81 the file dialog. This function edits the pattern string to make
82 this happen.
83 */
84 static void
85 fileDialogExtensionToPattern(Glib::ustring &pattern,
86 Glib::ustring &extension)
87 {
88 for (unsigned int i = 0; i < extension.length(); i++ )
89 {
90 Glib::ustring::value_type ch = extension[i];
91 if ( Glib::Unicode::isalpha(ch) )
92 {
93 pattern += '[';
94 pattern += Glib::Unicode::toupper(ch);
95 pattern += Glib::Unicode::tolower(ch);
96 pattern += ']';
97 }
98 else
99 {
100 pattern += ch;
101 }
102 }
103 }
106 /**
107 * Hack: Find all entry widgets in a container
108 */
109 void findEntryWidgets(Gtk::Container *parent,
110 std::vector<Gtk::Entry *> &result)
111 {
112 if (!parent)
113 return;
114 std::vector<Gtk::Widget *> children = parent->get_children();
115 for (unsigned int i=0; i<children.size() ; i++)
116 {
117 Gtk::Widget *child = children[i];
118 GtkWidget *wid = child->gobj();
119 if (GTK_IS_ENTRY(wid))
120 result.push_back((Gtk::Entry *)child);
121 else if (GTK_IS_CONTAINER(wid))
122 findEntryWidgets((Gtk::Container *)child, result);
123 }
125 }
130 /**
131 * Hack: Find all expander widgets in a container
132 */
133 void findExpanderWidgets(Gtk::Container *parent,
134 std::vector<Gtk::Expander *> &result)
135 {
136 if (!parent)
137 return;
138 std::vector<Gtk::Widget *> children = parent->get_children();
139 for (unsigned int i=0; i<children.size() ; i++)
140 {
141 Gtk::Widget *child = children[i];
142 GtkWidget *wid = child->gobj();
143 if (GTK_IS_EXPANDER(wid))
144 result.push_back((Gtk::Expander *)child);
145 else if (GTK_IS_CONTAINER(wid))
146 findExpanderWidgets((Gtk::Container *)child, result);
147 }
149 }
152 /*#########################################################################
153 ### SVG Preview Widget
154 #########################################################################*/
155 /**
156 * Simple class for displaying an SVG file in the "preview widget."
157 * Currently, this is just a wrapper of the sp_svg_view Gtk widget.
158 * Hopefully we will eventually replace with a pure Gtkmm widget.
159 */
160 class SVGPreview : public Gtk::VBox
161 {
162 public:
164 SVGPreview();
166 ~SVGPreview();
168 bool setDocument(SPDocument *doc);
170 bool setFileName(Glib::ustring &fileName);
172 bool setFromMem(char const *xmlBuffer);
174 bool set(Glib::ustring &fileName, int dialogType);
176 bool setURI(URI &uri);
178 /**
179 * Show image embedded in SVG
180 */
181 void showImage(Glib::ustring &fileName);
183 /**
184 * Show the "No preview" image
185 */
186 void showNoPreview();
188 /**
189 * Show the "Too large" image
190 */
191 void showTooLarge(long fileLength);
193 private:
194 /**
195 * The svg document we are currently showing
196 */
197 SPDocument *document;
199 /**
200 * The sp_svg_view widget
201 */
202 GtkWidget *viewerGtk;
204 /**
205 * are we currently showing the "no preview" image?
206 */
207 bool showingNoPreview;
209 };
212 bool SVGPreview::setDocument(SPDocument *doc)
213 {
214 if (document)
215 sp_document_unref(document);
217 sp_document_ref(doc);
218 document = doc;
220 //This should remove it from the box, and free resources
221 if (viewerGtk) {
222 gtk_widget_destroy(viewerGtk);
223 }
225 viewerGtk = sp_svg_view_widget_new(doc);
226 GtkWidget *vbox = (GtkWidget *)gobj();
227 gtk_box_pack_start(GTK_BOX(vbox), viewerGtk, TRUE, TRUE, 0);
228 gtk_widget_show(viewerGtk);
230 return true;
231 }
233 bool SVGPreview::setFileName(Glib::ustring &theFileName)
234 {
235 Glib::ustring fileName = theFileName;
237 fileName = Glib::filename_to_utf8(fileName);
239 SPDocument *doc = sp_document_new (fileName.c_str(), 0);
240 if (!doc) {
241 g_warning("SVGView: error loading document '%s'\n", fileName.c_str());
242 return false;
243 }
245 setDocument(doc);
247 sp_document_unref(doc);
249 return true;
250 }
254 bool SVGPreview::setFromMem(char const *xmlBuffer)
255 {
256 if (!xmlBuffer)
257 return false;
259 gint len = (gint)strlen(xmlBuffer);
260 SPDocument *doc = sp_document_new_from_mem(xmlBuffer, len, 0);
261 if (!doc) {
262 g_warning("SVGView: error loading buffer '%s'\n",xmlBuffer);
263 return false;
264 }
266 setDocument(doc);
268 sp_document_unref(doc);
270 Inkscape::GC::request_early_collection();
272 return true;
273 }
277 void SVGPreview::showImage(Glib::ustring &theFileName)
278 {
279 Glib::ustring fileName = theFileName;
282 /*#####################################
283 # LET'S HAVE SOME FUN WITH SVG!
284 # Instead of just loading an image, why
285 # don't we make a lovely little svg and
286 # display it nicely?
287 #####################################*/
289 //Arbitrary size of svg doc -- rather 'portrait' shaped
290 gint previewWidth = 400;
291 gint previewHeight = 600;
293 //Get some image info. Smart pointer does not need to be deleted
294 Glib::RefPtr<Gdk::Pixbuf> img = Gdk::Pixbuf::create_from_file(fileName);
295 gint imgWidth = img->get_width();
296 gint imgHeight = img->get_height();
298 //Find the minimum scale to fit the image inside the preview area
299 double scaleFactorX = (0.9 *(double)previewWidth) / ((double)imgWidth);
300 double scaleFactorY = (0.9 *(double)previewHeight) / ((double)imgHeight);
301 double scaleFactor = scaleFactorX;
302 if (scaleFactorX > scaleFactorY)
303 scaleFactor = scaleFactorY;
305 //Now get the resized values
306 gint scaledImgWidth = (int) (scaleFactor * (double)imgWidth);
307 gint scaledImgHeight = (int) (scaleFactor * (double)imgHeight);
309 //center the image on the area
310 gint imgX = (previewWidth - scaledImgWidth) / 2;
311 gint imgY = (previewHeight - scaledImgHeight) / 2;
313 //wrap a rectangle around the image
314 gint rectX = imgX-1;
315 gint rectY = imgY-1;
316 gint rectWidth = scaledImgWidth +2;
317 gint rectHeight = scaledImgHeight+2;
319 //Our template. Modify to taste
320 gchar const *xformat =
321 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
322 "<svg\n"
323 "xmlns=\"http://www.w3.org/2000/svg\"\n"
324 "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
325 "width=\"%d\" height=\"%d\">\n"
326 "<rect\n"
327 " style=\"fill:#eeeeee;stroke:none\"\n"
328 " x=\"-100\" y=\"-100\" width=\"4000\" height=\"4000\"/>\n"
329 "<image x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"\n"
330 "xlink:href=\"%s\"/>\n"
331 "<rect\n"
332 " style=\"fill:none;"
333 " stroke:#000000;stroke-width:1.0;"
334 " stroke-linejoin:miter;stroke-opacity:1.0000000;"
335 " stroke-miterlimit:4.0000000;stroke-dasharray:none\"\n"
336 " x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"/>\n"
337 "<text\n"
338 " style=\"font-size:24.000000;font-style:normal;font-weight:normal;"
339 " fill:#000000;fill-opacity:1.0000000;stroke:none;"
340 " font-family:Bitstream Vera Sans\"\n"
341 " x=\"10\" y=\"26\">%d x %d</text>\n"
342 "</svg>\n\n";
344 //if (!Glib::get_charset()) //If we are not utf8
345 fileName = Glib::filename_to_utf8(fileName);
347 //Fill in the template
348 /* FIXME: Do proper XML quoting for fileName. */
349 gchar *xmlBuffer = g_strdup_printf(xformat,
350 previewWidth, previewHeight,
351 imgX, imgY, scaledImgWidth, scaledImgHeight,
352 fileName.c_str(),
353 rectX, rectY, rectWidth, rectHeight,
354 imgWidth, imgHeight);
356 //g_message("%s\n", xmlBuffer);
358 //now show it!
359 setFromMem(xmlBuffer);
360 g_free(xmlBuffer);
361 }
365 void SVGPreview::showNoPreview()
366 {
367 //Are we already showing it?
368 if (showingNoPreview)
369 return;
371 //Arbitrary size of svg doc -- rather 'portrait' shaped
372 gint previewWidth = 300;
373 gint previewHeight = 600;
375 //Our template. Modify to taste
376 gchar const *xformat =
377 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
378 "<svg\n"
379 "xmlns=\"http://www.w3.org/2000/svg\"\n"
380 "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
381 "width=\"%d\" height=\"%d\">\n"
382 "<g transform=\"translate(-190,24.27184)\" style=\"opacity:0.12\">\n"
383 "<path\n"
384 "style=\"font-size:12;fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.936193pt\"\n"
385 "d=\"M 397.64309 320.25301 L 280.39197 282.517 L 250.74227 124.83447 L 345.08225 "
386 "29.146783 L 393.59996 46.667064 L 483.89679 135.61619 L 397.64309 320.25301 z \"\n"
387 "id=\"whiteSpace\" />\n"
388 "<path\n"
389 "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
390 "d=\"M 476.95792 339.17168 C 495.78197 342.93607 499.54842 356.11361 495.78197 359.87802 "
391 "C 492.01856 363.6434 482.6065 367.40781 475.07663 361.76014 C 467.54478 "
392 "356.11361 467.54478 342.93607 476.95792 339.17168 z \"\n"
393 "id=\"droplet01\" />\n"
394 "<path\n"
395 "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
396 "d=\"M 286.46194 340.42914 C 284.6277 340.91835 269.30405 327.71337 257.16909 333.8338 "
397 "C 245.03722 339.95336 236.89276 353.65666 248.22676 359.27982 C 259.56184 364.90298 "
398 "267.66433 358.41867 277.60113 351.44119 C 287.53903 344.46477 "
399 "287.18046 343.1206 286.46194 340.42914 z \"\n"
400 "id=\"droplet02\" />\n"
401 "<path\n"
402 "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
403 "d=\"M 510.35756 306.92856 C 520.59494 304.36879 544.24333 306.92856 540.47688 321.98634 "
404 "C 536.71354 337.04806 504.71297 331.39827 484.00371 323.87156 C 482.12141 "
405 "308.81083 505.53237 308.13423 510.35756 306.92856 z \"\n"
406 "id=\"droplet03\" />\n"
407 "<path\n"
408 "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
409 "d=\"M 359.2403 21.362537 C 347.92693 21.362537 336.6347 25.683095 327.96556 34.35223 "
410 "L 173.87387 188.41466 C 165.37697 196.9114 161.1116 207.95813 160.94269 219.04577 "
411 "L 160.88418 219.04577 C 160.88418 219.08524 160.94076 219.12322 160.94269 219.16279 "
412 "C 160.94033 219.34888 160.88418 219.53256 160.88418 219.71865 L 161.14748 219.71865 "
413 "C 164.0966 230.93917 240.29699 245.24198 248.79866 253.74346 C 261.63771 266.58263 "
414 "199.5652 276.01151 212.4041 288.85074 C 225.24316 301.68979 289.99433 313.6933 302.8346 "
415 "326.53254 C 315.67368 339.37161 276.5961 353.04289 289.43532 365.88196 C 302.27439 "
416 "378.72118 345.40201 362.67257 337.5908 396.16198 C 354.92909 413.50026 391.10302 "
417 "405.2208 415.32417 387.88252 C 428.16323 375.04345 390.6948 376.17577 403.53397 "
418 "363.33668 C 416.37304 350.49745 448.78128 350.4282 476.08902 319.71589 C 465.09739 "
419 "302.62116 429.10801 295.34136 441.94719 282.50217 C 454.78625 269.66311 479.74708 "
420 "276.18423 533.60644 251.72479 C 559.89837 239.78398 557.72636 230.71459 557.62567 "
421 "219.71865 C 557.62356 219.48727 557.62567 219.27892 557.62567 219.04577 L 557.56716 "
422 "219.04577 C 557.3983 207.95812 553.10345 196.9114 544.60673 188.41466 L 390.54428 "
423 "34.35223 C 381.87515 25.683095 370.55366 21.362537 359.2403 21.362537 z M 357.92378 "
424 "41.402939 C 362.95327 41.533963 367.01541 45.368018 374.98006 50.530832 L 447.76915 "
425 "104.50827 C 448.56596 105.02498 449.32484 105.564 450.02187 106.11735 C 450.7189 106.67062 "
426 "451.3556 107.25745 451.95277 107.84347 C 452.54997 108.42842 453.09281 109.01553 453.59111 "
427 "109.62808 C 454.08837 110.24052 454.53956 110.86661 454.93688 111.50048 C 455.33532 112.13538 "
428 "455.69164 112.78029 455.9901 113.43137 C 456.28877 114.08363 456.52291 114.75639 456.7215 "
429 "115.42078 C 456.92126 116.08419 457.08982 116.73973 457.18961 117.41019 C 457.28949 "
430 "118.08184 457.33588 118.75535 457.33588 119.42886 L 414.21245 98.598549 L 409.9118 "
431 "131.16055 L 386.18512 120.04324 L 349.55654 144.50131 L 335.54288 96.1703 L 317.4919 "
432 "138.4453 L 267.08369 143.47735 L 267.63956 121.03795 C 267.63956 115.64823 296.69685 "
433 "77.915899 314.39075 68.932902 L 346.77721 45.674327 C 351.55594 42.576634 354.90608 "
434 "41.324327 357.92378 41.402939 z M 290.92738 261.61333 C 313.87149 267.56365 339.40299 "
435 "275.37038 359.88393 275.50997 L 360.76161 284.72563 C 343.2235 282.91785 306.11346 "
436 "274.45012 297.36372 269.98057 L 290.92738 261.61333 z \"\n"
437 "id=\"mountainDroplet\" />\n"
438 "</g> <g transform=\"translate(-20,0)\">\n"
439 "<text xml:space=\"preserve\"\n"
440 "style=\"font-size:32.000000;font-style:normal;font-variant:normal;font-weight:bold;"
441 "font-stretch:normal;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000pt;"
442 "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
443 "font-family:Bitstream Vera Sans;text-anchor:middle;writing-mode:lr\"\n"
444 "x=\"190\" y=\"240\">%s</text></g>\n"
445 "</svg>\n\n";
447 //Fill in the template
448 gchar *xmlBuffer = g_strdup_printf(xformat,
449 previewWidth, previewHeight, _("No preview"));
451 //g_message("%s\n", xmlBuffer);
453 //now show it!
454 setFromMem(xmlBuffer);
455 g_free(xmlBuffer);
456 showingNoPreview = true;
458 }
460 void SVGPreview::showTooLarge(long fileLength)
461 {
463 //Arbitrary size of svg doc -- rather 'portrait' shaped
464 gint previewWidth = 300;
465 gint previewHeight = 600;
467 //Our template. Modify to taste
468 gchar const *xformat =
469 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
470 "<svg\n"
471 "xmlns=\"http://www.w3.org/2000/svg\"\n"
472 "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
473 "width=\"%d\" height=\"%d\">\n"
474 "<g transform=\"translate(-170,24.27184)\" style=\"opacity:0.12\">\n"
475 "<path\n"
476 "style=\"font-size:12;fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.936193pt\"\n"
477 "d=\"M 397.64309 320.25301 L 280.39197 282.517 L 250.74227 124.83447 L 345.08225 "
478 "29.146783 L 393.59996 46.667064 L 483.89679 135.61619 L 397.64309 320.25301 z \"\n"
479 "id=\"whiteSpace\" />\n"
480 "<path\n"
481 "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
482 "d=\"M 476.95792 339.17168 C 495.78197 342.93607 499.54842 356.11361 495.78197 359.87802 "
483 "C 492.01856 363.6434 482.6065 367.40781 475.07663 361.76014 C 467.54478 "
484 "356.11361 467.54478 342.93607 476.95792 339.17168 z \"\n"
485 "id=\"droplet01\" />\n"
486 "<path\n"
487 "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
488 "d=\"M 286.46194 340.42914 C 284.6277 340.91835 269.30405 327.71337 257.16909 333.8338 "
489 "C 245.03722 339.95336 236.89276 353.65666 248.22676 359.27982 C 259.56184 364.90298 "
490 "267.66433 358.41867 277.60113 351.44119 C 287.53903 344.46477 "
491 "287.18046 343.1206 286.46194 340.42914 z \"\n"
492 "id=\"droplet02\" />\n"
493 "<path\n"
494 "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
495 "d=\"M 510.35756 306.92856 C 520.59494 304.36879 544.24333 306.92856 540.47688 321.98634 "
496 "C 536.71354 337.04806 504.71297 331.39827 484.00371 323.87156 C 482.12141 "
497 "308.81083 505.53237 308.13423 510.35756 306.92856 z \"\n"
498 "id=\"droplet03\" />\n"
499 "<path\n"
500 "style=\"font-size:12;fill-rule:evenodd;stroke-width:1pt;fill:#000000;fill-opacity:1\"\n"
501 "d=\"M 359.2403 21.362537 C 347.92693 21.362537 336.6347 25.683095 327.96556 34.35223 "
502 "L 173.87387 188.41466 C 165.37697 196.9114 161.1116 207.95813 160.94269 219.04577 "
503 "L 160.88418 219.04577 C 160.88418 219.08524 160.94076 219.12322 160.94269 219.16279 "
504 "C 160.94033 219.34888 160.88418 219.53256 160.88418 219.71865 L 161.14748 219.71865 "
505 "C 164.0966 230.93917 240.29699 245.24198 248.79866 253.74346 C 261.63771 266.58263 "
506 "199.5652 276.01151 212.4041 288.85074 C 225.24316 301.68979 289.99433 313.6933 302.8346 "
507 "326.53254 C 315.67368 339.37161 276.5961 353.04289 289.43532 365.88196 C 302.27439 "
508 "378.72118 345.40201 362.67257 337.5908 396.16198 C 354.92909 413.50026 391.10302 "
509 "405.2208 415.32417 387.88252 C 428.16323 375.04345 390.6948 376.17577 403.53397 "
510 "363.33668 C 416.37304 350.49745 448.78128 350.4282 476.08902 319.71589 C 465.09739 "
511 "302.62116 429.10801 295.34136 441.94719 282.50217 C 454.78625 269.66311 479.74708 "
512 "276.18423 533.60644 251.72479 C 559.89837 239.78398 557.72636 230.71459 557.62567 "
513 "219.71865 C 557.62356 219.48727 557.62567 219.27892 557.62567 219.04577 L 557.56716 "
514 "219.04577 C 557.3983 207.95812 553.10345 196.9114 544.60673 188.41466 L 390.54428 "
515 "34.35223 C 381.87515 25.683095 370.55366 21.362537 359.2403 21.362537 z M 357.92378 "
516 "41.402939 C 362.95327 41.533963 367.01541 45.368018 374.98006 50.530832 L 447.76915 "
517 "104.50827 C 448.56596 105.02498 449.32484 105.564 450.02187 106.11735 C 450.7189 106.67062 "
518 "451.3556 107.25745 451.95277 107.84347 C 452.54997 108.42842 453.09281 109.01553 453.59111 "
519 "109.62808 C 454.08837 110.24052 454.53956 110.86661 454.93688 111.50048 C 455.33532 112.13538 "
520 "455.69164 112.78029 455.9901 113.43137 C 456.28877 114.08363 456.52291 114.75639 456.7215 "
521 "115.42078 C 456.92126 116.08419 457.08982 116.73973 457.18961 117.41019 C 457.28949 "
522 "118.08184 457.33588 118.75535 457.33588 119.42886 L 414.21245 98.598549 L 409.9118 "
523 "131.16055 L 386.18512 120.04324 L 349.55654 144.50131 L 335.54288 96.1703 L 317.4919 "
524 "138.4453 L 267.08369 143.47735 L 267.63956 121.03795 C 267.63956 115.64823 296.69685 "
525 "77.915899 314.39075 68.932902 L 346.77721 45.674327 C 351.55594 42.576634 354.90608 "
526 "41.324327 357.92378 41.402939 z M 290.92738 261.61333 C 313.87149 267.56365 339.40299 "
527 "275.37038 359.88393 275.50997 L 360.76161 284.72563 C 343.2235 282.91785 306.11346 "
528 "274.45012 297.36372 269.98057 L 290.92738 261.61333 z \"\n"
529 "id=\"mountainDroplet\" />\n"
530 "</g>\n"
531 "<text xml:space=\"preserve\"\n"
532 "style=\"font-size:32.000000;font-style:normal;font-variant:normal;font-weight:bold;"
533 "font-stretch:normal;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000pt;"
534 "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
535 "font-family:Bitstream Vera Sans;text-anchor:middle;writing-mode:lr\"\n"
536 "x=\"170\" y=\"215\">%5.1f MB</text>\n"
537 "<text xml:space=\"preserve\"\n"
538 "style=\"font-size:24.000000;font-style:normal;font-variant:normal;font-weight:bold;"
539 "font-stretch:normal;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000pt;"
540 "stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
541 "font-family:Bitstream Vera Sans;text-anchor:middle;writing-mode:lr\"\n"
542 "x=\"180\" y=\"245\">%s</text>\n"
543 "</svg>\n\n";
545 //Fill in the template
546 double floatFileLength = ((double)fileLength) / 1048576.0;
547 //printf("%ld %f\n", fileLength, floatFileLength);
548 gchar *xmlBuffer = g_strdup_printf(xformat,
549 previewWidth, previewHeight, floatFileLength,
550 _("too large for preview"));
552 //g_message("%s\n", xmlBuffer);
554 //now show it!
555 setFromMem(xmlBuffer);
556 g_free(xmlBuffer);
558 }
560 static bool
561 hasSuffix(Glib::ustring &str, Glib::ustring &ext)
562 {
563 int strLen = str.length();
564 int extLen = ext.length();
565 if (extLen > strLen)
566 {
567 return false;
568 }
569 int strpos = strLen-1;
570 for (int extpos = extLen-1 ; extpos>=0 ; extpos--, strpos--)
571 {
572 Glib::ustring::value_type ch = str[strpos];
573 if (ch != ext[extpos])
574 {
575 if ( ((ch & 0xff80) != 0) ||
576 static_cast<Glib::ustring::value_type>( g_ascii_tolower( static_cast<gchar>(0x07f & ch) ) ) != ext[extpos] )
577 {
578 return false;
579 }
580 }
581 }
582 return true;
583 }
586 /**
587 * Return true if the image is loadable by Gdk, else false
588 */
589 static bool
590 isValidImageFile(Glib::ustring &fileName)
591 {
592 std::vector<Gdk::PixbufFormat>formats = Gdk::Pixbuf::get_formats();
593 for (unsigned int i=0; i<formats.size(); i++)
594 {
595 Gdk::PixbufFormat format = formats[i];
596 std::vector<Glib::ustring>extensions = format.get_extensions();
597 for (unsigned int j=0; j<extensions.size(); j++)
598 {
599 Glib::ustring ext = extensions[j];
600 if (hasSuffix(fileName, ext))
601 {
602 return true;
603 }
604 }
605 }
606 return false;
607 }
609 bool SVGPreview::set(Glib::ustring &fileName, int dialogType)
610 {
612 if (!Glib::file_test(fileName, Glib::FILE_TEST_EXISTS))
613 return false;
615 gchar *fName = (gchar *)fileName.c_str();
616 //g_message("fname:%s\n", fName);
619 if (Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR))
620 {
621 struct stat info;
622 if (stat(fName, &info))
623 {
624 return FALSE;
625 }
626 long fileLen = info.st_size;
627 if (fileLen > 0x150000L)
628 {
629 showingNoPreview = false;
630 showTooLarge(fileLen);
631 return FALSE;
632 }
633 }
635 Glib::ustring svg = ".svg";
636 Glib::ustring svgz = ".svgz";
638 if ((dialogType == SVG_TYPES || dialogType == IMPORT_TYPES) &&
639 (hasSuffix(fileName, svg) || hasSuffix(fileName, svgz) )
640 )
641 {
642 bool retval = setFileName(fileName);
643 showingNoPreview = false;
644 return retval;
645 }
646 else if (isValidImageFile(fileName))
647 {
648 showImage(fileName);
649 showingNoPreview = false;
650 return true;
651 }
652 else
653 {
654 showNoPreview();
655 return false;
656 }
657 }
660 SVGPreview::SVGPreview()
661 {
662 if (!INKSCAPE)
663 inkscape_application_init("",false);
664 document = NULL;
665 viewerGtk = NULL;
666 set_size_request(150,150);
667 showingNoPreview = false;
668 }
670 SVGPreview::~SVGPreview()
671 {
673 }
679 /*#########################################################################
680 ### F I L E D I A L O G B A S E C L A S S
681 #########################################################################*/
683 /**
684 * This class is the base implementation for the others. This
685 * reduces redundancies and bugs.
686 */
687 class FileDialogBase : public Gtk::FileChooserDialog
688 {
689 public:
691 /**
692 *
693 */
694 FileDialogBase(const Glib::ustring &title) :
695 Gtk::FileChooserDialog(title)
696 {
697 }
699 /**
700 *
701 */
702 FileDialogBase(const Glib::ustring &title,
703 Gtk::FileChooserAction dialogType) :
704 Gtk::FileChooserDialog(title, dialogType)
705 {
706 }
708 /**
709 *
710 */
711 virtual ~FileDialogBase()
712 {}
714 };
718 /*#########################################################################
719 ### F I L E O P E N
720 #########################################################################*/
722 /**
723 * Our implementation class for the FileOpenDialog interface..
724 */
725 class FileOpenDialogImpl : public FileOpenDialog, public FileDialogBase
726 {
727 public:
729 FileOpenDialogImpl(const Glib::ustring &dir,
730 FileDialogType fileTypes,
731 const Glib::ustring &title);
733 virtual ~FileOpenDialogImpl();
735 bool show();
737 Inkscape::Extension::Extension *getSelectionType();
739 Glib::ustring getFilename();
741 std::vector<Glib::ustring> getFilenames ();
743 protected:
747 private:
750 /**
751 * What type of 'open' are we? (open, import, place, etc)
752 */
753 FileDialogType dialogType;
755 /**
756 * Our svg preview widget
757 */
758 SVGPreview svgPreview;
760 /**
761 * Callback for seeing if the preview needs to be drawn
762 */
763 void updatePreviewCallback();
765 /**
766 * Fix to allow the user to type the file name
767 */
768 Gtk::Entry fileNameEntry;
770 /**
771 * Create a filter menu for this type of dialog
772 */
773 void createFilterMenu();
775 /**
776 * Callback for user input into fileNameEntry
777 */
778 void fileNameEntryChangedCallback();
780 /**
781 * Callback for user changing which item is selected on the list
782 */
783 void fileSelectedCallback();
786 /**
787 * Filter name->extension lookup
788 */
789 std::map<Glib::ustring, Inkscape::Extension::Extension *> extensionMap;
791 /**
792 * The extension to use to write this file
793 */
794 Inkscape::Extension::Extension *extension;
796 /**
797 * Filename that was given
798 */
799 Glib::ustring myFilename;
801 };
807 /**
808 * Callback for checking if the preview needs to be redrawn
809 */
810 void FileOpenDialogImpl::updatePreviewCallback()
811 {
812 Glib::ustring fileName = get_preview_filename();
813 if (fileName.length() < 1)
814 return;
815 svgPreview.set(fileName, dialogType);
816 }
822 /**
823 * Callback for fileNameEntry widget
824 */
825 void FileOpenDialogImpl::fileNameEntryChangedCallback()
826 {
827 Glib::ustring rawFileName = fileNameEntry.get_text();
829 Glib::ustring fileName = Glib::filename_from_utf8(rawFileName);
831 //g_message("User hit return. Text is '%s'\n", fileName.c_str());
833 if (!Glib::path_is_absolute(fileName)) {
834 //try appending to the current path
835 // not this way: fileName = get_current_folder() + "/" + fName;
836 std::vector<Glib::ustring> pathSegments;
837 pathSegments.push_back( get_current_folder() );
838 pathSegments.push_back( fileName );
839 fileName = Glib::build_filename(pathSegments);
840 }
842 //g_message("path:'%s'\n", fName.c_str());
844 if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
845 set_current_folder(fileName);
846 } else if (Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)) {
847 //dialog with either (1) select a regular file or (2) cd to dir
848 //simulate an 'OK'
849 set_filename(fileName);
850 response(Gtk::RESPONSE_OK);
851 }
852 }
858 /**
859 * Callback for fileNameEntry widget
860 */
861 void FileOpenDialogImpl::fileSelectedCallback()
862 {
863 Glib::ustring fileName = get_filename();
864 if (!Glib::get_charset()) //If we are not utf8
865 fileName = Glib::filename_to_utf8(fileName);
866 //g_message("User selected '%s'\n",
867 // filename().c_str());
869 #ifdef INK_DUMP_FILENAME_CONV
870 ::dump_ustr( get_filename() );
871 #endif
872 fileNameEntry.set_text(fileName);
873 }
878 void FileOpenDialogImpl::createFilterMenu()
879 {
880 //patterns added dynamically below
881 Gtk::FileFilter allImageFilter;
882 allImageFilter.set_name(_("All Images"));
883 extensionMap[Glib::ustring(_("All Images"))]=NULL;
884 add_filter(allImageFilter);
886 Gtk::FileFilter allFilter;
887 allFilter.set_name(_("All Files"));
888 extensionMap[Glib::ustring(_("All Files"))]=NULL;
889 allFilter.add_pattern("*");
890 add_filter(allFilter);
892 //patterns added dynamically below
893 Gtk::FileFilter allInkscapeFilter;
894 allInkscapeFilter.set_name(_("All Inkscape Files"));
895 extensionMap[Glib::ustring(_("All Inkscape Files"))]=NULL;
896 add_filter(allInkscapeFilter);
898 Inkscape::Extension::DB::InputList extension_list;
899 Inkscape::Extension::db.get_input_list(extension_list);
901 for (Inkscape::Extension::DB::InputList::iterator current_item = extension_list.begin();
902 current_item != extension_list.end(); current_item++)
903 {
904 Inkscape::Extension::Input * imod = *current_item;
906 // FIXME: would be nice to grey them out instead of not listing them
907 if (imod->deactivated()) continue;
909 Glib::ustring upattern("*");
910 Glib::ustring extension = imod->get_extension();
911 fileDialogExtensionToPattern(upattern, extension);
913 Gtk::FileFilter filter;
914 Glib::ustring uname(_(imod->get_filetypename()));
915 filter.set_name(uname);
916 filter.add_pattern(upattern);
917 add_filter(filter);
918 extensionMap[uname] = imod;
920 //g_message("ext %s:%s '%s'\n", ioext->name, ioext->mimetype, upattern.c_str());
921 allInkscapeFilter.add_pattern(upattern);
922 if ( strncmp("image", imod->get_mimetype(), 5)==0 )
923 allImageFilter.add_pattern(upattern);
924 }
926 return;
927 }
931 /**
932 * Constructor. Not called directly. Use the factory.
933 */
934 FileOpenDialogImpl::FileOpenDialogImpl(const Glib::ustring &dir,
935 FileDialogType fileTypes,
936 const Glib::ustring &title) :
937 FileDialogBase(title)
938 {
941 /* One file at a time */
942 /* And also Multiple Files */
943 set_select_multiple(true);
945 /* Initalize to Autodetect */
946 extension = NULL;
947 /* No filename to start out with */
948 myFilename = "";
950 /* Set our dialog type (open, import, etc...)*/
951 dialogType = fileTypes;
954 /* Set the pwd and/or the filename */
955 if (dir.size() > 0)
956 {
957 Glib::ustring udir(dir);
958 Glib::ustring::size_type len = udir.length();
959 // leaving a trailing backslash on the directory name leads to the infamous
960 // double-directory bug on win32
961 if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
962 set_current_folder(udir.c_str());
963 }
965 //###### Add the file types menu
966 createFilterMenu();
968 //###### Add a preview widget
969 set_preview_widget(svgPreview);
970 set_preview_widget_active(true);
971 set_use_preview_label (false);
973 //Catch selection-changed events, so we can adjust the text widget
974 signal_update_preview().connect(
975 sigc::mem_fun(*this, &FileOpenDialogImpl::updatePreviewCallback) );
978 //###### Add a text entry bar, and tie it to file chooser events
979 fileNameEntry.set_text(get_current_folder());
980 set_extra_widget(fileNameEntry);
981 fileNameEntry.grab_focus();
983 //Catch when user hits [return] on the text field
984 fileNameEntry.signal_activate().connect(
985 sigc::mem_fun(*this, &FileOpenDialogImpl::fileNameEntryChangedCallback) );
987 //Catch selection-changed events, so we can adjust the text widget
988 signal_selection_changed().connect(
989 sigc::mem_fun(*this, &FileOpenDialogImpl::fileSelectedCallback) );
991 add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
992 add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
994 }
1000 /**
1001 * Public factory. Called by file.cpp, among others.
1002 */
1003 FileOpenDialog *FileOpenDialog::create(const Glib::ustring &path,
1004 FileDialogType fileTypes,
1005 const Glib::ustring &title)
1006 {
1007 FileOpenDialog *dialog = new FileOpenDialogImpl(path, fileTypes, title);
1008 return dialog;
1009 }
1014 /**
1015 * Destructor
1016 */
1017 FileOpenDialogImpl::~FileOpenDialogImpl()
1018 {
1020 }
1023 /**
1024 * Show this dialog modally. Return true if user hits [OK]
1025 */
1026 bool
1027 FileOpenDialogImpl::show()
1028 {
1029 Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());
1030 if (s.length() == 0)
1031 s = getcwd (NULL, 0);
1032 set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing
1033 set_modal (TRUE); //Window
1034 sp_transientize((GtkWidget *)gobj()); //Make transient
1035 gint b = run(); //Dialog
1036 svgPreview.showNoPreview();
1037 hide();
1039 if (b == Gtk::RESPONSE_OK)
1040 {
1041 //This is a hack, to avoid the warning messages that
1042 //Gtk::FileChooser::get_filter() returns
1043 //should be: Gtk::FileFilter *filter = get_filter();
1044 GtkFileChooser *gtkFileChooser = Gtk::FileChooser::gobj();
1045 GtkFileFilter *filter = gtk_file_chooser_get_filter(gtkFileChooser);
1046 if (filter)
1047 {
1048 //Get which extension was chosen, if any
1049 extension = extensionMap[gtk_file_filter_get_name(filter)];
1050 }
1051 myFilename = get_filename();
1052 return TRUE;
1053 }
1054 else
1055 {
1056 return FALSE;
1057 }
1058 }
1063 /**
1064 * Get the file extension type that was selected by the user. Valid after an [OK]
1065 */
1066 Inkscape::Extension::Extension *
1067 FileOpenDialogImpl::getSelectionType()
1068 {
1069 return extension;
1070 }
1073 /**
1074 * Get the file name chosen by the user. Valid after an [OK]
1075 */
1076 Glib::ustring
1077 FileOpenDialogImpl::getFilename (void)
1078 {
1079 return g_strdup(myFilename.c_str());
1080 }
1083 /**
1084 * To Get Multiple filenames selected at-once.
1085 */
1086 std::vector<Glib::ustring>FileOpenDialogImpl::getFilenames()
1087 {
1088 std::vector<Glib::ustring> result = get_filenames();
1089 return result;
1090 }
1097 //########################################################################
1098 //# F I L E S A V E
1099 //########################################################################
1101 class FileType
1102 {
1103 public:
1104 FileType() {}
1105 ~FileType() {}
1106 Glib::ustring name;
1107 Glib::ustring pattern;
1108 Inkscape::Extension::Extension *extension;
1109 };
1111 /**
1112 * Our implementation of the FileSaveDialog interface.
1113 */
1114 class FileSaveDialogImpl : public FileSaveDialog, public FileDialogBase
1115 {
1117 public:
1118 FileSaveDialogImpl(const Glib::ustring &dir,
1119 FileDialogType fileTypes,
1120 const Glib::ustring &title,
1121 const Glib::ustring &default_key);
1123 virtual ~FileSaveDialogImpl();
1125 bool show();
1127 Inkscape::Extension::Extension *getSelectionType();
1129 Glib::ustring getFilename();
1132 private:
1134 /**
1135 * What type of 'open' are we? (save, export, etc)
1136 */
1137 FileDialogType dialogType;
1139 /**
1140 * Our svg preview widget
1141 */
1142 SVGPreview svgPreview;
1144 /**
1145 * Fix to allow the user to type the file name
1146 */
1147 Gtk::Entry *fileNameEntry;
1149 /**
1150 * Callback for seeing if the preview needs to be drawn
1151 */
1152 void updatePreviewCallback();
1156 /**
1157 * Allow the specification of the output file type
1158 */
1159 Gtk::HBox fileTypeBox;
1161 /**
1162 * Allow the specification of the output file type
1163 */
1164 Gtk::ComboBoxText fileTypeComboBox;
1167 /**
1168 * Data mirror of the combo box
1169 */
1170 std::vector<FileType> fileTypes;
1172 //# Child widgets
1173 Gtk::CheckButton fileTypeCheckbox;
1176 /**
1177 * Callback for user input into fileNameEntry
1178 */
1179 void fileTypeChangedCallback();
1181 /**
1182 * Create a filter menu for this type of dialog
1183 */
1184 void createFileTypeMenu();
1187 bool append_extension;
1189 /**
1190 * The extension to use to write this file
1191 */
1192 Inkscape::Extension::Extension *extension;
1194 /**
1195 * Callback for user input into fileNameEntry
1196 */
1197 void fileNameEntryChangedCallback();
1199 /**
1200 * Filename that was given
1201 */
1202 Glib::ustring myFilename;
1203 };
1210 /**
1211 * Callback for checking if the preview needs to be redrawn
1212 */
1213 void FileSaveDialogImpl::updatePreviewCallback()
1214 {
1215 Glib::ustring fileName = get_preview_filename();
1216 if (!fileName.c_str())
1217 return;
1218 bool retval = svgPreview.set(fileName, dialogType);
1219 set_preview_widget_active(retval);
1220 }
1224 /**
1225 * Callback for fileNameEntry widget
1226 */
1227 void FileSaveDialogImpl::fileNameEntryChangedCallback()
1228 {
1229 if (!fileNameEntry)
1230 return;
1232 Glib::ustring fileName = fileNameEntry->get_text();
1233 if (!Glib::get_charset()) //If we are not utf8
1234 fileName = Glib::filename_to_utf8(fileName);
1236 //g_message("User hit return. Text is '%s'\n", fileName.c_str());
1238 if (!Glib::path_is_absolute(fileName)) {
1239 //try appending to the current path
1240 // not this way: fileName = get_current_folder() + "/" + fileName;
1241 std::vector<Glib::ustring> pathSegments;
1242 pathSegments.push_back( get_current_folder() );
1243 pathSegments.push_back( fileName );
1244 fileName = Glib::build_filename(pathSegments);
1245 }
1247 //g_message("path:'%s'\n", fileName.c_str());
1249 if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
1250 set_current_folder(fileName);
1251 } else if (/*Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)*/1) {
1252 //dialog with either (1) select a regular file or (2) cd to dir
1253 //simulate an 'OK'
1254 set_filename(fileName);
1255 response(Gtk::RESPONSE_OK);
1256 }
1257 }
1261 /**
1262 * Callback for fileNameEntry widget
1263 */
1264 void FileSaveDialogImpl::fileTypeChangedCallback()
1265 {
1266 int sel = fileTypeComboBox.get_active_row_number();
1267 if (sel<0 || sel >= (int)fileTypes.size())
1268 return;
1269 FileType type = fileTypes[sel];
1270 //g_message("selected: %s\n", type.name.c_str());
1271 Gtk::FileFilter filter;
1272 filter.add_pattern(type.pattern);
1273 set_filter(filter);
1274 }
1278 void FileSaveDialogImpl::createFileTypeMenu()
1279 {
1280 Inkscape::Extension::DB::OutputList extension_list;
1281 Inkscape::Extension::db.get_output_list(extension_list);
1283 for (Inkscape::Extension::DB::OutputList::iterator current_item = extension_list.begin();
1284 current_item != extension_list.end(); current_item++)
1285 {
1286 Inkscape::Extension::Output * omod = *current_item;
1288 // FIXME: would be nice to grey them out instead of not listing them
1289 if (omod->deactivated()) continue;
1291 FileType type;
1292 type.name = (_(omod->get_filetypename()));
1293 type.pattern = "*";
1294 Glib::ustring extension = omod->get_extension();
1295 fileDialogExtensionToPattern (type.pattern, extension);
1296 type.extension= omod;
1297 fileTypeComboBox.append_text(type.name);
1298 fileTypes.push_back(type);
1299 }
1301 //#Let user choose
1302 FileType guessType;
1303 guessType.name = _("Guess from extension");
1304 guessType.pattern = "*";
1305 guessType.extension = NULL;
1306 fileTypeComboBox.append_text(guessType.name);
1307 fileTypes.push_back(guessType);
1310 fileTypeComboBox.set_active(0);
1311 fileTypeChangedCallback(); //call at least once to set the filter
1312 }
1316 /**
1317 * Constructor
1318 */
1319 FileSaveDialogImpl::FileSaveDialogImpl(const Glib::ustring &dir,
1320 FileDialogType fileTypes,
1321 const Glib::ustring &title,
1322 const Glib::ustring &default_key) :
1323 FileDialogBase(title, Gtk::FILE_CHOOSER_ACTION_SAVE)
1324 {
1325 append_extension = (bool)prefs_get_int_attribute("dialogs.save_as",
1326 "append_extension", 1);
1328 /* One file at a time */
1329 set_select_multiple(false);
1331 /* Initalize to Autodetect */
1332 extension = NULL;
1333 /* No filename to start out with */
1334 myFilename = "";
1336 /* Set our dialog type (save, export, etc...)*/
1337 dialogType = fileTypes;
1339 /* Set the pwd and/or the filename */
1340 if (dir.size() > 0)
1341 {
1342 Glib::ustring udir(dir);
1343 Glib::ustring::size_type len = udir.length();
1344 // leaving a trailing backslash on the directory name leads to the infamous
1345 // double-directory bug on win32
1346 if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
1347 set_current_folder(udir.c_str());
1348 }
1350 //###### Add the file types menu
1351 //createFilterMenu();
1353 //###### Do we want the .xxx extension automatically added?
1354 fileTypeCheckbox.set_label(Glib::ustring(_("Append filename extension automatically")));
1355 fileTypeCheckbox.set_active(append_extension);
1357 fileTypeBox.pack_start(fileTypeCheckbox);
1358 createFileTypeMenu();
1359 fileTypeComboBox.set_size_request(200,40);
1360 fileTypeComboBox.signal_changed().connect(
1361 sigc::mem_fun(*this, &FileSaveDialogImpl::fileTypeChangedCallback) );
1363 fileTypeBox.pack_start(fileTypeComboBox);
1365 set_extra_widget(fileTypeBox);
1366 //get_vbox()->pack_start(fileTypeBox, false, false, 0);
1367 //get_vbox()->reorder_child(fileTypeBox, 2);
1369 //###### Add a preview widget
1370 set_preview_widget(svgPreview);
1371 set_preview_widget_active(true);
1372 set_use_preview_label (false);
1374 //Catch selection-changed events, so we can adjust the text widget
1375 signal_update_preview().connect(
1376 sigc::mem_fun(*this, &FileSaveDialogImpl::updatePreviewCallback) );
1379 //Let's do some customization
1380 fileNameEntry = NULL;
1381 Gtk::Container *cont = get_toplevel();
1382 std::vector<Gtk::Entry *> entries;
1383 findEntryWidgets(cont, entries);
1384 //g_message("Found %d entry widgets\n", entries.size());
1385 if (entries.size() >=1 )
1386 {
1387 //Catch when user hits [return] on the text field
1388 fileNameEntry = entries[0];
1389 fileNameEntry->signal_activate().connect(
1390 sigc::mem_fun(*this, &FileSaveDialogImpl::fileNameEntryChangedCallback) );
1391 }
1393 //Let's do more customization
1394 std::vector<Gtk::Expander *> expanders;
1395 findExpanderWidgets(cont, expanders);
1396 //g_message("Found %d expander widgets\n", expanders.size());
1397 if (expanders.size() >=1 )
1398 {
1399 //Always show the file list
1400 Gtk::Expander *expander = expanders[0];
1401 expander->set_expanded(true);
1402 }
1405 //if (extension == NULL)
1406 // checkbox.set_sensitive(FALSE);
1408 add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1409 add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
1411 show_all_children();
1412 }
1416 /**
1417 * Public factory method. Used in file.cpp
1418 */
1419 FileSaveDialog *FileSaveDialog::create(const Glib::ustring &path,
1420 FileDialogType fileTypes,
1421 const Glib::ustring &title,
1422 const Glib::ustring &default_key)
1423 {
1424 FileSaveDialog *dialog = new FileSaveDialogImpl(path, fileTypes, title, default_key);
1425 return dialog;
1426 }
1432 /**
1433 * Destructor
1434 */
1435 FileSaveDialogImpl::~FileSaveDialogImpl()
1436 {
1437 }
1441 /**
1442 * Show this dialog modally. Return true if user hits [OK]
1443 */
1444 bool
1445 FileSaveDialogImpl::show()
1446 {
1447 Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());
1448 if (s.length() == 0)
1449 s = getcwd (NULL, 0);
1450 set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing
1451 set_modal (TRUE); //Window
1452 sp_transientize((GtkWidget *)gobj()); //Make transient
1453 gint b = run(); //Dialog
1454 svgPreview.showNoPreview();
1455 hide();
1457 if (b == Gtk::RESPONSE_OK)
1458 {
1459 int sel = fileTypeComboBox.get_active_row_number ();
1460 if (sel>=0 && sel< (int)fileTypes.size())
1461 {
1462 FileType &type = fileTypes[sel];
1463 extension = type.extension;
1464 }
1465 myFilename = get_filename();
1467 /*
1469 // FIXME: Why do we have more code
1471 append_extension = checkbox.get_active();
1472 prefs_set_int_attribute("dialogs.save_as", "append_extension", append_extension);
1473 prefs_set_string_attribute("dialogs.save_as", "default",
1474 ( extension != NULL ? extension->get_id() : "" ));
1475 */
1476 return TRUE;
1477 }
1478 else
1479 {
1480 return FALSE;
1481 }
1482 }
1485 /**
1486 * Get the file extension type that was selected by the user. Valid after an [OK]
1487 */
1488 Inkscape::Extension::Extension *
1489 FileSaveDialogImpl::getSelectionType()
1490 {
1491 return extension;
1492 }
1495 /**
1496 * Get the file name chosen by the user. Valid after an [OK]
1497 */
1498 Glib::ustring
1499 FileSaveDialogImpl::getFilename()
1500 {
1501 return myFilename;
1502 }
1511 //########################################################################
1512 //# F I L E E X P O R T
1513 //########################################################################
1516 /**
1517 * Our implementation of the FileExportDialog interface.
1518 */
1519 class FileExportDialogImpl : public FileExportDialog, public FileDialogBase
1520 {
1522 public:
1523 FileExportDialogImpl(const Glib::ustring &dir,
1524 FileDialogType fileTypes,
1525 const Glib::ustring &title,
1526 const Glib::ustring &default_key);
1528 virtual ~FileExportDialogImpl();
1530 bool show();
1532 Inkscape::Extension::Extension *getSelectionType();
1534 Glib::ustring getFilename();
1537 private:
1539 /**
1540 * What type of 'open' are we? (save, export, etc)
1541 */
1542 FileDialogType dialogType;
1544 /**
1545 * Our svg preview widget
1546 */
1547 SVGPreview svgPreview;
1549 /**
1550 * Fix to allow the user to type the file name
1551 */
1552 Gtk::Entry *fileNameEntry;
1554 /**
1555 * Callback for seeing if the preview needs to be drawn
1556 */
1557 void updatePreviewCallback();
1561 /**
1562 * Allow the specification of the output file type
1563 */
1564 Gtk::HBox fileTypeBox;
1566 /**
1567 * Allow the specification of the output file type
1568 */
1569 Gtk::ComboBoxText fileTypeComboBox;
1572 /**
1573 * Data mirror of the combo box
1574 */
1575 std::vector<FileType> fileTypes;
1577 //# Child widgets
1578 Gtk::CheckButton fileTypeCheckbox;
1581 /**
1582 * Callback for user input into fileNameEntry
1583 */
1584 void fileTypeChangedCallback();
1586 /**
1587 * Create a filter menu for this type of dialog
1588 */
1589 void createFileTypeMenu();
1592 bool append_extension;
1594 /**
1595 * The extension to use to write this file
1596 */
1597 Inkscape::Extension::Extension *extension;
1599 /**
1600 * Callback for user input into fileNameEntry
1601 */
1602 void fileNameEntryChangedCallback();
1604 /**
1605 * Filename that was given
1606 */
1607 Glib::ustring myFilename;
1608 };
1615 /**
1616 * Callback for checking if the preview needs to be redrawn
1617 */
1618 void FileExportDialogImpl::updatePreviewCallback()
1619 {
1620 Glib::ustring fileName = get_preview_filename();
1621 if (!fileName.c_str())
1622 return;
1623 bool retval = svgPreview.set(fileName, dialogType);
1624 set_preview_widget_active(retval);
1625 }
1629 /**
1630 * Callback for fileNameEntry widget
1631 */
1632 void FileExportDialogImpl::fileNameEntryChangedCallback()
1633 {
1634 if (!fileNameEntry)
1635 return;
1637 Glib::ustring fileName = fileNameEntry->get_text();
1638 if (!Glib::get_charset()) //If we are not utf8
1639 fileName = Glib::filename_to_utf8(fileName);
1641 //g_message("User hit return. Text is '%s'\n", fileName.c_str());
1643 if (!Glib::path_is_absolute(fileName)) {
1644 //try appending to the current path
1645 // not this way: fileName = get_current_folder() + "/" + fileName;
1646 std::vector<Glib::ustring> pathSegments;
1647 pathSegments.push_back( get_current_folder() );
1648 pathSegments.push_back( fileName );
1649 fileName = Glib::build_filename(pathSegments);
1650 }
1652 //g_message("path:'%s'\n", fileName.c_str());
1654 if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
1655 set_current_folder(fileName);
1656 } else if (/*Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)*/1) {
1657 //dialog with either (1) select a regular file or (2) cd to dir
1658 //simulate an 'OK'
1659 set_filename(fileName);
1660 response(Gtk::RESPONSE_OK);
1661 }
1662 }
1666 /**
1667 * Callback for fileNameEntry widget
1668 */
1669 void FileExportDialogImpl::fileTypeChangedCallback()
1670 {
1671 int sel = fileTypeComboBox.get_active_row_number();
1672 if (sel<0 || sel >= (int)fileTypes.size())
1673 return;
1674 FileType type = fileTypes[sel];
1675 //g_message("selected: %s\n", type.name.c_str());
1676 Gtk::FileFilter filter;
1677 filter.add_pattern(type.pattern);
1678 set_filter(filter);
1679 }
1683 void FileExportDialogImpl::createFileTypeMenu()
1684 {
1685 Inkscape::Extension::DB::OutputList extension_list;
1686 Inkscape::Extension::db.get_output_list(extension_list);
1688 for (Inkscape::Extension::DB::OutputList::iterator current_item = extension_list.begin();
1689 current_item != extension_list.end(); current_item++)
1690 {
1691 Inkscape::Extension::Output * omod = *current_item;
1693 // FIXME: would be nice to grey them out instead of not listing them
1694 if (omod->deactivated()) continue;
1696 FileType type;
1697 type.name = (_(omod->get_filetypename()));
1698 type.pattern = "*";
1699 Glib::ustring extension = omod->get_extension();
1700 fileDialogExtensionToPattern (type.pattern, extension);
1701 type.extension= omod;
1702 fileTypeComboBox.append_text(type.name);
1703 fileTypes.push_back(type);
1704 }
1706 //#Let user choose
1707 FileType guessType;
1708 guessType.name = _("Guess from extension");
1709 guessType.pattern = "*";
1710 guessType.extension = NULL;
1711 fileTypeComboBox.append_text(guessType.name);
1712 fileTypes.push_back(guessType);
1715 fileTypeComboBox.set_active(0);
1716 fileTypeChangedCallback(); //call at least once to set the filter
1717 }
1720 /**
1721 * Constructor
1722 */
1723 FileExportDialogImpl::FileExportDialogImpl(const Glib::ustring &dir,
1724 FileDialogType fileTypes,
1725 const Glib::ustring &title,
1726 const Glib::ustring &default_key) :
1727 FileDialogBase(title, Gtk::FILE_CHOOSER_ACTION_SAVE)
1728 {
1729 append_extension = (bool)prefs_get_int_attribute("dialogs.save_as", "append_extension", 1);
1731 /* One file at a time */
1732 set_select_multiple(false);
1734 /* Initalize to Autodetect */
1735 extension = NULL;
1736 /* No filename to start out with */
1737 myFilename = "";
1739 /* Set our dialog type (save, export, etc...)*/
1740 dialogType = fileTypes;
1742 /* Set the pwd and/or the filename */
1743 if (dir.size()>0)
1744 {
1745 Glib::ustring udir(dir);
1746 Glib::ustring::size_type len = udir.length();
1747 // leaving a trailing backslash on the directory name leads to the infamous
1748 // double-directory bug on win32
1749 if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
1750 set_current_folder(udir.c_str());
1751 }
1753 //###### Add the file types menu
1754 //createFilterMenu();
1756 //###### Do we want the .xxx extension automatically added?
1757 fileTypeCheckbox.set_label(Glib::ustring(_("Append filename extension automatically")));
1758 fileTypeCheckbox.set_active(append_extension);
1760 fileTypeBox.pack_start(fileTypeCheckbox);
1761 createFileTypeMenu();
1762 fileTypeComboBox.set_size_request(200,40);
1763 fileTypeComboBox.signal_changed().connect(
1764 sigc::mem_fun(*this, &FileExportDialogImpl::fileTypeChangedCallback) );
1766 fileTypeBox.pack_start(fileTypeComboBox);
1768 set_extra_widget(fileTypeBox);
1769 //get_vbox()->pack_start(fileTypeBox, false, false, 0);
1770 //get_vbox()->reorder_child(fileTypeBox, 2);
1772 //###### Add a preview widget
1773 set_preview_widget(svgPreview);
1774 set_preview_widget_active(true);
1775 set_use_preview_label (false);
1777 //Catch selection-changed events, so we can adjust the text widget
1778 signal_update_preview().connect(
1779 sigc::mem_fun(*this, &FileExportDialogImpl::updatePreviewCallback) );
1782 //Let's do some customization
1783 fileNameEntry = NULL;
1784 Gtk::Container *cont = get_toplevel();
1785 std::vector<Gtk::Entry *> entries;
1786 findEntryWidgets(cont, entries);
1787 //g_message("Found %d entry widgets\n", entries.size());
1788 if (entries.size() >=1 )
1789 {
1790 //Catch when user hits [return] on the text field
1791 fileNameEntry = entries[0];
1792 fileNameEntry->signal_activate().connect(
1793 sigc::mem_fun(*this, &FileExportDialogImpl::fileNameEntryChangedCallback) );
1794 }
1796 //Let's do more customization
1797 std::vector<Gtk::Expander *> expanders;
1798 findExpanderWidgets(cont, expanders);
1799 //g_message("Found %d expander widgets\n", expanders.size());
1800 if (expanders.size() >=1 )
1801 {
1802 //Always show the file list
1803 Gtk::Expander *expander = expanders[0];
1804 expander->set_expanded(true);
1805 }
1808 //if (extension == NULL)
1809 // checkbox.set_sensitive(FALSE);
1811 add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1812 add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
1814 show_all_children();
1815 }
1819 /**
1820 * Public factory method. Used in file.cpp
1821 */
1822 FileExportDialog *FileExportDialog::create(const Glib::ustring &path,
1823 FileDialogType fileTypes,
1824 const Glib::ustring &title,
1825 const Glib::ustring &default_key)
1826 {
1827 FileExportDialog *dialog = new FileExportDialogImpl(path, fileTypes, title, default_key);
1828 return dialog;
1829 }
1835 /**
1836 * Destructor
1837 */
1838 FileExportDialogImpl::~FileExportDialogImpl()
1839 {
1840 }
1844 /**
1845 * Show this dialog modally. Return true if user hits [OK]
1846 */
1847 bool
1848 FileExportDialogImpl::show()
1849 {
1850 Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());
1851 if (s.length() == 0)
1852 s = getcwd (NULL, 0);
1853 set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing
1854 set_modal (TRUE); //Window
1855 sp_transientize((GtkWidget *)gobj()); //Make transient
1856 gint b = run(); //Dialog
1857 svgPreview.showNoPreview();
1858 hide();
1860 if (b == Gtk::RESPONSE_OK)
1861 {
1862 int sel = fileTypeComboBox.get_active_row_number ();
1863 if (sel>=0 && sel< (int)fileTypes.size())
1864 {
1865 FileType &type = fileTypes[sel];
1866 extension = type.extension;
1867 }
1868 myFilename = get_filename();
1870 /*
1872 // FIXME: Why do we have more code
1874 append_extension = checkbox.get_active();
1875 prefs_set_int_attribute("dialogs.save_as", "append_extension", append_extension);
1876 prefs_set_string_attribute("dialogs.save_as", "default",
1877 ( extension != NULL ? extension->get_id() : "" ));
1878 */
1879 return TRUE;
1880 }
1881 else
1882 {
1883 return FALSE;
1884 }
1885 }
1888 /**
1889 * Get the file extension type that was selected by the user. Valid after an [OK]
1890 */
1891 Inkscape::Extension::Extension *
1892 FileExportDialogImpl::getSelectionType()
1893 {
1894 return extension;
1895 }
1898 /**
1899 * Get the file name chosen by the user. Valid after an [OK]
1900 */
1901 Glib::ustring
1902 FileExportDialogImpl::getFilename()
1903 {
1904 return myFilename;
1905 }
1910 } //namespace Dialog
1911 } //namespace UI
1912 } //namespace Inkscape
1915 /*
1916 Local Variables:
1917 mode:c++
1918 c-file-style:"stroustrup"
1919 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1920 indent-tabs-mode:nil
1921 fill-column:99
1922 End:
1923 */
1924 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :