]> git.decadent.org.uk Git - videolink.git/blobdiff - videolink.cpp
Brought up to date in preparation for version 1.2.
[videolink.git] / videolink.cpp
index 74b8a21a067979a5317ba0abc9bd645558e4dc40..112125ad2b27e47c2ab8e8ef1d3e08b76fb080cc 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <stdlib.h>
 
+#include <gdk/gdkkeysyms.h>
 #include <gdkmm/pixbuf.h>
 #include <glibmm/convert.h>
 #include <glibmm/spawn.h>
@@ -120,6 +121,7 @@ namespace
            {".vob",     video_format_mpeg2_ps},
            {".mpeg",    video_format_mpeg2_ps},
            {".mpeg2",   video_format_mpeg2_ps},
+           {".mpg",     video_format_mpeg2_ps},
            {".voblist", video_format_vob_list}
        };
        for (std::size_t i = 0;
@@ -135,15 +137,89 @@ namespace
        return video_format_none;
     }
 
-    
-    class videolink_window : public Gtk::Window
+
+    class base_window : public Gtk::Window
+    {
+    public:
+       base_window(const video::frame_params & frame_params);
+
+    protected:
+       video::frame_params frame_params_;
+       browser_widget browser_widget_;
+
+    private:
+       bool on_idle();
+       virtual void do_late_initialisation() = 0;
+    };
+
+    base_window::base_window(const video::frame_params & frame_params)
+       : frame_params_(frame_params)
+    {
+       set_size_request(frame_params_.width, frame_params_.height);
+       set_resizable(false);
+
+       add(browser_widget_);
+       browser_widget_.show();
+
+       Glib::signal_idle().connect(
+           sigc::mem_fun(this, &base_window::on_idle));
+    }
+
+    bool base_window::on_idle()
+    {
+       do_late_initialisation();
+       return false; // don't call again thankyou
+    }
+
+    class preview_window : public base_window
+    {
+    public:
+       preview_window(const video::frame_params & frame_params,
+                      const std::string & main_page_uri);
+
+    private:
+       virtual void do_late_initialisation();
+       bool on_key_press(GdkEventKey *);
+
+       std::string main_page_uri_;
+    };
+
+    preview_window::preview_window(const video::frame_params & frame_params,
+                                  const std::string & main_page_uri)
+       : base_window(frame_params),
+         main_page_uri_(main_page_uri)
+    {
+       signal_key_press_event().connect(
+           sigc::mem_fun(this, &preview_window::on_key_press));
+    }
+
+    void preview_window::do_late_initialisation()
+    {
+       browser_widget_.load_uri(main_page_uri_);
+    }
+
+    bool preview_window::on_key_press(GdkEventKey * event)
+    {
+       switch (event->keyval)
+       {
+       case GDK_t: // = top menu
+           browser_widget_.load_uri(main_page_uri_);
+           return true;
+       case GDK_q: // = quit
+           Gtk::Main::quit();
+           return true;
+       default:
+           return false;
+       }
+    }
+
+    class conversion_window : public base_window
     {
     public:
-       videolink_window(
-           const video::frame_params & frame_params,
-           const std::string & main_page_uri,
-           const std::string & output_dir,
-           dvd_generator::mpeg_encoder encoder);
+       conversion_window(const video::frame_params & frame_params,
+                         const std::string & main_page_uri,
+                         const std::string & output_dir,
+                         dvd_generator::mpeg_encoder encoder);
 
        bool is_finished() const;
 
@@ -154,12 +230,15 @@ namespace
        dvd_generator::pgc_ref add_title(const std::string & uri,
                                         video_format format);
        void load_next_page();
-       bool on_idle();
+       void do_late_initialisation();
        void on_net_state_change(const char * uri, gint flags, guint status);
        bool browser_is_busy() const
            {
                return pending_window_update_ || pending_req_count_;
            }
+       // Try to do as much processing as possible.  Quit if done;
+       // report and quit if an exception occurs.
+       void try_process();
        // Do as much processing as possible.  Return a flag indicating
        // whether to call again once the browser is idle.
        bool process();
@@ -175,9 +254,7 @@ namespace
            nsPresContext * pres_context,
            nsIDOMWindow * dom_window);
 
-       video::frame_params frame_params_;
        std::string output_dir_;
-       browser_widget browser_widget_;
 
        dvd_generator generator_;
        typedef std::map<std::string, dvd_generator::pgc_ref>
@@ -187,44 +264,35 @@ namespace
        std::queue<std::string> page_queue_;
        bool pending_window_update_;
        int pending_req_count_;
-       bool have_tweaked_page_;
        std::auto_ptr<page_state> page_state_;
 
        bool finished_;
     };
 
-    videolink_window::videolink_window(
+    conversion_window::conversion_window(
        const video::frame_params & frame_params,
        const std::string & main_page_uri,
        const std::string & output_dir,
        dvd_generator::mpeg_encoder encoder)
-           : frame_params_(frame_params),
-             output_dir_(output_dir),
-             generator_(frame_params, encoder),
-             pending_window_update_(false),
-             pending_req_count_(0),
-             have_tweaked_page_(false),
-             finished_(false)
+       : base_window(frame_params),
+         output_dir_(output_dir),
+         generator_(frame_params, encoder),
+         pending_window_update_(false),
+         pending_req_count_(0),
+         finished_(false)
     {
-       set_size_request(frame_params_.width, frame_params_.height);
-       set_resizable(false);
-
-       add(browser_widget_);
-       browser_widget_.show();
-       Glib::signal_idle().connect(
-           SigC::slot(*this, &videolink_window::on_idle));
        browser_widget_.signal_net_state().connect(
-           SigC::slot(*this, &videolink_window::on_net_state_change));
+           sigc::mem_fun(this, &conversion_window::on_net_state_change));
 
        add_menu(main_page_uri);
     }
 
-    bool videolink_window::is_finished() const
+    bool conversion_window::is_finished() const
     {
        return finished_;
     }
 
-    dvd_generator::pgc_ref videolink_window::add_menu(const std::string & uri)
+    dvd_generator::pgc_ref conversion_window::add_menu(const std::string & uri)
     {
        dvd_generator::pgc_ref & pgc_ref = resource_map_[uri];
        if (pgc_ref.type == dvd_generator::unknown_pgc)
@@ -235,7 +303,7 @@ namespace
        return pgc_ref;
     }
 
-    dvd_generator::pgc_ref videolink_window::add_title(const std::string & uri,
+    dvd_generator::pgc_ref conversion_window::add_title(const std::string & uri,
                                                      video_format format)
     {
        dvd_generator::pgc_ref & pgc_ref = resource_map_[uri];
@@ -274,34 +342,30 @@ namespace
        return pgc_ref;
     }
 
-    void videolink_window::load_next_page()
+    void conversion_window::load_next_page()
     {
        assert(!page_queue_.empty());
        const std::string & uri = page_queue_.front();
-       std::cout << "loading " << uri << std::endl;
+       std::cout << "INFO: Loading <" << uri << ">" << std::endl;
 
        browser_widget_.load_uri(uri);
     }
 
-    bool videolink_window::on_idle()
+    void conversion_window::do_late_initialisation()
     {
-       if (!output_dir_.empty())
-       {
-           // Put pointer in the top-left so that no links appear in
-           // the hover state when we take a screenshot.
-           warp_pointer(get_window(),
-                        -frame_params_.width, -frame_params_.height);
-       }
+       // Put pointer in the top-left so that no links appear in
+       // the hover state when we take a screenshot.
+       warp_pointer(get_window(),
+                    -frame_params_.width, -frame_params_.height);
        
        load_next_page();
-       return false; // don't call again thankyou
     }
 
-    void videolink_window::on_net_state_change(const char * uri,
-                                          gint flags, guint status)
+    void conversion_window::on_net_state_change(const char * uri,
+                                               gint flags, guint status)
     {
 #       ifdef DEBUG_ON_NET_STATE_CHANGE
-       std::cout << "videolink_window::on_net_state_change(";
+       std::cout << "conversion_window::on_net_state_change(";
        if (uri)
            std::cout << '"' << uri << '"';
        else
@@ -355,7 +419,6 @@ namespace
            && flags & GTK_MOZ_EMBED_FLAG_START)
        {
            pending_window_update_ = true;
-           have_tweaked_page_ = false;
        }
 
        if (flags & GTK_MOZ_EMBED_FLAG_IS_WINDOW
@@ -370,37 +433,10 @@ namespace
        }
 
        if (!browser_is_busy())
-       {
-           try
-           {
-               if (!process())
-               {
-                   finished_ = true;
-                   Gtk::Main::quit();
-               }
-           }
-           catch (std::exception & e)
-           {
-               std::cerr << "Fatal error";
-               if (!page_queue_.empty())
-                   std::cerr << " while processing <" << page_queue_.front()
-                             << ">";
-               std::cerr << ": " << e.what() << "\n";
-               Gtk::Main::quit();
-           }
-           catch (Glib::Exception & e)
-           {
-               std::cerr << "Fatal error";
-               if (!page_queue_.empty())
-                   std::cerr << " while processing <" << page_queue_.front()
-                             << ">";
-               std::cerr << ": " << e.what() << "\n";
-               Gtk::Main::quit();
-           }
-       }
+           try_process();
     }
 
-    struct videolink_window::page_state
+    struct conversion_window::page_state
     {
        page_state(Glib::RefPtr<Gdk::Pixbuf> norm_pixbuf,
                   nsIDOMDocument * doc, int width, int height)
@@ -420,10 +456,55 @@ namespace
        link_iterator links_it, links_end;
 
        rectangle link_rect;
+       std::string link_target;
        bool link_changing;
     };
 
-    bool videolink_window::process()
+    void conversion_window::try_process()
+    {
+       try
+       {
+           if (!process())
+           {
+               finished_ = true;
+               Gtk::Main::quit();
+           }
+       }
+       catch (...)
+       {
+           // Print context of exception.
+           if (!page_queue_.empty())
+           {
+               std::cerr << "ERROR: While processing page <"
+                         << page_queue_.front() << ">:\n";
+               if (page_state_.get() && !page_state_->link_target.empty())
+                   std::cerr << "ERROR: While processing link to <"
+                             << page_state_->link_target << ">:\n";
+           }
+
+           // Print exception message.
+           try
+           {
+               throw;
+           }
+           catch (std::exception & e)
+           {
+               std::cerr << "ERROR: " << e.what() << "\n";
+           }
+           catch (Glib::Exception & e)
+           {
+               std::cerr << "ERROR: " << e.what() << "\n";
+           }
+           catch (...)
+           {
+               std::cerr << "ERROR: Unknown exception\n";
+           }
+
+           Gtk::Main::quit();
+       }
+    }
+
+    bool conversion_window::process()
     {
        assert(!page_queue_.empty());
 
@@ -437,71 +518,46 @@ namespace
        nsCOMPtr<nsIDOMWindow> dom_window;
        check(browser->GetContentDOMWindow(getter_AddRefs(dom_window)));
 
-       // If we haven't done so already, disable scrollbars.
-       if (!have_tweaked_page_)
+       nsCOMPtr<nsIDOMDocument> basic_doc;
+       check(dom_window->GetDocument(getter_AddRefs(basic_doc)));
+
+       // Start or continue processing links.
+       if (!page_state_.get())
+           page_state_.reset(
+               new page_state(
+                   get_screenshot(),
+                   basic_doc, frame_params_.width, frame_params_.height));
+       if (!process_links(
+               page_state_.get(),
+               basic_doc, pres_shell, pres_context, dom_window))
        {
-           // This actually only needs to be done once.
-           nsCOMPtr<nsIDOMBarProp> dom_bar_prop;
-           check(dom_window->GetScrollbars(getter_AddRefs(dom_bar_prop)));
-           check(dom_bar_prop->SetVisible(false));
-
-           have_tweaked_page_ = true;
-
-           // Might need to wait a while for things to load or more
-           // likely for a re-layout.
-           if (browser_is_busy())
-               return true;
-       }
-
-       // All further work should only be done if we're not in preview mode.
-       if (!output_dir_.empty())
-       {
-           nsCOMPtr<nsIDOMDocument> basic_doc;
-           check(dom_window->GetDocument(getter_AddRefs(basic_doc)));
-
-           // Start or continue processing links.
-           std::auto_ptr<page_state> state(page_state_);
-           if (!state.get())
-               state.reset(
-                   new page_state(
-                       get_screenshot(),
-                       basic_doc, frame_params_.width, frame_params_.height));
-           if (process_links(
-                   state.get(),
-                   basic_doc, pres_shell, pres_context, dom_window))
+           // We've finished work on the links so generate the
+           // menu VOB.
+           quantise_rgba_pixbuf(page_state_->diff_pixbuf,
+                                dvd::button_n_colours);
+           generator_.generate_menu_vob(
+               resource_map_[page_queue_.front()].index,
+               page_state_->norm_pixbuf, page_state_->diff_pixbuf);
+
+           // Move on to the next page, if any, or else generate
+           // the DVD filesystem.
+           page_state_.reset();
+           page_queue_.pop();
+           if (!page_queue_.empty())
            {
-               // Save iteration state for later.
-               page_state_ = state;
+               load_next_page();
            }
            else
            {
-               // We've finished work on the links so generate the
-               // menu VOB.
-               quantise_rgba_pixbuf(state->diff_pixbuf,
-                                    dvd::button_n_colours);
-               generator_.generate_menu_vob(
-                   resource_map_[page_queue_.front()].index,
-                   state->norm_pixbuf, state->diff_pixbuf);
-
-               // Move on to the next page, if any, or else generate
-               // the DVD filesystem.
-               page_queue_.pop();
-               if (!page_queue_.empty())
-               {
-                   load_next_page();
-               }
-               else
-               {
-                   generator_.generate(output_dir_);
-                   return false;
-               }
+               generator_.generate(output_dir_);
+               return false;
            }
        }
 
        return true;
     }
 
-    Glib::RefPtr<Gdk::Pixbuf> videolink_window::get_screenshot()
+    Glib::RefPtr<Gdk::Pixbuf> conversion_window::get_screenshot()
     {
        Glib::RefPtr<Gdk::Window> window(get_window());
        assert(window);
@@ -513,7 +569,7 @@ namespace
                                   frame_params_.width, frame_params_.height);
     }
 
-    bool videolink_window::process_links(
+    bool conversion_window::process_links(
        page_state * state,
        nsIDOMDocument * basic_doc,
        nsIPresShell * pres_shell,
@@ -553,17 +609,17 @@ namespace
            assert(link);
            nsCOMPtr<nsIURI> uri_iface;
            check(link->GetHrefURI(getter_AddRefs(uri_iface)));
-           std::string uri_and_fragment, uri, fragment;
+           std::string uri, fragment;
            {
-               nsCString uri_and_fragment_ns;
-               check(uri_iface->GetSpec(uri_and_fragment_ns));
-               uri_and_fragment.assign(uri_and_fragment_ns.BeginReading(),
-                                       uri_and_fragment_ns.EndReading());
+               nsCString link_target_ns;
+               check(uri_iface->GetSpec(link_target_ns));
+               state->link_target.assign(link_target_ns.BeginReading(),
+                                         link_target_ns.EndReading());
 
-               std::size_t hash_pos = uri_and_fragment.find('#');
-               uri.assign(uri_and_fragment, 0, hash_pos);
+               std::size_t hash_pos = state->link_target.find('#');
+               uri.assign(state->link_target, 0, hash_pos);
                if (hash_pos != std::string::npos)
-                   fragment.assign(uri_and_fragment,
+                   fragment.assign(state->link_target,
                                    hash_pos + 1, std::string::npos);
            }
 
@@ -579,8 +635,8 @@ namespace
 
                if (state->link_rect.empty())
                {
-                   std::cerr << "Ignoring invisible link to "
-                             << uri_and_fragment << "\n";
+                   std::cerr << "WARN: Ignoring invisible link to <"
+                             << state->link_target << ">\n";
                    continue;
                }
 
@@ -594,11 +650,8 @@ namespace
                    PRBool is_file;
                    check(uri_iface->SchemeIs("file", &is_file));
                    if (!is_file)
-                   {
-                       std::cerr << "Links to video must use the file:"
-                                 << " scheme\n";
-                       continue;
-                   }
+                       throw std::runtime_error(
+                           "Link to video does not use file: scheme");
                    target = add_title(uri, format);
                    target.sub_index =
                        std::strtoul(fragment.c_str(), NULL, 10);
@@ -757,7 +810,7 @@ namespace
 
 void fatal_error(const std::string & message)
 {
-    std::cerr << "Fatal error: " << message << "\n";
+    std::cerr << "ERROR: " << message << "\n";
     Gtk::Main::quit();
 }
 
@@ -922,18 +975,26 @@ int main(int argc, char ** argv)
            null_prompt_service::install();
 
        // Run the browser/converter
-       videolink_window window(frame_params, menu_url, output_dir, encoder);
-       window.show();
-       window.signal_hide().connect(SigC::slot(&Gtk::Main::quit));
-       Gtk::Main::run();
-
-       return ((preview_mode || window.is_finished())
-               ? EXIT_SUCCESS
-               : EXIT_FAILURE);
+       if (preview_mode)
+       {
+           preview_window window(frame_params, menu_url);
+           window.show();
+           window.signal_hide().connect(sigc::ptr_fun(Gtk::Main::quit));
+           Gtk::Main::run();
+           return EXIT_SUCCESS;
+       }
+       else
+       {
+           conversion_window window(frame_params, menu_url, output_dir, encoder);
+           window.show();
+           window.signal_hide().connect(sigc::ptr_fun(Gtk::Main::quit));
+           Gtk::Main::run();
+           return window.is_finished() ? EXIT_SUCCESS  : EXIT_FAILURE;
+       }
     }
     catch (std::exception & e)
     {
-       std::cerr << "Fatal error: " << e.what() << "\n";
+       std::cerr << "ERROR: " << e.what() << "\n";
        return EXIT_FAILURE;
     }
 }