Corrected handling of fragments. Added support for linking to chapters using fragments.
authorBen Hutchings <ben@decadent.org.uk>
Sat, 24 Dec 2005 23:47:20 +0000 (23:47 +0000)
committerBen Hutchings <ben@decadent.org.uk>
Sun, 2 Nov 2008 23:39:55 +0000 (23:39 +0000)
webdvd.cpp

index e809968..e2d8cb8 100644 (file)
@@ -174,8 +174,29 @@ namespace
     
     struct dvd_contents
     {
-       enum pgc_type { menu_pgc, title_pgc };
-       typedef std::pair<pgc_type, int> pgc_ref;
+       enum pgc_type { unknown_pgc,  menu_pgc, title_pgc };
+
+       struct pgc_ref
+       {
+           explicit pgc_ref(pgc_type type = unknown_pgc,
+                            int index = -1,
+                            int sub_index = 0)
+                   : type(type), index(index), sub_index(sub_index)
+               {}
+           bool operator==(const pgc_ref & other) const
+               {
+                   return type == other.type && index == other.index;
+               }
+           bool operator!=(const pgc_ref & other) const
+               {
+                   return !(*this == other);
+               }
+           
+           pgc_type type;      // Menu or title reference?
+           unsigned index;     // Menu or title index (within resp. vector)
+           unsigned sub_index; // Button or chapter number (1-based; 0 if
+                               // unspecified; not compared!)
+       };
 
        struct menu
        {
@@ -429,6 +450,15 @@ namespace
                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();
+           }
        }
     }
 
@@ -536,7 +566,7 @@ namespace
 
        temp_file links_temp;
 
-       int link_num;
+       unsigned link_num;
        link_iterator links_it, links_end;
 
        rectangle link_rect;
@@ -587,7 +617,7 @@ namespace
            0, 0, frame_params_.width, frame_params_.height
        };
 
-       int menu_num = resource_map_[page_queue_.front()].second;
+       unsigned menu_num = resource_map_[page_queue_.front()].index;
 
        for (/* no initialisation */;
             state->links_it != state->links_end;
@@ -595,19 +625,24 @@ namespace
        {
            nsCOMPtr<nsIDOMNode> node(*state->links_it);
 
-           // Find the link URI.
+           // Find the link URI and separate any fragment from it.
            nsCOMPtr<nsILink> link(do_QueryInterface(node));
            assert(link);
-           nsCOMPtr<nsIURI> uri;
-           check(link->GetHrefURI(getter_AddRefs(uri)));
-           std::string uri_string;
+           nsCOMPtr<nsIURI> uri_iface;
+           check(link->GetHrefURI(getter_AddRefs(uri_iface)));
+           std::string uri_and_fragment, uri, fragment;
            {
-               nsCString uri_ns_string;
-               check(uri->GetSpec(uri_ns_string));
-               uri_string.assign(uri_ns_string.BeginReading(),
-                                 uri_ns_string.EndReading());
+               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());
+
+               std::size_t hash_pos = uri_and_fragment.find('#');
+               uri.assign(uri_and_fragment, 0, hash_pos);
+               if (hash_pos != std::string::npos)
+                   fragment.assign(uri_and_fragment,
+                                   hash_pos + 1, std::string::npos);
            }
-           std::string uri_sans_fragment(uri_string, 0, uri_string.find('#'));
 
            // Is this a new link?
            if (!state->link_changing)
@@ -622,18 +657,19 @@ namespace
                if (state->link_rect.empty())
                {
                    std::cerr << "Ignoring invisible link to "
-                             << uri_string << "\n";
+                             << uri_and_fragment << "\n";
                    continue;
                }
 
                ++state->link_num;
 
-               if (state->link_num >= dvd::menu_buttons_max)
+               if (state->link_num >= unsigned(dvd::menu_buttons_max))
                {
-                   if (state->link_num == dvd::menu_buttons_max)
+                   if (state->link_num == unsigned(dvd::menu_buttons_max))
                        std::cerr << "No more than " << dvd::menu_buttons_max
                                  << " buttons can be placed on a menu\n";
-                   std::cerr << "Ignoring link to " << uri_string << "\n";
+                   std::cerr << "Ignoring link to " << uri_and_fragment
+                             << "\n";
                    continue;
                }
 
@@ -646,32 +682,33 @@ namespace
                // Check whether this is a link to a video or a page then
                // add it to the known resources if not already seen; then
                // add it to the menu entries.
-               nsCString path;
-               check(uri->GetPath(path));
-               dvd_contents::pgc_ref dest_pgc;
+               dvd_contents::pgc_ref target;
                // FIXME: This is a bit of a hack.  Perhaps we could decide
                // later based on the MIME type determined by Mozilla?
-               if ((path.Length() > 4
-                    && std::strcmp(path.EndReading() - 4, ".vob") == 0)
-                   || (path.Length() > 8
-                       && std::strcmp(path.EndReading() - 8, ".voblist")
-                          == 0))
+               if ((uri.size() > 4
+                    && uri.compare(uri.size() - 4, 4, ".vob") == 0)
+                   || (uri.size() > 8
+                       && uri.compare(uri.size() - 8, 8, ".voblist") == 0))
                {
                    PRBool is_file;
-                   check(uri->SchemeIs("file", &is_file));
+                   check(uri_iface->SchemeIs("file", &is_file));
                    if (!is_file)
                    {
                        std::cerr << "Links to video must use the file:"
                                  << " scheme\n";
                        continue;
                    }
-                   dest_pgc = add_title(uri_sans_fragment);
+                   target = add_title(uri);
+                   target.sub_index =
+                       std::strtoul(fragment.c_str(), NULL, 10);
                }
                else
                {
-                   dest_pgc = add_menu(uri_sans_fragment);
+                   target = add_menu(uri);
+                   // TODO: If there's a fragment, work out which button
+                   // is closest and set target.sub_index.
                }
-               contents_.menus[menu_num].entries.push_back(dest_pgc);
+               contents_.menus[menu_num].entries.push_back(target);
 
                nsCOMPtr<nsIContent> content(do_QueryInterface(node));
                assert(content);
@@ -828,7 +865,7 @@ namespace
            "  <vmgm>\n"
            "    <menus>\n";
            
-       for (std::size_t menu_num = 0;
+       for (unsigned menu_num = 0;
             menu_num != contents.menus.size();
             ++menu_num)
        {
@@ -895,44 +932,57 @@ namespace
                "        </pre>\n"
                "        <vob file='" << menu.vob_temp->get_name() << "'/>\n";
 
-           for (std::size_t button_num = 0;
+           for (unsigned button_num = 0;
                 button_num != menu.entries.size();
                 ++button_num)
            {
+               const dvd_contents::pgc_ref & target =
+                   menu.entries[button_num];
+
                file << "        <button> ";
 
-               if (menu.entries[button_num].first == dvd_contents::menu_pgc)
+               if (target.type == dvd_contents::menu_pgc)
                {
-                   int dest_menu_num = menu.entries[button_num].second;
-
-                   // Look for a button on the new menu that links
-                   // back to this one.  If there is one, set that to
-                   // be the highlighted button; otherwise, use the
-                   // first button.
-                   const std::vector<dvd_contents::pgc_ref> &
-                       dest_menu_entries =
-                       contents.menus[dest_menu_num].entries;
-                   dvd_contents::pgc_ref this_pgc(
-                       dvd_contents::menu_pgc, menu_num);
-                   std::size_t dest_button_num = dest_menu_entries.size();
-                   while (dest_button_num != 0
-                          && dest_menu_entries[--dest_button_num] != this_pgc)
-                       ;
+                   unsigned target_button_num;
+
+                   if (target.sub_index)
+                   {
+                       target_button_num = target.sub_index;
+                   }
+                   else
+                   {
+                       // Look for a button on the new menu that links
+                       // back to this one.  If there is one, set that to
+                       // be the highlighted button; otherwise, use the
+                       // first button.
+                       const std::vector<dvd_contents::pgc_ref> &
+                           target_menu_entries =
+                           contents.menus[target.index].entries;
+                       dvd_contents::pgc_ref this_pgc(dvd_contents::menu_pgc,
+                                                      menu_num);
+                       target_button_num = target_menu_entries.size();
+                       while (target_button_num != 0
+                              && (target_menu_entries[--target_button_num]
+                                  != this_pgc))
+                           ;
+                   }
                         
                    file << "g1 = "
-                        << (1 + dest_menu_num
-                            + (1 + dest_button_num) * button_mult)
-                        << "; jump menu " << 1 + dest_menu_num << ";";
+                        << (1 + target.index
+                            + (1 + target_button_num) * button_mult)
+                        << "; jump menu " << 1 + target.index << ";";
                }
                else
                {
-                   assert(menu.entries[button_num].first
-                          == dvd_contents::title_pgc);
+                   assert(target.type == dvd_contents::title_pgc);
 
                    file << "g1 = "
                         << 1 + menu_num + (1 + button_num) * button_mult
                         << "; jump title "
-                        << 1 + menu.entries[button_num].second << ";";
+                        << 1 + target.index;
+                   if (target.sub_index)
+                       file << " chapter " << target.sub_index;
+                   file << ";";
                }
 
                file <<  " </button>\n";
@@ -948,7 +998,7 @@ namespace
        // Generate a titleset for each title.  This appears to make
        // jumping to titles a whole lot simpler (but limits us to 99
        // titles).
-       for (std::size_t title_num = 0;
+       for (unsigned title_num = 0;
             title_num != contents.titles.size();
             ++title_num)
        {