e3ebffc632bc8eabc0d5b6572812493352aca8c2
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 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 gchar *getFilename();
741 Glib::SListHandle<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 gchar *
1077 FileOpenDialogImpl::getFilename (void)
1078 {
1079 return g_strdup(myFilename.c_str());
1080 }
1083 /**
1084 * To Get Multiple filenames selected at-once.
1085 */
1086 Glib::SListHandle<Glib::ustring>FileOpenDialogImpl::getFilenames()
1087 {
1088 return get_filenames();
1089 }
1096 //########################################################################
1097 //# F I L E S A V E
1098 //########################################################################
1100 class FileType
1101 {
1102 public:
1103 FileType() {}
1104 ~FileType() {}
1105 Glib::ustring name;
1106 Glib::ustring pattern;
1107 Inkscape::Extension::Extension *extension;
1108 };
1110 /**
1111 * Our implementation of the FileSaveDialog interface.
1112 */
1113 class FileSaveDialogImpl : public FileSaveDialog, public FileDialogBase
1114 {
1116 public:
1117 FileSaveDialogImpl(const Glib::ustring &dir,
1118 FileDialogType fileTypes,
1119 const Glib::ustring &title,
1120 const Glib::ustring &default_key);
1122 virtual ~FileSaveDialogImpl();
1124 bool show();
1126 Inkscape::Extension::Extension *getSelectionType();
1128 gchar *getFilename();
1131 private:
1133 /**
1134 * What type of 'open' are we? (save, export, etc)
1135 */
1136 FileDialogType dialogType;
1138 /**
1139 * Our svg preview widget
1140 */
1141 SVGPreview svgPreview;
1143 /**
1144 * Fix to allow the user to type the file name
1145 */
1146 Gtk::Entry *fileNameEntry;
1148 /**
1149 * Callback for seeing if the preview needs to be drawn
1150 */
1151 void updatePreviewCallback();
1155 /**
1156 * Allow the specification of the output file type
1157 */
1158 Gtk::HBox fileTypeBox;
1160 /**
1161 * Allow the specification of the output file type
1162 */
1163 Gtk::ComboBoxText fileTypeComboBox;
1166 /**
1167 * Data mirror of the combo box
1168 */
1169 std::vector<FileType> fileTypes;
1171 //# Child widgets
1172 Gtk::CheckButton fileTypeCheckbox;
1175 /**
1176 * Callback for user input into fileNameEntry
1177 */
1178 void fileTypeChangedCallback();
1180 /**
1181 * Create a filter menu for this type of dialog
1182 */
1183 void createFileTypeMenu();
1186 bool append_extension;
1188 /**
1189 * The extension to use to write this file
1190 */
1191 Inkscape::Extension::Extension *extension;
1193 /**
1194 * Callback for user input into fileNameEntry
1195 */
1196 void fileNameEntryChangedCallback();
1198 /**
1199 * Filename that was given
1200 */
1201 Glib::ustring myFilename;
1202 };
1209 /**
1210 * Callback for checking if the preview needs to be redrawn
1211 */
1212 void FileSaveDialogImpl::updatePreviewCallback()
1213 {
1214 Glib::ustring fileName = get_preview_filename();
1215 if (!fileName.c_str())
1216 return;
1217 bool retval = svgPreview.set(fileName, dialogType);
1218 set_preview_widget_active(retval);
1219 }
1223 /**
1224 * Callback for fileNameEntry widget
1225 */
1226 void FileSaveDialogImpl::fileNameEntryChangedCallback()
1227 {
1228 if (!fileNameEntry)
1229 return;
1231 Glib::ustring fileName = fileNameEntry->get_text();
1232 if (!Glib::get_charset()) //If we are not utf8
1233 fileName = Glib::filename_to_utf8(fileName);
1235 //g_message("User hit return. Text is '%s'\n", fileName.c_str());
1237 if (!Glib::path_is_absolute(fileName)) {
1238 //try appending to the current path
1239 // not this way: fileName = get_current_folder() + "/" + fileName;
1240 std::vector<Glib::ustring> pathSegments;
1241 pathSegments.push_back( get_current_folder() );
1242 pathSegments.push_back( fileName );
1243 fileName = Glib::build_filename(pathSegments);
1244 }
1246 //g_message("path:'%s'\n", fileName.c_str());
1248 if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
1249 set_current_folder(fileName);
1250 } else if (/*Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)*/1) {
1251 //dialog with either (1) select a regular file or (2) cd to dir
1252 //simulate an 'OK'
1253 set_filename(fileName);
1254 response(Gtk::RESPONSE_OK);
1255 }
1256 }
1260 /**
1261 * Callback for fileNameEntry widget
1262 */
1263 void FileSaveDialogImpl::fileTypeChangedCallback()
1264 {
1265 int sel = fileTypeComboBox.get_active_row_number();
1266 if (sel<0 || sel >= (int)fileTypes.size())
1267 return;
1268 FileType type = fileTypes[sel];
1269 //g_message("selected: %s\n", type.name.c_str());
1270 Gtk::FileFilter filter;
1271 filter.add_pattern(type.pattern);
1272 set_filter(filter);
1273 }
1277 void FileSaveDialogImpl::createFileTypeMenu()
1278 {
1279 Inkscape::Extension::DB::OutputList extension_list;
1280 Inkscape::Extension::db.get_output_list(extension_list);
1282 for (Inkscape::Extension::DB::OutputList::iterator current_item = extension_list.begin();
1283 current_item != extension_list.end(); current_item++)
1284 {
1285 Inkscape::Extension::Output * omod = *current_item;
1287 // FIXME: would be nice to grey them out instead of not listing them
1288 if (omod->deactivated()) continue;
1290 FileType type;
1291 type.name = (_(omod->get_filetypename()));
1292 type.pattern = "*";
1293 Glib::ustring extension = omod->get_extension();
1294 fileDialogExtensionToPattern (type.pattern, extension);
1295 type.extension= omod;
1296 fileTypeComboBox.append_text(type.name);
1297 fileTypes.push_back(type);
1298 }
1300 //#Let user choose
1301 FileType guessType;
1302 guessType.name = _("Guess from extension");
1303 guessType.pattern = "*";
1304 guessType.extension = NULL;
1305 fileTypeComboBox.append_text(guessType.name);
1306 fileTypes.push_back(guessType);
1309 fileTypeComboBox.set_active(0);
1310 fileTypeChangedCallback(); //call at least once to set the filter
1311 }
1315 /**
1316 * Constructor
1317 */
1318 FileSaveDialogImpl::FileSaveDialogImpl(const Glib::ustring &dir,
1319 FileDialogType fileTypes,
1320 const Glib::ustring &title,
1321 const Glib::ustring &default_key) :
1322 FileDialogBase(title, Gtk::FILE_CHOOSER_ACTION_SAVE)
1323 {
1324 append_extension = (bool)prefs_get_int_attribute("dialogs.save_as",
1325 "append_extension", 1);
1327 /* One file at a time */
1328 set_select_multiple(false);
1330 /* Initalize to Autodetect */
1331 extension = NULL;
1332 /* No filename to start out with */
1333 myFilename = "";
1335 /* Set our dialog type (save, export, etc...)*/
1336 dialogType = fileTypes;
1338 /* Set the pwd and/or the filename */
1339 if (dir.size() > 0)
1340 {
1341 Glib::ustring udir(dir);
1342 Glib::ustring::size_type len = udir.length();
1343 // leaving a trailing backslash on the directory name leads to the infamous
1344 // double-directory bug on win32
1345 if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
1346 set_current_folder(udir.c_str());
1347 }
1349 //###### Add the file types menu
1350 //createFilterMenu();
1352 //###### Do we want the .xxx extension automatically added?
1353 fileTypeCheckbox.set_label(Glib::ustring(_("Append filename extension automatically")));
1354 fileTypeCheckbox.set_active(append_extension);
1356 fileTypeBox.pack_start(fileTypeCheckbox);
1357 createFileTypeMenu();
1358 fileTypeComboBox.set_size_request(200,40);
1359 fileTypeComboBox.signal_changed().connect(
1360 sigc::mem_fun(*this, &FileSaveDialogImpl::fileTypeChangedCallback) );
1362 fileTypeBox.pack_start(fileTypeComboBox);
1364 set_extra_widget(fileTypeBox);
1365 //get_vbox()->pack_start(fileTypeBox, false, false, 0);
1366 //get_vbox()->reorder_child(fileTypeBox, 2);
1368 //###### Add a preview widget
1369 set_preview_widget(svgPreview);
1370 set_preview_widget_active(true);
1371 set_use_preview_label (false);
1373 //Catch selection-changed events, so we can adjust the text widget
1374 signal_update_preview().connect(
1375 sigc::mem_fun(*this, &FileSaveDialogImpl::updatePreviewCallback) );
1378 //Let's do some customization
1379 fileNameEntry = NULL;
1380 Gtk::Container *cont = get_toplevel();
1381 std::vector<Gtk::Entry *> entries;
1382 findEntryWidgets(cont, entries);
1383 //g_message("Found %d entry widgets\n", entries.size());
1384 if (entries.size() >=1 )
1385 {
1386 //Catch when user hits [return] on the text field
1387 fileNameEntry = entries[0];
1388 fileNameEntry->signal_activate().connect(
1389 sigc::mem_fun(*this, &FileSaveDialogImpl::fileNameEntryChangedCallback) );
1390 }
1392 //Let's do more customization
1393 std::vector<Gtk::Expander *> expanders;
1394 findExpanderWidgets(cont, expanders);
1395 //g_message("Found %d expander widgets\n", expanders.size());
1396 if (expanders.size() >=1 )
1397 {
1398 //Always show the file list
1399 Gtk::Expander *expander = expanders[0];
1400 expander->set_expanded(true);
1401 }
1404 //if (extension == NULL)
1405 // checkbox.set_sensitive(FALSE);
1407 add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1408 add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
1410 show_all_children();
1411 }
1415 /**
1416 * Public factory method. Used in file.cpp
1417 */
1418 FileSaveDialog *FileSaveDialog::create(const Glib::ustring &path,
1419 FileDialogType fileTypes,
1420 const Glib::ustring &title,
1421 const Glib::ustring &default_key)
1422 {
1423 FileSaveDialog *dialog = new FileSaveDialogImpl(path, fileTypes, title, default_key);
1424 return dialog;
1425 }
1431 /**
1432 * Destructor
1433 */
1434 FileSaveDialogImpl::~FileSaveDialogImpl()
1435 {
1436 }
1440 /**
1441 * Show this dialog modally. Return true if user hits [OK]
1442 */
1443 bool
1444 FileSaveDialogImpl::show()
1445 {
1446 Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());
1447 if (s.length() == 0)
1448 s = getcwd (NULL, 0);
1449 set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing
1450 set_modal (TRUE); //Window
1451 sp_transientize((GtkWidget *)gobj()); //Make transient
1452 gint b = run(); //Dialog
1453 svgPreview.showNoPreview();
1454 hide();
1456 if (b == Gtk::RESPONSE_OK)
1457 {
1458 int sel = fileTypeComboBox.get_active_row_number ();
1459 if (sel>=0 && sel< (int)fileTypes.size())
1460 {
1461 FileType &type = fileTypes[sel];
1462 extension = type.extension;
1463 }
1464 myFilename = get_filename();
1466 /*
1468 // FIXME: Why do we have more code
1470 append_extension = checkbox.get_active();
1471 prefs_set_int_attribute("dialogs.save_as", "append_extension", append_extension);
1472 prefs_set_string_attribute("dialogs.save_as", "default",
1473 ( extension != NULL ? extension->get_id() : "" ));
1474 */
1475 return TRUE;
1476 }
1477 else
1478 {
1479 return FALSE;
1480 }
1481 }
1484 /**
1485 * Get the file extension type that was selected by the user. Valid after an [OK]
1486 */
1487 Inkscape::Extension::Extension *
1488 FileSaveDialogImpl::getSelectionType()
1489 {
1490 return extension;
1491 }
1494 /**
1495 * Get the file name chosen by the user. Valid after an [OK]
1496 */
1497 gchar *
1498 FileSaveDialogImpl::getFilename()
1499 {
1500 return g_strdup(myFilename.c_str());
1501 }
1510 //########################################################################
1511 //# F I L E E X P O R T
1512 //########################################################################
1515 /**
1516 * Our implementation of the FileExportDialog interface.
1517 */
1518 class FileExportDialogImpl : public FileExportDialog, public FileDialogBase
1519 {
1521 public:
1522 FileExportDialogImpl(const Glib::ustring &dir,
1523 FileDialogType fileTypes,
1524 const Glib::ustring &title,
1525 const Glib::ustring &default_key);
1527 virtual ~FileExportDialogImpl();
1529 bool show();
1531 Inkscape::Extension::Extension *getSelectionType();
1533 gchar *getFilename();
1536 private:
1538 /**
1539 * What type of 'open' are we? (save, export, etc)
1540 */
1541 FileDialogType dialogType;
1543 /**
1544 * Our svg preview widget
1545 */
1546 SVGPreview svgPreview;
1548 /**
1549 * Fix to allow the user to type the file name
1550 */
1551 Gtk::Entry *fileNameEntry;
1553 /**
1554 * Callback for seeing if the preview needs to be drawn
1555 */
1556 void updatePreviewCallback();
1560 /**
1561 * Allow the specification of the output file type
1562 */
1563 Gtk::HBox fileTypeBox;
1565 /**
1566 * Allow the specification of the output file type
1567 */
1568 Gtk::ComboBoxText fileTypeComboBox;
1571 /**
1572 * Data mirror of the combo box
1573 */
1574 std::vector<FileType> fileTypes;
1576 //# Child widgets
1577 Gtk::CheckButton fileTypeCheckbox;
1580 /**
1581 * Callback for user input into fileNameEntry
1582 */
1583 void fileTypeChangedCallback();
1585 /**
1586 * Create a filter menu for this type of dialog
1587 */
1588 void createFileTypeMenu();
1591 bool append_extension;
1593 /**
1594 * The extension to use to write this file
1595 */
1596 Inkscape::Extension::Extension *extension;
1598 /**
1599 * Callback for user input into fileNameEntry
1600 */
1601 void fileNameEntryChangedCallback();
1603 /**
1604 * Filename that was given
1605 */
1606 Glib::ustring myFilename;
1607 };
1614 /**
1615 * Callback for checking if the preview needs to be redrawn
1616 */
1617 void FileExportDialogImpl::updatePreviewCallback()
1618 {
1619 Glib::ustring fileName = get_preview_filename();
1620 if (!fileName.c_str())
1621 return;
1622 bool retval = svgPreview.set(fileName, dialogType);
1623 set_preview_widget_active(retval);
1624 }
1628 /**
1629 * Callback for fileNameEntry widget
1630 */
1631 void FileExportDialogImpl::fileNameEntryChangedCallback()
1632 {
1633 if (!fileNameEntry)
1634 return;
1636 Glib::ustring fileName = fileNameEntry->get_text();
1637 if (!Glib::get_charset()) //If we are not utf8
1638 fileName = Glib::filename_to_utf8(fileName);
1640 //g_message("User hit return. Text is '%s'\n", fileName.c_str());
1642 if (!Glib::path_is_absolute(fileName)) {
1643 //try appending to the current path
1644 // not this way: fileName = get_current_folder() + "/" + fileName;
1645 std::vector<Glib::ustring> pathSegments;
1646 pathSegments.push_back( get_current_folder() );
1647 pathSegments.push_back( fileName );
1648 fileName = Glib::build_filename(pathSegments);
1649 }
1651 //g_message("path:'%s'\n", fileName.c_str());
1653 if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
1654 set_current_folder(fileName);
1655 } else if (/*Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)*/1) {
1656 //dialog with either (1) select a regular file or (2) cd to dir
1657 //simulate an 'OK'
1658 set_filename(fileName);
1659 response(Gtk::RESPONSE_OK);
1660 }
1661 }
1665 /**
1666 * Callback for fileNameEntry widget
1667 */
1668 void FileExportDialogImpl::fileTypeChangedCallback()
1669 {
1670 int sel = fileTypeComboBox.get_active_row_number();
1671 if (sel<0 || sel >= (int)fileTypes.size())
1672 return;
1673 FileType type = fileTypes[sel];
1674 //g_message("selected: %s\n", type.name.c_str());
1675 Gtk::FileFilter filter;
1676 filter.add_pattern(type.pattern);
1677 set_filter(filter);
1678 }
1682 void FileExportDialogImpl::createFileTypeMenu()
1683 {
1684 Inkscape::Extension::DB::OutputList extension_list;
1685 Inkscape::Extension::db.get_output_list(extension_list);
1687 for (Inkscape::Extension::DB::OutputList::iterator current_item = extension_list.begin();
1688 current_item != extension_list.end(); current_item++)
1689 {
1690 Inkscape::Extension::Output * omod = *current_item;
1692 // FIXME: would be nice to grey them out instead of not listing them
1693 if (omod->deactivated()) continue;
1695 FileType type;
1696 type.name = (_(omod->get_filetypename()));
1697 type.pattern = "*";
1698 Glib::ustring extension = omod->get_extension();
1699 fileDialogExtensionToPattern (type.pattern, extension);
1700 type.extension= omod;
1701 fileTypeComboBox.append_text(type.name);
1702 fileTypes.push_back(type);
1703 }
1705 //#Let user choose
1706 FileType guessType;
1707 guessType.name = _("Guess from extension");
1708 guessType.pattern = "*";
1709 guessType.extension = NULL;
1710 fileTypeComboBox.append_text(guessType.name);
1711 fileTypes.push_back(guessType);
1714 fileTypeComboBox.set_active(0);
1715 fileTypeChangedCallback(); //call at least once to set the filter
1716 }
1719 /**
1720 * Constructor
1721 */
1722 FileExportDialogImpl::FileExportDialogImpl(const Glib::ustring &dir,
1723 FileDialogType fileTypes,
1724 const Glib::ustring &title,
1725 const Glib::ustring &default_key) :
1726 FileDialogBase(title, Gtk::FILE_CHOOSER_ACTION_SAVE)
1727 {
1728 append_extension = (bool)prefs_get_int_attribute("dialogs.save_as", "append_extension", 1);
1730 /* One file at a time */
1731 set_select_multiple(false);
1733 /* Initalize to Autodetect */
1734 extension = NULL;
1735 /* No filename to start out with */
1736 myFilename = "";
1738 /* Set our dialog type (save, export, etc...)*/
1739 dialogType = fileTypes;
1741 /* Set the pwd and/or the filename */
1742 if (dir.size()>0)
1743 {
1744 Glib::ustring udir(dir);
1745 Glib::ustring::size_type len = udir.length();
1746 // leaving a trailing backslash on the directory name leads to the infamous
1747 // double-directory bug on win32
1748 if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1);
1749 set_current_folder(udir.c_str());
1750 }
1752 //###### Add the file types menu
1753 //createFilterMenu();
1755 //###### Do we want the .xxx extension automatically added?
1756 fileTypeCheckbox.set_label(Glib::ustring(_("Append filename extension automatically")));
1757 fileTypeCheckbox.set_active(append_extension);
1759 fileTypeBox.pack_start(fileTypeCheckbox);
1760 createFileTypeMenu();
1761 fileTypeComboBox.set_size_request(200,40);
1762 fileTypeComboBox.signal_changed().connect(
1763 sigc::mem_fun(*this, &FileExportDialogImpl::fileTypeChangedCallback) );
1765 fileTypeBox.pack_start(fileTypeComboBox);
1767 set_extra_widget(fileTypeBox);
1768 //get_vbox()->pack_start(fileTypeBox, false, false, 0);
1769 //get_vbox()->reorder_child(fileTypeBox, 2);
1771 //###### Add a preview widget
1772 set_preview_widget(svgPreview);
1773 set_preview_widget_active(true);
1774 set_use_preview_label (false);
1776 //Catch selection-changed events, so we can adjust the text widget
1777 signal_update_preview().connect(
1778 sigc::mem_fun(*this, &FileExportDialogImpl::updatePreviewCallback) );
1781 //Let's do some customization
1782 fileNameEntry = NULL;
1783 Gtk::Container *cont = get_toplevel();
1784 std::vector<Gtk::Entry *> entries;
1785 findEntryWidgets(cont, entries);
1786 //g_message("Found %d entry widgets\n", entries.size());
1787 if (entries.size() >=1 )
1788 {
1789 //Catch when user hits [return] on the text field
1790 fileNameEntry = entries[0];
1791 fileNameEntry->signal_activate().connect(
1792 sigc::mem_fun(*this, &FileExportDialogImpl::fileNameEntryChangedCallback) );
1793 }
1795 //Let's do more customization
1796 std::vector<Gtk::Expander *> expanders;
1797 findExpanderWidgets(cont, expanders);
1798 //g_message("Found %d expander widgets\n", expanders.size());
1799 if (expanders.size() >=1 )
1800 {
1801 //Always show the file list
1802 Gtk::Expander *expander = expanders[0];
1803 expander->set_expanded(true);
1804 }
1807 //if (extension == NULL)
1808 // checkbox.set_sensitive(FALSE);
1810 add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1811 add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
1813 show_all_children();
1814 }
1818 /**
1819 * Public factory method. Used in file.cpp
1820 */
1821 FileExportDialog *FileExportDialog::create(const Glib::ustring &path,
1822 FileDialogType fileTypes,
1823 const Glib::ustring &title,
1824 const Glib::ustring &default_key)
1825 {
1826 FileExportDialog *dialog = new FileExportDialogImpl(path, fileTypes, title, default_key);
1827 return dialog;
1828 }
1834 /**
1835 * Destructor
1836 */
1837 FileExportDialogImpl::~FileExportDialogImpl()
1838 {
1839 }
1843 /**
1844 * Show this dialog modally. Return true if user hits [OK]
1845 */
1846 bool
1847 FileExportDialogImpl::show()
1848 {
1849 Glib::ustring s = Glib::filename_to_utf8 (get_current_folder());
1850 if (s.length() == 0)
1851 s = getcwd (NULL, 0);
1852 set_current_folder(Glib::filename_from_utf8(s)); //hack to force initial dir listing
1853 set_modal (TRUE); //Window
1854 sp_transientize((GtkWidget *)gobj()); //Make transient
1855 gint b = run(); //Dialog
1856 svgPreview.showNoPreview();
1857 hide();
1859 if (b == Gtk::RESPONSE_OK)
1860 {
1861 int sel = fileTypeComboBox.get_active_row_number ();
1862 if (sel>=0 && sel< (int)fileTypes.size())
1863 {
1864 FileType &type = fileTypes[sel];
1865 extension = type.extension;
1866 }
1867 myFilename = get_filename();
1869 /*
1871 // FIXME: Why do we have more code
1873 append_extension = checkbox.get_active();
1874 prefs_set_int_attribute("dialogs.save_as", "append_extension", append_extension);
1875 prefs_set_string_attribute("dialogs.save_as", "default",
1876 ( extension != NULL ? extension->get_id() : "" ));
1877 */
1878 return TRUE;
1879 }
1880 else
1881 {
1882 return FALSE;
1883 }
1884 }
1887 /**
1888 * Get the file extension type that was selected by the user. Valid after an [OK]
1889 */
1890 Inkscape::Extension::Extension *
1891 FileExportDialogImpl::getSelectionType()
1892 {
1893 return extension;
1894 }
1897 /**
1898 * Get the file name chosen by the user. Valid after an [OK]
1899 */
1900 gchar *
1901 FileExportDialogImpl::getFilename()
1902 {
1903 return g_strdup(myFilename.c_str());
1904 }
1907 } //namespace Dialog
1908 } //namespace UI
1909 } //namespace Inkscape
1912 /*
1913 Local Variables:
1914 mode:c++
1915 c-file-style:"stroustrup"
1916 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1917 indent-tabs-mode:nil
1918 fill-column:99
1919 End:
1920 */
1921 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :