Logo Search packages:      
Sourcecode: bibledit version File versions  Download package

stylesheetutils.cpp

/*
** Copyright (C) 2003-2006 Teus Benschop.
**  
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**  
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**  
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**  
*/


#include "libraries.h"
#include "utilities.h"
#include "stylesheetutils.h"
#include "directories.h"
#include <config.h>
#include "constants.h"
#include "xmlutils.h"
#include "gwrappers.h"
#include "gtkwrappers.h"
#include "shell.h"
#include "sqlite3.h"
#include "sqlite_reader.h"
#include "vacuum.h"


#define STYLESHEET_SUFFIX2 ".sql1"


ustring stylesheet_filename (const ustring& name)
// This returns the database's filename for a named stylesheet.
{
  ustring filename;
  filename = gw_build_filename (directories_get_stylesheets (), name + STYLESHEET_SUFFIX2);
  return filename;
}


void stylesheet_get_ones_available (vector<ustring>& names)
// Gets the names of the stylesheets that are there.
{
  ReadFiles rf (directories_get_stylesheets (), "", STYLESHEET_SUFFIX2);
  for (unsigned int i = 0; i < rf.files.size(); i++)
    rf.files[i].erase (rf.files[i].length() - strlen (STYLESHEET_SUFFIX2), strlen (STYLESHEET_SUFFIX2));
  names.assign (rf.files.begin(), rf.files.end());
}


void stylesheet_create_new (const ustring& name)
// Create a new stylesheet from templates, named "name".
{
  // Create a stylesheet "old style", as the templates are stored that way.
  ustring directory;
  directory = directories_get_stylesheets ();
  directory = gw_build_filename (directory, name);
  create_directory (directory);
  ReadFiles rf (PACKAGE_DATA_DIR, "", ".style");
  for (unsigned int i = 0; i < rf.files.size(); i++) {
    ustring command;
    command = "cp";
    ustring file = gw_build_filename (PACKAGE_DATA_DIR, rf.files[i]);
    command.append (shell_quote_space (file));
    file = rf.files[i];
    file = file.substr (0, file.length() - 6);
    file.append (STYLE_SUFFIX);
    file = gw_build_filename (directory, file);
    command.append (shell_quote_space (file));
    system (command.c_str());
  }
  // Upgrade this to the new format stored in db.
  stylesheets_upgrade ();
}


void stylesheet_create_new_empty (const ustring& name)
// Create the database layout for a new stylesheet, named "name".
{
  // Delete possible existing database.
  unlink (stylesheet_filename (name).c_str());
  // Some variables.  
  sqlite3 *db;
  int rc;
  char *error = NULL;
  try
  {
    // Connect to the database.
    rc = sqlite3_open(stylesheet_filename (name).c_str (), &db);
    if (rc) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    // Create the styles table. See the fields of object Style.
    char * sql;
    sql = g_strdup_printf("create table styles (marker text, name text, info text, "
      "fontsize real, fontpercentage integer, "
      "italic text, bold text, underline text, smallcaps text, superscript integer, justification text, "
      "spacebefore real, spaceafter real, leftmargin real, rightmargin real, "
      "firstlineindent real, spancolumns integer, "
      "type integer, subtype integer, "
      "userbool1 integer, userbool2 integer, userbool3 integer, "
      "userint1 integer, userint2 integer, userint3 integer, "
      "userstring1 text, userstring2 text, userstring3 text);");
    rc = sqlite3_exec (db, sql, NULL, NULL, &error);
    if (rc) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    // Create the recently used styles table.
    sql = g_strdup_printf("create table recent (marker text);");
    rc = sqlite3_exec (db, sql, NULL, NULL, &error);
    if (rc) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    g_free (sql);
  }
  catch (exception & ex)
  {
    gw_critical (ex.what ());
  }
  // Close connection.
  sqlite3_close (db);
}


void stylesheet_delete (const ustring& name)
// Deletes a stylesheet.
{
  unlink (stylesheet_filename (name).c_str());
}


void stylesheet_copy (const ustring& from_name, const ustring& to_name)
// Copies one stylesheet to another.
{
  ustring command = "cp";
  command.append (shell_quote_space (stylesheet_filename (from_name)));
  command.append (shell_quote_space (stylesheet_filename (to_name)));
  system (command.c_str());
}


ustring stylesheet_import (const ustring& filename)
// Imports a new stylesheet from "filename".
// It expects a file in the formst as it is given in the export function,
// that is a zipped file that contains all the individual styles.
{
  // Derive the name of the new stylesheet from the filename of the zipped archive.
  ustring name;
  name = gw_path_get_basename (filename);
  if (g_str_has_suffix (filename.c_str(), ".zip"))
    name.erase (name.length() - 4, 4);
  // Check whether it already exists.
  if (stylesheet_exists (name)) {
    gtkw_dialog_error (NULL, "A stylesheet with this name could not be imported, because it already exists");
    return "";
  }
  // Get the path of the new stylesheet.
  ustring path = directories_get_stylesheets ();
  path = gw_build_filename (path, name);
  // Create the directory.
  create_directory (path);
  // Copy the zip archive there.
  ustring command;
  command = "cp";
  command.append (shell_quote_space (filename));
  command.append (shell_quote_space (path));
  system (command.c_str());
  // Unpack the zip archive and remove it.
  command = "cd ";
  command.append (shell_quote_space (path));
  command.append ("; unzip *.zip; rm *.zip");
  system (command.c_str());
  // Update the stylesheet.
  stylesheets_upgrade ();
  // Return the name of the stylesheet we imported;
  return name;
}


bool stylesheet_exists (const ustring& name)
// Returns whether this stylesheet exists.
{
  vector<ustring> sheets;
  stylesheet_get_ones_available (sheets);
  set<ustring> existing_sheets (sheets.begin(), sheets.end());
  return (existing_sheets.find (name) != existing_sheets.end());
}


void stylesheet_get_styles (const ustring& stylesheet, vector<Style>& styles)
// Get all data of all stylesheets.
{
  // Get available markers.
  vector<ustring> markers = stylesheet_get_markers (stylesheet, NULL);
  // Read all styles.
  for (unsigned int i = 0; i < markers.size(); i++) {
    Style style (stylesheet, markers[i], false);
    styles.push_back (style);
  }
}


vector<ustring> stylesheet_get_markers (const ustring& stylesheet, vector<ustring> * names)
/*
This function only gets the markers and the names of the styles of the stylesheet,
and is therefore faster than the similar function that gets the whole style.
It is intended to be used in such situation that only the markers and/or the
name of a style is needed, such as in the styletree.
*/
{
  // Store styles
  vector<ustring> markers;
  // Some variables.  
  sqlite3 *db;
  int rc;
  char *error = NULL;
  try
  {
    // Connect to the database.
    rc = sqlite3_open(stylesheet_filename (stylesheet).c_str (), &db);
    if (rc) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    // Read the available markers.
    SqliteReader reader (0);
    char * sql;
    sql = g_strdup_printf ("select marker, name from styles;");
    rc = sqlite3_exec(db, sql, reader.callback, &reader, &error);
    g_free (sql);
    if (rc != SQLITE_OK) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    markers.assign (reader.ustring0.begin(), reader.ustring0.end());
    if (names)
      names->assign (reader.ustring1.begin(), reader.ustring1.end());
  }
  catch (exception & ex)
  {
    gw_critical (ex.what ());
  }
  // Close connection.
  sqlite3_close (db);
  // Return result.
  return markers;
}


void stylesheet_delete_style (const ustring& stylesheet, const ustring& marker)
// Deletes a style.
{
  // Some variables.  
  sqlite3 *db;
  int rc;
  char *error = NULL;
  try
  {
    // Connect to the database.
    rc = sqlite3_open(stylesheet_filename (stylesheet).c_str (), &db);
    if (rc) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    // Delete style.
    char * sql;
    sql = g_strdup_printf ("delete from styles where marker = '%s';", marker.c_str());
    rc = sqlite3_exec (db, sql, NULL, NULL, &error);
    if (rc) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    g_free (sql);
  }
  catch (exception & ex)
  {
    gw_critical (ex.what ());
  }
  // Close connection.
  sqlite3_close (db);
}


void stylesheet_new_style (const ustring& stylesheet, const ustring& marker, const ustring& name, const ustring& info)
// Adds a new style.
{
  // See if the marker happens to be in the templates already.  
  ustring template_file = gw_build_filename (PACKAGE_DATA_DIR, marker + ".style");
  bool read_from_template = (g_file_test (template_file.c_str(), G_FILE_TEST_IS_REGULAR));
  Style style (stylesheet, marker, true);
  if (read_from_template) {
    style.import_it (template_file);
  } else {
    style.name = name;
    style.info = info;
  }
}


void stylesheet_save_style (const ustring& stylesheet, const ustring& marker,
                            const ustring& name, const ustring& info,
                            StyleType type, int subtype,
                            double fontsize, int fontpercentage,
                            const ustring& italic, const ustring& bold, 
                            const ustring& underline, const ustring& smallcaps,
                            bool superscript, const ustring& justification,
                            double spacebefore, double spaceafter,
                            double leftmargin, double rightmargin,
                            double firstlineindent, bool spancolumns,
                            bool userbool1, bool userbool2, bool userbool3,
                            int userint1, int userint2, int userint3,
                            ustring userstring1, ustring userstring2, ustring userstring3)
// Saves style information.
{
  Style style (stylesheet, marker, true);
  style.name = name;
  style.info = info;
  style.type = type;
  style.subtype = subtype;
  style.fontsize = fontsize;
  style.fontpercentage = fontpercentage;
  style.italic = italic;
  style.bold = bold;
  style.underline = underline;
  style.smallcaps = smallcaps;
  style.superscript = superscript;
  style.justification = justification;
  style.spacebefore = spacebefore;
  style.spaceafter = spaceafter;
  style.leftmargin = leftmargin;
  style.rightmargin = rightmargin;
  style.firstlineindent = firstlineindent;
  style.spancolumns = spancolumns;
  style.userbool1 = userbool1;
  style.userbool2 = userbool2;
  style.userbool3 = userbool3;
  style.userint1 = userint1;
  style.userint2 = userint2;
  style.userint3 = userint3;
  style.userstring1 = userstring1;
  style.userstring2 = userstring2;
  style.userstring3 = userstring3;
}


int stylesheet_style_get_pointer (const vector<Style>& styles, const ustring& marker)
// Returns a pointer to "styles" which describes "marker".
// Or -1 if not found.
{
  // Search marker and return pointer.
  for (unsigned int i = 0; i < styles.size(); i++)
    if (marker == styles[i].marker)
      return i;
  // Ok, marker not found.
  return -1;
}


void stylesheets_upgrade ()
// Upgrade older stylesheets to the currently used format.
{
  // Deal with all available stylesheets as they are stored in the way the first
  // generation of Bibledit did it: A directory, with separate style files in it.
  vector<ustring> available_stylesheets;
  {
    ReadDirectories rd (directories_get_stylesheets (), "", "");
    available_stylesheets.assign (rd.directories.begin(), rd.directories.end());
  }
  for (unsigned int ss = 0; ss < available_stylesheets.size(); ss++) {

    ustring directory;
    directory = gw_build_filename (directories_get_stylesheets (), available_stylesheets[ss]);
    
    // Check whether first version needs upgrade. The upgrade affects styles 
    // that start paragraphs. The substyle should be set properly.
    ReadFiles rf (directory, "", ".xml");
    if (!rf.files.empty())
      gw_message ("Upgrading stylesheet " + available_stylesheets[ss]);
    for (unsigned int i = 0; i < rf.files.size(); i++) {
      // Get the three filenames: old name of stylesheet, template, and new name.
      ustring stylesheet_oldname = gw_build_filename (directory, rf.files[i]);
      ustring templatefile = gw_build_filename (PACKAGE_DATA_DIR, rf.files[i].substr (0, rf.files[i].length() - 4) + ".style");
      ustring stylesheet_newname = gw_build_filename (directory, rf.files[i].substr (0, rf.files[i].length() - 4) + ".style2");
      // Read the text, and see whether this is a paragraph style, and get the line number to upgrade.
      ReadText rt (stylesheet_oldname, true, false);
      int substyle_line_number = -1;
      bool paragraph_style = false;
      for (unsigned int ln = 0; ln < rt.lines.size(); ln++) {
        if (rt.lines[ln].find ("<subtype>") != string::npos) {
          substyle_line_number = ln;
        }
        if (rt.lines[ln].find ("starts-paragraph") != string::npos) {
          paragraph_style = true;
        }
      }
      // If paragraph style, upgrade it.
      if (paragraph_style && (substyle_line_number > 0)) {
        ReadText rt_template (templatefile, true, false);
        for (unsigned int ln = 0; ln < rt_template.lines.size(); ln++) {
          if (rt_template.lines[ln].find ("<subtype>") != string::npos) {
            rt.lines[substyle_line_number] = rt_template.lines[ln];
          }
        }
      }
      // Save under new name and delete old filename.
      write_lines (stylesheet_newname, rt.lines);
      unlink (stylesheet_oldname.c_str());
    }
  }

  // As Bibledit switched to stylesheet stored in SQLite, upgrade to that format.
  for (unsigned int ss = 0; ss < available_stylesheets.size(); ss++) {
    
    ustring stylesheet = available_stylesheets[ss];

    gw_message ("Upgrading stylesheet " + stylesheet);
    
    stylesheet_create_new_empty (stylesheet);

    // Get the available stylesheets.
    ustring directory = gw_build_filename (directories_get_stylesheets (), stylesheet);
    ReadFiles rf (directory, "", STYLE_SUFFIX);

    // Upgrade each of them.
    for (unsigned int i = 0; i < rf.files.size(); i++) {
      ustring marker = rf.files[i].substr (0, rf.files[i].length() - strlen (STYLE_SUFFIX));
      Style style (stylesheet, marker, true);
      style.import_it (gw_build_filename (directory, rf.files[i]));
    }

    // Delete the stylesheet in old formaat.
    remove_directory (directory);      
    
    // Get all recently used styles.
    {
      // Read all the styles (format: style <space> name).
      ustring filename = gw_build_filename (directories_get_stylesheets (), stylesheet, "recently-used");      
      ReadText rt (filename, true);
      vector<ustring> recent_markers;
      // Keep the marker only, and throw away the name.
      for (unsigned int i = 0; i < rt.lines.size(); i++) {
        Parse parse (rt.lines[i]);
        recent_markers.push_back (parse.words[0]);
      } 
      stylesheet_set_recently_used (stylesheet, recent_markers);      
    }
  }  

  // At the end of all check we've at least one stylesheet available.  
  {
    vector<ustring> stylesheets;
    stylesheet_get_ones_available (stylesheets);
    if (stylesheets.size() == 0) {
      stylesheet_create_new (STANDARDSHEET);
    }
  }
}


vector<ustring> stylesheet_get_recently_used (const ustring& stylesheet)
{
  // Read all the markers.
  vector<ustring> recent_markers;
  // Some variables.  
  sqlite3 *db;
  int rc;
  char *error = NULL;
  try
  {
    // Connect to the database.
    rc = sqlite3_open(stylesheet_filename (stylesheet).c_str (), &db);
    if (rc) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    // Read the styles.
    SqliteReader reader (0);
    char * sql;
    sql = g_strdup_printf ("select marker from recent;");
    rc = sqlite3_exec(db, sql, reader.callback, &reader, &error);
    g_free (sql);
    if (rc != SQLITE_OK) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    recent_markers.assign (reader.ustring0.begin(), reader.ustring0.end());
  }
  catch (exception & ex)
  {
    gw_critical (ex.what ());
  }
  // Close connection.
  sqlite3_close (db);
  // Take out the ones that are not in the stylesheet.
  vector<ustring> styles = stylesheet_get_markers (stylesheet, NULL);
  set<ustring> styles2 (styles.begin(), styles.end());
  vector<ustring> existing_ones;
  for (unsigned int i = 0; i < recent_markers.size(); i++) {
    if (styles2.find (recent_markers[i]) != styles2.end())
      existing_ones.push_back (recent_markers[i]);
  }
  recent_markers.clear();
  recent_markers.assign (existing_ones.begin(), existing_ones.end());
  // Return result.
  return recent_markers;  
}


void stylesheet_set_recently_used (const ustring& stylesheet, vector<ustring> styles)
{
  // Some variables.  
  sqlite3 *db;
  int rc;
  char *error = NULL;
  try
  {
    // Connect to the database.
    rc = sqlite3_open(stylesheet_filename (stylesheet).c_str (), &db);
    if (rc) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    // Variable.
    char * sql;
    // Delete all existing markers.
    sql = g_strdup_printf ("delete from recent;");
    rc = sqlite3_exec (db, sql, NULL, NULL, &error);
    if (rc) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    // Insert all new markers.
    for (unsigned int i = 0; i < styles.size(); i++) {
      sql = g_strdup_printf("insert into recent values ('%s');", styles[i].c_str());
      rc = sqlite3_exec (db, sql, NULL, NULL, &error);
      if (rc) {
        throw runtime_error (sqlite3_errmsg(db));
      }
    }
    // Free memory.
    g_free (sql);
  }
  catch (exception & ex)
  {
    gw_critical (ex.what ());
  }
  // Close connection.
  sqlite3_close (db);
}


bool stylesheet_style_has_endmarker (const Style& style)
{
  bool endmarker = false;
  if (style.type == stInlineText)
    endmarker = true;
  if (style.type == stFootEndNote) {
    if (style.subtype == fentFootnote)
      endmarker = true;
    if (style.subtype == fentEndnote)
      endmarker = true;
    if (style.subtype == fentContentWithEndmarker)
      endmarker = true;
  }
  if (style.type == stCrossreference) {
    if (style.subtype == ctCrossreference)
      endmarker = true;
    if (style.subtype == ctContentWithEndmarker)
      endmarker = true;
  }
  return endmarker;
}


void stylesheet_save_style (const ustring& stylesheet, const Style& style)
{
  // Some variables.  
  sqlite3 *db;
  int rc;
  char *error = NULL;
  try
  {
    // Connect to the database.
    rc = sqlite3_open(stylesheet_filename (stylesheet).c_str (), &db);
    if (rc) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    // Variable.
    char * sql;
    // Delete possible existing style.
    sql = g_strdup_printf ("delete from styles where marker = '%s';", style.marker.c_str());
    rc = sqlite3_exec (db, sql, NULL, NULL, &error);
    if (rc) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    // Save the style.
    sql = g_strdup_printf("insert into styles values ('%s', '%s', '%s', %f, %i, '%s', '%s', '%s', '%s', %i, '%s', %f, %f, %f, %f, %f, %i, %i, %i, %i, %i, %i, %i, %i, %i, '%s', '%s', '%s');",
      style.marker.c_str(), double_apostrophy (style.name).c_str(), double_apostrophy (style.info).c_str(),
      style.fontsize, style.fontpercentage,
      style.italic.c_str(), style.bold.c_str(), style.underline.c_str(), style.smallcaps.c_str(), style.superscript, style.justification.c_str(),
      style.spacebefore, style.spaceafter, style.leftmargin, style.rightmargin,
      style.firstlineindent, style.spancolumns, 
      style.type, style.subtype,
      style.userbool1, style.userbool2, style.userbool3,
      style.userint1, style.userint2, style.userint3,
      style.userstring1.c_str(), style.userstring2.c_str(), style.userstring3.c_str());
    rc = sqlite3_exec (db, sql, NULL, NULL, &error);
    if (rc) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    // Free memory.
    g_free (sql);
  }
  catch (exception & ex)
  {
    gw_critical (ex.what ());
  }
  // Close connection.
  sqlite3_close (db);
}


void stylesheet_load_style (const ustring& stylesheet, Style& style)
{
  // Some variables.  
  sqlite3 *db;
  int rc;
  char *error = NULL;
  try
  {
    // Connect to the database.
    rc = sqlite3_open(stylesheet_filename (stylesheet).c_str (), &db);
    if (rc) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    // Read the style.
    SqliteReader reader (0);
    char * sql;
    sql = g_strdup_printf ("select name, info, fontsize, fontpercentage, "
      "italic, bold, underline, smallcaps, superscript, justification, "
      "spacebefore, spaceafter, leftmargin, rightmargin, firstlineindent, spancolumns, "
      "type, subtype, userbool1, userbool2, userbool3, "
      "userint1, userint2, userint3, userstring1, userstring2, userstring3 "
      "from styles where marker = '%s';", style.marker.c_str());
    rc = sqlite3_exec(db, sql, reader.callback, &reader, &error);
    g_free (sql);
    if (rc != SQLITE_OK) {
      throw runtime_error (sqlite3_errmsg(db));
    }
    if (reader.ustring0.size() > 0) {
      style.name = reader.ustring0[0];
      style.info = reader.ustring1[0];
      style.fontsize = convert_to_double (reader.ustring2[0]);
      style.fontpercentage = convert_to_int (reader.ustring3[0]);
      style.italic = reader.ustring4[0];
      style.bold = reader.ustring5[0];
      style.underline = reader.ustring6[0];
      style.smallcaps = reader.ustring7[0];
      style.superscript = convert_to_bool (reader.ustring8[0]);
      style.justification = reader.ustring9[0];
      style.spacebefore = convert_to_double (reader.ustring10[0]);
      style.spaceafter = convert_to_double (reader.ustring11[0]);
      style.leftmargin = convert_to_double (reader.ustring12[0]);
      style.rightmargin = convert_to_double (reader.ustring13[0]);
      style.firstlineindent = convert_to_double (reader.ustring14[0]);
      style.spancolumns = convert_to_bool (reader.ustring15[0]);
      style.type = (StyleType) convert_to_int (reader.ustring16[0]);
      style.subtype = convert_to_int (reader.ustring17[0]);
      style.userbool1 = convert_to_bool (reader.ustring18[0]);
      style.userbool2 = convert_to_bool (reader.ustring19[0]);
      style.userbool3 = convert_to_bool (reader.ustring20[0]);
      style.userint1 = convert_to_int (reader.ustring21[0]);
      style.userint2 = convert_to_int (reader.ustring22[0]);
      style.userint3 = convert_to_int (reader.ustring23[0]);
      style.userstring1 = reader.ustring24[0];
      style.userstring2 = reader.ustring25[0];
      style.userstring3 = reader.ustring26[0];
    }
  }
  catch (exception & ex)
  {
    gw_critical (ex.what ());
  }
  // Close connection.
  sqlite3_close (db);
}


void stylesheet_vacuum (const ustring& stylesheet, unsigned int starttime)
{
  vacuum_database (stylesheet_filename (stylesheet), starttime);
}

Generated by  Doxygen 1.6.0   Back to index