1/*
2* Copyright (c) 2018 (https://github.com/phase1geo/Minder)
3*
4* This program is free software; you can redistribute it and/or
5* modify it under the terms of the GNU General Public
6* License as published by the Free Software Foundation; either
7* version 2 of the License, or (at your option) any later version.
8*
9* This program is distributed in the hope that it will be useful,
10* but WITHOUT ANY WARRANTY; without even the implied warranty of
11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12* General Public License for more details.
13*
14* You should have received a copy of the GNU General Public
15* License along with this program; if not, write to the
16* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17* Boston, MA 02110-1301 USA
18*
19* Authored by: Trevor Williams <phase1geo@gmail.com>
20*/
21
22using Gtk;
23
24public class ExportMarkdown : Export {
25
26  /* Constructor */
27  public ExportMarkdown() {
28    base( "markdown", _( "Markdown" ), { ".md", ".markdown" }, true, false );
29  }
30
31  private bool handle_directory( string fname, out string mdfile, out string imgdir ) {
32    mdfile = fname;
33    imgdir = fname;
34    if( get_bool( "include-image-links" ) ) {
35      var filename = fname;
36      var dirname  = fname;
37      if( fname.has_suffix( ".md" ) ) {
38        var parts = fname.split( "." );
39        dirname = string.joinv( ".", parts[0:parts.length-1] );
40      }
41      if( DirUtils.create_with_parents( dirname, 0775 ) == 0 ) {
42        imgdir = GLib.Path.build_filename( dirname, "images" );
43        if( DirUtils.create_with_parents( imgdir, 0775 ) == 0 ) {
44          mdfile = GLib.Path.build_filename( dirname, GLib.Path.get_basename( fname ) );
45          return( true );
46        }
47      }
48      return( false );
49    }
50    return( true );
51  }
52
53  /* Exports the given drawing area to the file of the given name */
54  public override bool export( string fname, DrawArea da ) {
55    string filename, imgdir;
56    if( !handle_directory( fname, out filename, out imgdir ) ) return( false );
57    var  file     = File.new_for_path( filename );
58    bool retval   = true;
59    try {
60      var os = file.replace( null, false, FileCreateFlags.NONE );
61      export_top_nodes( os, da, imgdir );
62    } catch( Error e ) {
63      retval = false;
64    }
65    return( retval );
66  }
67
68  /* Draws each of the top-level nodes */
69  private void export_top_nodes( FileOutputStream os, DrawArea da, string imgdir ) {
70
71    try {
72
73      var nodes = da.get_nodes();
74      for( int i=0; i<nodes.length; i++ ) {
75        string title = "# " + nodes.index( i ).name.text.text + "\n\n";
76        os.write( title.data );
77        if( nodes.index( i ).note != "" ) {
78          string note = "  > " + nodes.index( i ).note.replace( "\n", "\n  > " );
79          os.write( note.data );
80        }
81        var children = nodes.index( i ).children();
82        for( int j=0; j<children.length; j++ ) {
83          export_node( os, da.image_manager, children.index( j ), imgdir );
84        }
85      }
86
87    } catch( Error e ) {
88      // Handle the error
89    }
90
91  }
92
93  /* Copies the given image file to the image directory */
94  private bool copy_file( string imgdir, string filename ) {
95    var basename = GLib.Path.get_basename( filename );
96    var lname    = GLib.Path.build_filename( imgdir, basename );
97    var rfile    = File.new_for_path( filename );
98    var lfile    = File.new_for_path( lname );
99    stdout.printf( "basename: %s, lname: %s, filename: %s\n", basename, lname, filename );
100    try {
101      rfile.copy( lfile, FileCopyFlags.OVERWRITE );
102    } catch( Error e ) {
103      stdout.printf( "message: %s\n", e.message );
104      return( false );
105    }
106    return( true );
107  }
108
109  /* Draws the given node and its children to the output stream */
110  private void export_node( FileOutputStream os, ImageManager im, Node node, string imgdir, string prefix = "  " ) {
111
112    try {
113
114      var title = prefix + "- ";
115
116      if( node.is_task() ) {
117        if( node.is_task_done() ) {
118          title += "[x] ";
119        } else {
120          title += "[ ] ";
121        }
122      }
123
124      if( (node.image != null) && get_bool( "include-image-links" ) ) {
125        var file = im.get_file( node.image.id );
126        if( copy_file( imgdir, file ) ) {
127          var basename = GLib.Path.get_basename( file );
128          title += "<img src=\"images/" + basename +
129                   "\" alt=\"image\" width=\"" + node.image.width.to_string() +
130                   "\" height=\"" + node.image.height.to_string() + "\"/>\n" + prefix + "  ";
131        }
132      }
133
134      title += node.name.text.text.replace( "\n", prefix + " " ) + "\n";
135
136      os.write( title.data );
137
138      if( node.note != "" ) {
139        string note = prefix + "  > " + node.note.replace( "\n", "\n" + prefix + "  > " ) + "\n";
140        os.write( note.data );
141      }
142
143      os.write( "\n".data );
144
145      var children = node.children();
146      for( int i=0; i<children.length; i++ ) {
147        export_node( os, im, children.index( i ), imgdir, prefix + "  " );
148      }
149
150    } catch( Error e ) {
151      // Handle error
152    }
153
154  }
155
156  /* Add the PNG settings */
157  public override void add_settings( Grid grid ) {
158
159    add_setting_bool( "include-image-links", grid, _( "Include image links" ),
160      _( "Creates a directory containing the Markdown and embedded images" ), false );
161
162  }
163
164  /* Save the settings */
165  public override void save_settings( Xml.Node* node ) {
166
167    node->set_prop( "include-image-links", get_bool( "include-image-links" ).to_string() );
168
169  }
170
171  /* Load the settings */
172  public override void load_settings( Xml.Node* node ) {
173
174    var i = node->get_prop( "include-image-links" );
175    if( i != null ) {
176      set_bool( "include-image-links", bool.parse( i ) );
177    }
178
179  }
180
181}
182