Changeset 44f4113f1630461cc00e8ad6aae3d5720fa585e5

Show
Ignore:
Timestamp:
10/25/09 14:24:51 (4 years ago)
Author:
Benjamin Herr <ben@…>
git-author:
Benjamin Herr <ben@0x539.de> / 2009-01-16T14:50:17Z+0100
Parents:
d451ded122128ae7642f03735ebc062afc1b7e9d
Children:
80d0eeda014ec64671fb9da68e646348a9165ded
git-committer:
Benjamin Herr <ben@0x539.de> / 2009-10-25T14:24:51Z+0100
Message:

Implemented basic HTML export

Location:
code
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • code/commands/file-commands.cpp

    r3da60b7 r44f4113  
    11/* Gobby - GTK-based collaborative text editor 
    2  * Copyright (C) 2008, 2009 Armin Burgmeier <armin@arbur.net> 
     2 * Copyright (C) 2008 Armin Burgmeier <armin@arbur.net> 
    33 * 
    44 * This program is free software; you can redistribute it and/or 
     
    116116        header.action_file_save_all->signal_activate().connect( 
    117117                sigc::mem_fun(*this, &FileCommands::on_save_all)); 
     118        header.action_file_export_html->signal_activate().connect( 
     119                sigc::mem_fun(*this, &FileCommands::on_export_html)); 
    118120        header.action_file_close->signal_activate().connect( 
    119121                sigc::mem_fun(*this, &FileCommands::on_close)); 
     
    220222} 
    221223 
     224#include <ctime> 
     225 
     226#include <gtkmm/textbuffer.h> 
     227#include <libxml++/libxml++.h> 
     228 
     229#include <libinftextgtk/inf-text-gtk-buffer.h> 
     230 
     231// write the Gtk::TextBuffer from document into content, inserting <span/>s for 
     232// line breaks and authorship of chunks of text, also save all users 
     233// encountered and the total number of lines dumped 
     234void dump_buffer(DocWindow& document, 
     235                 xmlpp::Element* content, 
     236                 std::set<InfTextUser*>& users, 
     237                 unsigned int& line_counter) { 
     238  users.clear(); 
     239  line_counter = 0; 
     240        xmlpp::Element* last_node = content; 
     241        last_node->add_child("div")->set_attribute("class", "line_no"); 
     242 
     243        //Glib::RefPtr<Gtk::TextBuffer> buffer( 
     244        //      Glib::wrap( 
     245        GtkTextBuffer* buffer = GTK_TEXT_BUFFER(document.get_text_buffer()); 
     246        InfTextGtkBuffer* inf_buffer 
     247                = INF_TEXT_GTK_BUFFER( 
     248                                inf_session_get_buffer(INF_SESSION(document.get_session()))); 
     249 
     250        guint user_id; 
     251        bool user_active = false; 
     252 
     253        GtkTextIter begin; 
     254        gtk_text_buffer_get_start_iter(buffer, &begin); 
     255         
     256        if (gtk_text_iter_is_end(&begin)) 
     257                return; 
     258 
     259        // TODO: I just made this use the Gtk+ API instead of Gtkmm at like 2 am, 
     260        //       surely there are a bunch of segfaults/memleaks lurking 
     261        for (;;) { 
     262                GtkTextIter next = begin; 
     263                gtk_text_iter_forward_to_tag_toggle(&next, 0); 
     264 
     265                // split text by newlines so we can insert line number elements 
     266                gchar* text = gtk_text_iter_get_text(&begin, &next); 
     267                try{ 
     268                        gchar* last_pos = text; 
     269                        for (gchar* i = text; *i; ++i) { 
     270                                if (*i != '\n') 
     271                                        continue; 
     272 
     273                                ++line_counter; 
     274 
     275                                gchar* next_pos = i; 
     276                                ++next_pos; 
     277                                last_node->add_child_text(Glib::ustring(last_pos, next_pos - last_pos)); 
     278                                last_pos = next_pos; 
     279 
     280                                // drop author <span/> for a moment for the line number <span/> 
     281                                if (user_active) 
     282                                        last_node = last_node->get_parent(); 
     283                                last_node 
     284                                  ->add_child("div") 
     285                                  ->set_attribute("class", "line_no"); 
     286                                if (user_active) { 
     287                                        last_node = last_node->add_child("span"); 
     288                                        last_node->set_attribute( 
     289                                                "class", 
     290                                                Glib::ustring::compose("user_%1", user_id)); 
     291                                } 
     292                        } 
     293                        last_node->add_child_text(Glib::ustring(last_pos)); 
     294                } catch (...) { 
     295                        g_free(text); 
     296                        throw; 
     297                } 
     298                g_free(text); 
     299 
     300                begin = next; 
     301 
     302                if (gtk_text_iter_is_end(&begin)) 
     303                        break; 
     304 
     305                // check author, insert new <span/> if necessary 
     306                InfTextUser* user 
     307                        = inf_text_gtk_buffer_get_author(inf_buffer, &next); 
     308 
     309                if (user) { 
     310                        guint new_id = inf_user_get_id(INF_USER(user)); 
     311                        if (!user_active || new_id != user_id) { 
     312                                if (user_active) 
     313                                        last_node = last_node->get_parent(); 
     314 
     315                                last_node = last_node->add_child("span"); 
     316                                last_node->set_attribute( 
     317                                        "class", 
     318                                        Glib::ustring::compose("user_%1", new_id)); 
     319                        } 
     320 
     321                        user_id = new_id; 
     322                        user_active = true; 
     323                        users.insert(user); 
     324                } else { 
     325                        if (user_active) 
     326                                last_node = last_node->get_parent(); 
     327                        user_active = false; 
     328                } 
     329        } 
     330} 
     331 
     332// some random interesting information/advertisement to be put at the end of 
     333// the html output 
     334void dump_info(xmlpp::Element* node, DocWindow& document) { 
     335        // put current time 
     336  char const* time_str; 
     337        int const n = 128; 
     338        char buf[n]; 
     339  { 
     340    std::time_t now; 
     341          std::time(&now); 
     342          // TODO: localtime is not threadsafe, use boost.date_time!! 
     343                if (std::strftime(buf, n, "%c", localtime(&now))) 
     344                        time_str = buf; 
     345                else 
     346                        time_str = _("<unable to print date>"); 
     347        } 
     348 
     349        // put document metadata, like path, hostname of infinoted 
     350        // TODO: figure out what interesting info we can pull out of the session 
     351        char session_info[] = "<placeholder for session info and path>"; 
     352 
     353        // TODO: do some more magic to make localising this easier 
     354        node->add_child_text(_("Document generated by ")); 
     355        xmlpp::Element* link = node->add_child("a"); 
     356        link->set_attribute("href", "http://gobby.0x539.de/"); 
     357        link->add_child_text(PACKAGE_STRING); 
     358        node->add_child_text( 
     359                Glib::ustring::compose(_(" %1 from %2 at %3"), 
     360                PACKAGE_STRING, session_info, time_str)); 
     361} 
     362 
     363// put author colours into the css, list each author before the actual text 
     364void add_user_colours(xmlpp::Element* css, 
     365                      xmlpp::Element* list, 
     366                      const std::set<InfTextUser*>& users) { 
     367        for (std::set<InfTextUser*>::const_iterator i = users.begin(); 
     368             i != users.end(); 
     369                         ++i) { 
     370                guint id = inf_user_get_id(INF_USER(*i)); 
     371                gdouble hue = inf_text_user_get_hue(*i); 
     372                gdouble r, g, b; 
     373                // TODO: use Gdk::Color c; c.set_hsv(...) if we are not depending on 2.14 
     374                gtk_hsv_to_rgb(hue, 0.35, 1.0, &r, &g, &b); 
     375                unsigned int rgb = 
     376                         ((static_cast<unsigned int>(r * 0xff) << 16) 
     377                        | (static_cast<unsigned int>(g * 0xff) << 8) 
     378                        | (static_cast<unsigned int>(b * 0xff))) & 0xffffff; 
     379                gchar const* name = inf_user_get_name(INF_USER(*i)); 
     380                // TODO: figure out if we can get "written by <name>" tooltips easily 
     381                std::ostringstream ss; 
     382                ss 
     383                        << ".user_" << id << " {\n" 
     384                        << "  background-color:       #" << std::hex << rgb << ";\n" 
     385                        << "}\n"; 
     386 
     387                css->add_child_text(ss.str()); 
     388                xmlpp::Element* item = list->add_child("li"); 
     389                item->set_attribute("class", Glib::ustring::compose("user_%1", id)); 
     390                item->add_child_text(name); 
     391        } 
     392} 
     393 
     394// generate xhtml representation of the document and write it to the 
     395// specified location in the filesystem 
     396void export_html(DocWindow& document, const Glib::ustring& output_path) { 
     397        xmlpp::Document output; 
     398        xmlpp::Element 
     399                * root      = output.create_root_node("html", "http://www.w3.org/1999/xhtml"), 
     400                * head      = root->add_child("head"), 
     401                * body      = root->add_child("body"), 
     402                * title     = head->add_child("title"), 
     403                * style     = head->add_child("style"), 
     404                * h1        = body->add_child("h1"), 
     405                * h2        = body->add_child("h2"), 
     406                * user_list = body->add_child("ul"), 
     407                * content   = body->add_child("pre"), 
     408                * info      = body->add_child("p"); 
     409 
     410        const Glib::ustring& document_name = document.get_title(); 
     411        title->add_child_text(document_name + " - infinote document"); 
     412 
     413        h1->add_child_text(document_name); 
     414 
     415        content->set_attribute("class", "document"); 
     416 
     417        std::set<InfTextUser*> users; 
     418        unsigned int line_counter; 
     419        dump_buffer(document, content, users, line_counter); 
     420 
     421        h2->add_child_text(_("Participants")); 
     422 
     423        info->set_attribute("class", "info"); 
     424  dump_info(info, document); 
     425 
     426        style->set_attribute("type", "text/css"); 
     427        add_user_colours(style, user_list, users); 
     428 
     429        style->add_child_text( 
     430                        "h1 {\n" 
     431                        "  font-family:\n" 
     432                        "}\n" 
     433                        ".document {\n" 
     434                        "  border-top:             1px solid gray;\n" 
     435                        "  border-bottom:          1px solid black;\n" 
     436                        "  padding-bottom:         1.2em;\n" 
     437                        "  counter-reset:          line;\n" 
     438                        "}\n" 
     439                        ".line_no:before {\n" 
     440                        "  content:                counter(line);\n" 
     441                        "  counter-increment:      line;\n" 
     442                        "}\n" 
     443                        ".info {\n" 
     444                        "  font-size:              small;\n" 
     445                        "}\n"); 
     446 
     447        style->add_child_text( 
     448                Glib::ustring::compose( 
     449                        ".line_no {\n" 
     450                        "  position:               absolute;\n" 
     451                        "  float:                  left;\n" 
     452                        "  clear:                  left;\n" 
     453                        "  margin-left:            -%1em;\n" 
     454                        "  color:                  gray;\n" 
     455                        "}\n" 
     456                        ".document {\n" 
     457                        "  padding-left:            %1em\n" 
     458                        "}\n", 
     459          static_cast<unsigned int>(std::log(line_counter)/std::log(10))+1)); 
     460 
     461        output.write_to_file(output_path); 
     462} 
     463 
     464void Gobby::FileCommands::on_export_html() { 
     465        DocWindow* document = m_folder.get_current_document(); 
     466        g_assert(document != NULL); 
     467 
     468        // TODO: filechooser, possibly remember choice 
     469        // what is that whole operation/task thing? 
     470        export_html(*document, "output.xhtml"); 
     471} 
     472 
     473 
    222474void Gobby::FileCommands::on_close() 
    223475{ 
     
    247499        m_header.action_file_save_as->set_sensitive(active_sensitivity); 
    248500        m_header.action_file_save_all->set_sensitive(active_sensitivity); 
     501        m_header.action_file_export_html->set_sensitive(active_sensitivity); 
    249502        m_header.action_file_close->set_sensitive(active_sensitivity); 
    250503} 
  • code/commands/file-commands.hpp

    r9d2c7bc r44f4113  
    11/* Gobby - GTK-based collaborative text editor 
    2  * Copyright (C) 2008, 2009 Armin Burgmeier <armin@arbur.net> 
     2 * Copyright (C) 2008 Armin Burgmeier <armin@arbur.net> 
    33 * 
    44 * This program is free software; you can redistribute it and/or 
     
    104104        void on_save_as(); 
    105105        void on_save_all(); 
     106        void on_export_html(); 
    106107 
    107108        void on_close(); 
  • code/core/header.cpp

    r1e48810 r44f4113  
    11/* Gobby - GTK-based collaborative text editor 
    2  * Copyright (C) 2008, 2009 Armin Burgmeier <armin@arbur.net> 
     2 * Copyright (C) 2008 Armin Burgmeier <armin@arbur.net> 
    33 * 
    44 * This program is free software; you can redistribute it and/or 
     
    4040                "      <menuitem action=\"FileSaveAs\" />" 
    4141                "      <menuitem action=\"FileSaveAll\" />" 
     42                "      <menuitem action=\"FileExportHtml\" />" 
    4243                "      <separator />" 
    4344                "      <menuitem action=\"FileClose\" />" 
     
    226227                        "FileSaveAll", Gobby::IconManager::STOCK_SAVE_ALL, 
    227228                        _("Save All"), _("Save all open files locally"))), 
     229        action_file_export_html( 
     230                Gtk::Action::create("FileExportHtml", _("Export As _HTML..."))), 
    228231        action_file_close( 
    229232                Gtk::Action::create("FileClose", Gtk::Stock::CLOSE)), 
     
    317320                        Gtk::AccelKey("<control><shift>L", 
    318321                                      "<Actions>/MenuFile/FileSaveAll")); 
     322        group_file->add(action_file_export_html, 
     323                        Gtk::AccelKey("<control><shift>H", 
     324                                      "<Actions>/MenuFile/FileExportHtml")); 
    319325        group_file->add(action_file_close); 
    320326        group_file->add(action_file_quit); 
  • code/core/header.hpp

    r0d851a2 r44f4113  
    11/* Gobby - GTK-based collaborative text editor 
    2  * Copyright (C) 2008, 2009 Armin Burgmeier <armin@arbur.net> 
     2 * Copyright (C) 2008 Armin Burgmeier <armin@arbur.net> 
    33 * 
    44 * This program is free software; you can redistribute it and/or 
     
    103103        const Glib::RefPtr<Gtk::Action> action_file_save_as; 
    104104        const Glib::RefPtr<Gtk::Action> action_file_save_all; 
     105        const Glib::RefPtr<Gtk::Action> action_file_export_html; 
    105106        const Glib::RefPtr<Gtk::Action> action_file_close; 
    106107        const Glib::RefPtr<Gtk::Action> action_file_quit;