Merge branch 'master' of git.ardour.org:ardour/ardour
[ardour.git] / gtk2_ardour / video_copy_dialog.cc
1 /*
2     Copyright (C) 2010 Paul Davis
3     Author: Robin Gareus <robin@gareus.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20 #ifdef WITH_VIDEOTIMELINE
21
22 #include <cstdio>
23 #include <string>
24 #include <sstream>
25 #include <iomanip>
26
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31
32 #include <sigc++/bind.h>
33 #include <libgen.h>
34
35 #include "pbd/error.h"
36 #include "pbd/convert.h"
37 #include "gtkmm2ext/utils.h"
38 #include "ardour/session_directory.h"
39 #include "ardour/profile.h"
40 #include "ardour/template_utils.h"
41 #include "ardour/session.h"
42 #include "ardour_ui.h"
43 #include "gui_thread.h"
44
45 #include "utils_videotl.h"
46 #include "utils.h"
47 #include "opts.h"
48 #include "video_copy_dialog.h"
49 #include "i18n.h"
50
51 using namespace Gtk;
52 using namespace std;
53 using namespace PBD;
54 using namespace ARDOUR;
55
56 VideoCopyDialog::VideoCopyDialog (Session* s, std::string infile)
57         : ArdourDialog (_("Import Video File "))
58         , infn (infile)
59         , path_label (_("Output File:"), Gtk::ALIGN_LEFT)
60         , browse_button (_("Browse"))
61         , copy_button (_("Copy/Embed"))
62         , abort_button (_("Abort"))
63         , progress_label ()
64 {
65         set_session (s);
66         autostart = false;
67
68         set_name ("VideoCopyDialog");
69         set_position (Gtk::WIN_POS_MOUSE);
70         set_modal (true);
71         set_skip_taskbar_hint (true);
72         set_resizable (false);
73         p_connection = sigc::connection();
74
75         std::string dstdir = video_dest_dir(_session->session_directory().video_path(), Config->get_video_server_docroot());
76         std::string dstfn  = dstdir + G_DIR_SEPARATOR + Glib::path_get_basename(infile);
77         path_entry.set_text (dstfn);
78
79         path_hbox = manage (new HBox);
80         path_hbox->pack_start (path_label, false, false, 3);
81         path_hbox->pack_start (path_entry, true, true, 3);
82         path_hbox->pack_start (browse_button, false, false, 3);
83         browse_button.set_name ("PaddedButton");
84         path_entry.set_width_chars(38);
85
86         browse_button.signal_clicked().connect (sigc::mem_fun (*this, &VideoCopyDialog::open_browse_dialog));
87         copy_button.signal_clicked().connect (sigc::mem_fun (*this, &VideoCopyDialog::launch_copy));
88         abort_button.signal_clicked().connect (sigc::mem_fun (*this, &VideoCopyDialog::abort_clicked));
89
90         progress_box = manage (new VBox);
91         progress_box->pack_start (progress_label, false, false);
92         progress_box->pack_start (pbar, false, false);
93         progress_box->pack_start (abort_button, false, false);
94
95         get_vbox()->pack_start (*path_hbox, false, false);
96         get_vbox()->pack_start (*progress_box, false, false);
97
98
99         cancel_button = add_button (Stock::CANCEL, RESPONSE_CANCEL);
100         get_action_area()->pack_start (copy_button, false, false);
101         show_all_children ();
102         progress_box->hide();
103 }
104
105 VideoCopyDialog::~VideoCopyDialog ()
106 {
107 }
108
109 void
110 VideoCopyDialog::setup_non_interactive_copy (std::string destfn)
111 {
112         if (destfn.empty()) {
113                 std::string dstdir = video_dest_dir(_session->session_directory().video_path(), Config->get_video_server_docroot());
114                 outfn= dstdir + G_DIR_SEPARATOR + Glib::path_get_basename(infn);
115         } else {
116                 outfn=destfn;
117         }
118         autostart=true;
119 }
120
121 void
122 VideoCopyDialog::on_show ()
123 {
124         if (autostart) {
125           Glib::signal_timeout().connect_once (sigc::mem_fun(*this, &VideoCopyDialog::launch_copy), 200);
126         }
127         Dialog::on_show ();
128 }
129
130 void
131 VideoCopyDialog::abort_clicked ()
132 {
133         aborted = true;
134 }
135
136 gint
137 VideoCopyDialog::progress_timeout ()
138 {
139         if (p_tot == 0) {
140                 pbar.set_pulse_step(.5);
141                 pbar.pulse();
142                 return 1;
143         }
144         pbar.set_fraction ((double)p_cur / (double) p_tot);
145         return 1;
146 }
147
148 void*
149 video_copy_thread (void *arg)
150 {
151         VideoCopyDialog *cvd = static_cast<VideoCopyDialog*>(arg);
152         cvd->do_copy();
153         return 0;
154 }
155
156
157 void
158 VideoCopyDialog::launch_copy ()
159 {
160         if (!autostart) {
161                 outfn = path_entry.get_text();
162         }
163         if (!confirm_video_outfn(outfn)) { return; }
164         p_cur = 0; p_tot = 0;
165
166         p_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &VideoCopyDialog::progress_timeout), 80);
167
168         pbar.set_size_request(300,-1);
169         progress_box->show();
170         path_hbox->hide();
171         cancel_button->hide();
172         copy_button.hide();
173         aborted = false;
174         finished = false;
175
176         pthread_create(&thread, NULL, video_copy_thread ,this);
177         while (!finished) {
178                 if (gtk_events_pending()) {
179                         gtk_main_iteration ();
180                 } else {
181                         usleep (10000);
182                 }
183         }
184         pthread_join(thread, NULL);
185
186         p_connection.disconnect();
187
188         if (aborted) {
189                 Gtk::Dialog::response(RESPONSE_CANCEL);
190         } else {
191                 Gtk::Dialog::response(RESPONSE_ACCEPT);
192         }
193 }
194
195 void
196 VideoCopyDialog::do_copy ()
197 {
198         progress_label.set_text (_("Linking File."));
199
200         unlink (outfn.c_str());
201
202         bool try_hardlink = false; // Config->get_try_link_for_embed(); /* XXX */
203         struct stat sb;
204         if (lstat (infn.c_str(), &sb) == 0) {
205                 p_tot = sb.st_size;
206                 /* don't hardlink a symlink */
207                 if ((sb.st_mode&S_IFMT) == S_IFLNK) {
208                         try_hardlink = false;
209                         if (stat (infn.c_str(), &sb) == 0) {
210                                 p_tot = sb.st_size;
211                         }
212                 }
213         } else {
214                 /* Can not stat() input file */
215                 warning << _("Can not read input file.") << endmsg;
216                 aborted=true;
217                 finished=true;
218                 return;
219         }
220
221         if ( !try_hardlink || link(infn.c_str(), outfn.c_str()) ) {
222                 /* hard-link failed , try copy */
223                 progress_label.set_text (_("Copying File."));
224                 int infd = open (infn.c_str(), O_RDONLY);
225                 int outfd = open (outfn.c_str(), O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
226                 if (infd <0 || outfd <0) {
227                         if (infd != -1) close(infd);
228                         warning << _("Can not open files for copy.") << endmsg;
229                         aborted=true;
230                         finished=true;
231                         return;
232                 }
233                 char buffer[BUFSIZ];
234                 ssize_t nrb, ret;
235                 while ((nrb = read(infd, buffer, BUFSIZ)) > 0  && nrb != -1 ) {
236                         ret = write(outfd, buffer, nrb);
237                         if(ret != nrb || aborted) {
238                                 warning << _("File copy failed.") << endmsg;
239                                 unlink(outfn.c_str());
240                                 aborted=true;
241                                 finished=true;
242                                 return;
243                         }
244                         p_cur+=ret;
245                 }
246         }
247         finished=true;
248         return;
249 }
250
251 void
252 VideoCopyDialog::open_browse_dialog ()
253 {
254         Gtk::FileChooserDialog dialog(_("Video File Copy Destination"), Gtk::FILE_CHOOSER_ACTION_SAVE);
255         dialog.set_filename (path_entry.get_text());
256
257         dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
258         dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
259
260         int result = dialog.run();
261
262         if (result == Gtk::RESPONSE_OK) {
263                 std::string filename = dialog.get_filename();
264
265                 if (filename.length()) {
266                         path_entry.set_text (filename);
267                 }
268         }
269 }
270 #endif /* WITH_VIDEOTIMELINE */