// -*- C++ -*-
/*
* GChemPaint library
* application.cc
*
* Copyright (C) 2004-2007 Jean Bréfort <jean.brefort@normalesup.org>
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include "gchempaint-config.h"
#include "application.h"
#include "document.h"
#include "electron.h"
#include "text.h"
#include "plugin.h"
#include "mesomer.h"
#include "mesomery.h"
#include "mesomery-arrow.h"
#include "newfiledlg.h"
#include "reaction.h"
#include "reactant.h"
#include "reaction-step.h"
#include "reaction-arrow.h"
#include "settings.h"
#include "theme.h"
#include "tools.h"
#include "window.h"
#include "zoomdlg.h"
#include <gcu/filechooser.h>
#include <goffice/utils/go-file.h>
#include <sys/stat.h>
#include <gconf/gconf-client.h>
#include <libgnomevfs/gnome-vfs-mime.h>
#include <libgnomevfs/gnome-vfs-ops.h>
#include <libgnomevfs/gnome-vfs-utils.h>
#include <libgnomevfs/gnome-vfs-mime-info.h>
#include <glib/gi18n-lib.h>
#include <openbabel/mol.h>
#include <openbabel/obconversion.h>
#ifdef HAVE_FSTREAM
# include <fstream>
#else
# include <fstream.h>
#endif
#include <clocale>
#include <cstring>
// following code is needed to get file extensions, it as been essentially copied from gnome-vfs
static map<string, list<string> > globs;
static void load_globs_from_dir (char const *directory)
{
char *file_name = (char*) malloc (strlen (directory) + strlen ("/mime/globs") + 1);
struct stat st;
strcpy (file_name, directory); strcat (file_name, "/mime/globs");
if (stat (file_name, &st) == 0)
{
FILE *glob_file;
char line[255];
glob_file = fopen (file_name, "r");
if (glob_file == NULL)
return;
/* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
* Blah */
while (fgets (line, 255, glob_file) != NULL)
{
char *colon;
if (line[0] == '#')
continue;
colon = strchr (line, ':');
if (colon == NULL)
continue;
*(colon++) = '\000';
colon[strlen (colon) -1] = '\000';
colon = strchr (colon, '.');
if (colon == NULL)
continue;
colon++;
if (!*colon)
continue;
globs[line].push_back (colon);
}
fclose (glob_file);
}
free (file_name);
}
static void load_globs ()
{
const char *xdg_data_home;
const char *xdg_data_dirs;
const char *ptr;
xdg_data_home = getenv ("XDG_DATA_HOME");
if (xdg_data_home)
{
load_globs_from_dir (xdg_data_home);
}
else
{
const char *home;
home = getenv ("HOME");
if (home != NULL)
{
char *guessed_xdg_home;
guessed_xdg_home = (char*) malloc (strlen (home) + strlen ("/.local/share/") + 1);
strcpy (guessed_xdg_home, home);
strcat (guessed_xdg_home, "/.local/share/");
load_globs_from_dir (guessed_xdg_home);
free (guessed_xdg_home);
}
}
xdg_data_dirs = getenv ("XDG_DATA_DIRS");
if (xdg_data_dirs == NULL)
xdg_data_dirs = "/usr/local/share/:/usr/share/";
ptr = xdg_data_dirs;
while (*ptr != '\000')
{
const char *end_ptr;
char *dir;
int len;
end_ptr = ptr;
while (*end_ptr != ':' && *end_ptr != '\000')
end_ptr ++;
if (end_ptr == ptr)
{
ptr++;
continue;
}
if (*end_ptr == ':')
len = end_ptr - ptr;
else
len = end_ptr - ptr + 1;
dir = (char*) malloc (len + 1);
strncpy (dir, ptr, len);
dir[len] = '\0';
load_globs_from_dir (dir);
free (dir);
ptr = end_ptr;
}
}
// Objects creation static methods
static Object* CreateMolecule ()
{
return new gcpMolecule ();
}
static Object* CreateReaction ()
{
return new gcpReaction ();
}
static Object* CreateReactant ()
{
return new gcpReactant ();
}
static Object* CreateReactionStep ()
{
return new gcpReactionStep ();
}
static Object* CreateReactionArrow ()
{
return new gcpReactionArrow (NULL);
}
static Object* CreateMesomery ()
{
return new gcpMesomery ();
}
static Object* CreateMesomeryArrow ()
{
return new gcpMesomeryArrow (NULL);
}
static Object* CreateMesomer ()
{
return new gcpMesomer ();
}
static Object* CreateText ()
{
return new gcpText ();
}
static Object* CreateFragment ()
{
return new gcpFragment ();
}
bool gcpApplication::m_bInit = false;
bool gcpApplication::m_Have_Ghemical = false;
bool gcpApplication::m_Have_InChI = false;
static void on_config_changed (GConfClient *client, guint cnxn_id, GConfEntry *entry, gcpApplication *app)
{
app->OnConfigChanged (client, cnxn_id, entry);
}
gcpApplication::gcpApplication ():
Application ("GChemPaint", DATADIR, PACKAGE, "gchempaint")
{
m_CurZ = 6;
m_pActiveDoc = NULL;
m_pActiveTool = NULL;
m_NumWindow = 1;
if (!m_bInit) {
// Check for programs
char *result = NULL, *errors = NULL;
// check for ghemical
m_Have_Ghemical = (g_spawn_command_line_sync ("which ghemical", &result, &errors, NULL, NULL)
&& result && strlen (result));
if (result) {
g_free (result);
result = NULL;
}
if (errors) {
g_free (errors);
errors = NULL;
}
OBConversion Conv;
m_Have_InChI = Conv.FindFormat ("inchi") != NULL ||
(g_spawn_command_line_sync ("which main_inchi", &result, &errors, NULL, NULL)
&& result && strlen (result));
if (result)
g_free (result);
if (errors) {
g_free (errors);
errors = NULL;
}
// Initialize types
Object::AddType ("molecule", ::CreateMolecule, MoleculeType);
Object::AddType ("reaction", CreateReaction, ReactionType);
Object::SetCreationLabel (ReactionType, _("Create a new reaction"));
ReactionStepType = Object::AddType ("reaction-step", CreateReactionStep);
Object::AddType ("reactant", CreateReactant, ReactantType);
Object::AddType("reaction-arrow", CreateReactionArrow, ReactionArrowType);
MesomerType = Object::AddType ("mesomer", CreateMesomer);
Object::AddType ("mesomery", CreateMesomery, MesomeryType);
Object::SetCreationLabel (MesomeryType, _("Create a new mesomery relationship"));
Object::AddType("mesomery-arrow", CreateMesomeryArrow, MesomeryArrowType);
Object::AddType("text", CreateText, TextType);
Object::AddType("fragment", CreateFragment, FragmentType);
ElectronType = Object::AddType ("electron", NULL);
//Add rules
Object::AddRule ("reaction", RuleMustContain, "reaction-step");
Object::AddRule ("reaction-step", RuleMustContain, "reactant");
Object::AddRule ("reactant", RuleMustBeIn, "reaction-step");
Object::AddRule ("reaction-step", RuleMustBeIn, "reaction");
Object::AddRule ("reaction", RuleMustContain, "reaction-arrow");
Object::AddRule ("reaction-arrow", RuleMustBeIn, "reaction");
Object::AddRule ("reactant", RuleMayContain, "molecule");
Object::AddRule ("mesomer", RuleMustContain, "molecule");
Object::AddRule ("mesomer", RuleMustBeIn, "mesomery");
Object::AddRule ("mesomery", RuleMustContain, "mesomer");
Object::AddRule ("mesomery", RuleMustContain, "mesomery-arrow");
Object::AddRule ("mesomery-arrow", RuleMustBeIn, "mesomery");
// Create global signal ids
OnChangedSignal = Object::CreateNewSignalId ();
OnDeleteSignal = Object::CreateNewSignalId ();
OnThemeChangedSignal = Object::CreateNewSignalId ();
gcpPlugin::LoadPlugins();
m_bInit = true;
}
RadioActions = NULL;
m_entries = 0;
IconFactory = gtk_icon_factory_new ();
set<gcpPlugin*>::iterator i = Plugins.begin (), end = Plugins.end ();
while (i != end) (*i++)->Populate (this);
gtk_icon_factory_add_default (IconFactory);
g_object_unref (G_OBJECT (IconFactory));
XmlDoc = xmlNewDoc ((xmlChar const*) "1.0");
visible_windows = 0;
load_globs ();
m_SupportedMimeTypes.push_back ("application/x-gchempaint");
m_WriteableMimeTypes.push_back ("application/x-gchempaint");
// test if OpenBabel supports some extra types
TestSupportedType ("chemical/x-cml");
TestSupportedType ("chemical/x-mdl-molfile");
TestSupportedType ("chemical/x-pdb");
TestSupportedType ("chemical/x-xyz");
TestSupportedType ("chemical/x-cdx");
TestSupportedType ("chemical/x-cdxml");
TestSupportedType ("chemical/x-ncbi-asn1");
TestSupportedType ("chemical/x-ncbi-asn1-binary");
TestSupportedType ("chemical/x-ncbi-asn1-xml");
// now read extra types declared by the user
char *home = getenv ("HOME");
if (home) {
string path = home;
path += "/.gchempaint/mime-types";
char line[255];
ifstream f (path.c_str ());
while (!f.fail ()) {
f.getline (line, 255);
if (*line)
TestSupportedType (line);
}
}
m_ConfClient = gconf_client_get_default ();
gconf_client_add_dir (m_ConfClient, "/apps/gchempaint/settings", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
GError *error = NULL;
GCU_GCONF_GET (ROOTDIR"compression", int, CompressionLevel, 0)
GCU_GCONF_GET (ROOTDIR"tearable-mendeleiev", bool, TearableMendeleiev, false)
bool CopyAsText;
GCU_GCONF_GET (ROOTDIR"copy-as-text", bool, CopyAsText, false)
ClipboardFormats = CopyAsText? GCP_CLIPBOARD_ALL: GCP_CLIPBOARD_NO_TEXT;
m_NotificationId = gconf_client_notify_add (m_ConfClient, "/apps/gchempaint/settings", (GConfClientNotifyFunc) on_config_changed, this, NULL, NULL);
// make themes permanent with this as a dummy client
list <string> Names = ThemeManager.GetThemesNames ();
list <string>::iterator j, jend = Names.end ();
gcpTheme *pTheme;
m_Dummy = new Object (0);
for (j = Names.begin (); j != jend; j++) {
pTheme = ThemeManager.GetTheme ("Default");
pTheme->AddClient (m_Dummy);
}
}
gcpApplication::~gcpApplication()
{
map<string, gcpTool*>::iterator tool = Tools.begin(), endtool = Tools.end();
for (; tool!= endtool; tool++)
delete (*tool).second;
Tools.clear ();
if (XmlDoc)
xmlFreeDoc (XmlDoc);
m_SupportedMimeTypes.clear ();
delete m_Dummy;
}
void gcpApplication::ActivateTool(const string& toolname, bool activate)
{
if (Tools[toolname]) {
if (activate) {
if (m_pActiveTool != Tools[toolname]) {
if (m_pActiveTool)
m_pActiveTool->Activate (false);
m_pActiveTool = Tools[toolname];
m_pActiveTool->Activate (true);
GtkToggleToolButton* button = (GtkToggleToolButton*) ToolItems[toolname];
if (button && !gtk_toggle_tool_button_get_active (button))
gtk_toggle_tool_button_set_active (button, true);
}
} else {
if (m_pActiveTool == Tools[toolname])
m_pActiveTool = NULL;
Tools[toolname]->Activate (false);
}
}
}
void gcpApplication::ClearStatus()
{
if (m_pActiveDoc) {
gcpWindow *Win = m_pActiveDoc->GetWindow ();
if (Win)
Win->ClearStatus ();
}
}
void gcpApplication::SetStatusText(const char* text)
{
if (m_pActiveDoc) {
gcpWindow *Win = m_pActiveDoc->GetWindow ();
if (Win)
Win->SetStatusText (text);
}
}
void gcpApplication::OnSaveAs()
{
FileChooser (this, true, m_WriteableMimeTypes, m_pActiveDoc);
}
enum {
CHEMISTRY,
SVG,
EPS,
PIXBUF
};
bool gcpApplication::FileProcess (const gchar* filename, const gchar* mime_type, bool bSave, GtkWindow *window, Document *Doc)
{
const gchar* ext;
gcpDocument *pDoc = static_cast<gcpDocument*> (Doc);
if (!filename || !strlen(filename) || filename[strlen(filename) - 1] == '/')
{
GtkWidget* message = gtk_message_dialog_new (window, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
_("Please enter a file name,\nnot a directory"));
gtk_window_set_icon_name (GTK_WINDOW (message), "gchempaint");
gtk_dialog_run (GTK_DIALOG (message));
gtk_widget_destroy (message);
return true;
}
int file_type = -1;
int n = strlen (filename), i = n - 1;
char const *pixbuf_type = NULL;
string filename2 = filename;
while ((i > 0) && (filename[i] != '.') && (filename[i] != '/')) i--;
if (filename[i] == '/') i = 0;
ext = (i > 0)? filename + i + 1: NULL;
list<string>::iterator it, itend = m_SupportedMimeTypes.end ();
for (it = m_SupportedMimeTypes.begin (); it != itend; it++)
if (*it == mime_type) {
file_type = CHEMISTRY;
break;
}
if (file_type != CHEMISTRY) {
if (!strcmp (mime_type, "image/svg+xml"))
file_type = SVG;
else if (!strcmp (mime_type, "image/x-eps"))
file_type = EPS;
else if ((pixbuf_type = GetPixbufTypeName (filename2, mime_type))) {
file_type = PIXBUF;
if (!ext) {
filename = filename2.c_str ();
i = strlen (filename) - 1;
while ((i > 0) && (filename[i] != '.') && (filename[i] != '/')) i--;
if (filename[i] == '/') i = 0;
ext = (i > 0)? filename + i + 1: NULL;
}
}
}
if (file_type < 0 || (!bSave && (file_type > CHEMISTRY))) {
char *mess = g_strdup_printf (_("Sorry, format %s not supported!\nFailed to load %s."), mime_type, filename);
GtkWidget* message = gtk_message_dialog_new (window, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
mess);
gtk_dialog_run (GTK_DIALOG (message));
g_free (mess);
gtk_widget_destroy (message);
return true;
}
list<string> &exts = globs[mime_type];
bool err;
GnomeVFSURI *uri;
if (bSave) {
char const *default_ext = (exts.size ())? exts.front ().c_str (): NULL;
if (ext) {
list<string>::iterator cur, end = exts.end ();
for (cur = exts.begin (); cur != end; cur++)
if (*cur != ext) {
default_ext = ext;
break;
}
if (default_ext && strcmp (ext, default_ext))
ext = NULL;
}
if (default_ext && !ext)
filename2 += string(".") + default_ext;
uri = gnome_vfs_uri_new (filename2.c_str ());
err = gnome_vfs_uri_exists (uri);
gnome_vfs_uri_unref (uri);
gint result = GTK_RESPONSE_YES;
if (err) {
gchar * message = g_strdup_printf (_("File %s\nexists, overwrite?"), filename2.c_str ());
GtkDialog* Box = GTK_DIALOG (gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, message));
gtk_window_set_icon_name (GTK_WINDOW (Box), "gchempaint");
result = gtk_dialog_run (Box);
gtk_widget_destroy (GTK_WIDGET (Box));
g_free (message);
}
if (result == GTK_RESPONSE_YES) {
// destroy the old file
gnome_vfs_unlink (filename2.c_str ());
switch (file_type) {
case SVG:
m_pActiveDoc->ExportImage (filename2, "svg");
break;
case EPS:
m_pActiveDoc->ExportImage (filename2, "eps");
break;
case PIXBUF:
m_pActiveDoc->ExportImage (filename2, pixbuf_type, GetImageResolution ());
break;
default:
if (!strcmp (mime_type, "application/x-gchempaint"))
SaveGcp (filename2, pDoc);
else
SaveWithBabel (filename2, mime_type, pDoc);
}
}
} else { //loading
uri = gnome_vfs_uri_new (filename);
err = !gnome_vfs_uri_exists (uri);
gnome_vfs_uri_unref (uri);
if (err) {
if (!ext) {
list<string>::iterator cur, end = exts.end ();
for (cur = exts.begin (); cur != end; cur++) {
filename2 = string (filename) + "." + *cur;
uri = gnome_vfs_uri_new (filename2.c_str ());
err = !gnome_vfs_uri_exists (uri);
gnome_vfs_uri_unref (uri);
if (!err)
break;
}
}
if (err) {
filename2 = filename;
}
}
if (!strcmp (mime_type, "application/x-gchempaint"))
OpenGcp (filename2, pDoc);
else
OpenWithBabel (filename2, mime_type, pDoc);
}
return false;
}
void gcpApplication::SaveWithBabel (string const &filename, const gchar *mime_type, gcpDocument* pDoc)
{
pDoc->SetFileName (filename, mime_type);
pDoc->Save ();
GtkRecentData data;
data.display_name = (char*) pDoc->GetTitle ();
data.description = NULL;
data.mime_type = (char*) mime_type;
data.app_name = (char*) "gchempaint";
data.app_exec = (char*) "gchempaint %u";
data.groups = NULL;
data.is_private = FALSE;
gtk_recent_manager_add_full (GetRecentManager (), filename.c_str (), &data);
}
void gcpApplication::OpenWithBabel (string const &filename, const gchar *mime_type, gcpDocument* pDoc)
{
char *old_num_locale;
bool bNew = (pDoc == NULL || !pDoc->GetEmpty () || pDoc->GetDirty ()), local;
GnomeVFSFileInfo *info = NULL;
bool result = false, read_only = false;
try {
if (!filename.length ())
throw (int) 0;
info = gnome_vfs_file_info_new ();
gnome_vfs_get_file_info (filename.c_str (), info, GNOME_VFS_FILE_INFO_DEFAULT);
local = GNOME_VFS_FILE_INFO_LOCAL (info);
if (!(info->permissions & (GNOME_VFS_PERM_USER_WRITE | GNOME_VFS_PERM_GROUP_WRITE)))
read_only = true;
gnome_vfs_file_info_unref (info);
if (bNew) {
OnFileNew ();
pDoc = m_pActiveDoc;
}
if (local) {
ifstream ifs;
GnomeVFSURI *uri = gnome_vfs_uri_new (filename.c_str ());
ifs.open (gnome_vfs_uri_get_path (uri));
gnome_vfs_uri_unref (uri);
if (ifs.fail ())
throw (int) 1;
old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
setlocale(LC_NUMERIC, "C");
OBMol Mol;
OBConversion Conv;
OBFormat* pInFormat = Conv.FormatFromMIME (mime_type);
if (pInFormat == NULL)
throw 1;
Conv.SetInFormat (pInFormat);
while (!ifs.eof () && Conv.Read (&Mol, &ifs)) {
result = pDoc->ImportOB(Mol);
Mol.Clear ();
if (!result)
break;
}
setlocale (LC_NUMERIC, old_num_locale);
g_free (old_num_locale);
ifs.close ();
} else {
char *buf;
int size;
if (gnome_vfs_read_entire_file (filename.c_str (), &size, &buf) != GNOME_VFS_OK)
throw 1;
istringstream iss (buf);
old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
setlocale(LC_NUMERIC, "C");
OBMol Mol;
OBConversion Conv;
OBFormat* pInFormat = Conv.FormatFromExt (filename.c_str ());
if (pInFormat == NULL)
throw 1;
Conv.SetInFormat (pInFormat);
while (!iss.eof () && Conv.Read (&Mol, &iss)) {
result = pDoc->ImportOB(Mol);
Mol.Clear ();
if (!result)
break;
}
setlocale (LC_NUMERIC, old_num_locale);
g_free (old_num_locale);
g_free (buf);
}
if (!result) {
if (bNew)
pDoc->GetWindow ()->Destroy ();
throw (int) 2;
}
pDoc->SetFileName (filename, mime_type);
pDoc->SetReadOnly (read_only);
double l = pDoc->GetMedianBondLength ();
if (l > 0.) {
double r = pDoc->GetBondLength () / l;
if (fabs (r - 1.) > .0001) {
Matrix2D m (r, 0., 0., r);
// FIXME: this would not work for reactions
pDoc->Transform2D (m, 0., 0.);
}
}
gcpView *pView = pDoc->GetView ();
pView->Update (pDoc);
pDoc->Update ();
pView->EnsureSize ();
gcpWindow *win = pDoc->GetWindow ();
if (win)
win->SetTitle (pDoc->GetTitle ());
GtkRecentData data;
data.display_name = (char*) pDoc->GetTitle ();
data.description = NULL;
data.mime_type = (char*) mime_type;
data.app_name = (char*) "gchempaint";
data.app_exec = (char*) "gchempaint %u";
data.groups = NULL;
data.is_private = FALSE;
gtk_recent_manager_add_full (GetRecentManager (), filename.c_str (), &data);
}
catch (int num)
{
gchar *mess = NULL;
GtkWidget* message;
switch (num)
{
case 0:
mess = g_strdup_printf(_("No filename"));
break;
case 1:
mess = g_strdup_printf(_("Could not open file\n%s"), filename.c_str ());
break;
case 2:
mess = g_strdup_printf(_("%s: parse error."), filename.c_str ());
break;
default:
throw (num); //this should not occur
}
message = gtk_message_dialog_new(NULL, (GtkDialogFlags) 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, mess);
gtk_window_set_icon_name (GTK_WINDOW (message), "gchempaint");
g_signal_connect_swapped (G_OBJECT (message), "response", G_CALLBACK (gtk_widget_destroy), G_OBJECT (message));
gtk_widget_show(message);
g_free(mess);
}
}
void gcpApplication::SaveGcp (string const &filename, gcpDocument* pDoc)
{
pDoc->SetFileName (filename, "application/x-gchempaint");
pDoc->Save ();
GtkRecentData data;
data.display_name = (char*) pDoc->GetTitle ();
data.description = NULL;
data.mime_type = (char*) "application/x-gchempaint";
data.app_name = (char*) "gchempaint";
data.app_exec = (char*) "gchempaint %u";
data.groups = NULL;
data.is_private = FALSE;
gtk_recent_manager_add_full (GetRecentManager (), filename.c_str (), &data);
}
static int cb_vfs_to_xml (GnomeVFSHandle *handle, char* buf, int nb)
{
GnomeVFSFileSize ndone;
return (gnome_vfs_read (handle, buf, nb, &ndone) == GNOME_VFS_OK)? (int) ndone: -1;
}
void gcpApplication::OpenGcp (string const &filename, gcpDocument* pDoc)
{
xmlDocPtr xml = NULL;
char *old_num_locale, *old_time_locale;
GnomeVFSFileInfo *info = NULL;
bool create = false;
try
{
if (!filename.length ())
throw (int) 0;
// try opening with write access to see if it is readonly
// use xmlReadIO for non local files.
info = gnome_vfs_file_info_new ();
gnome_vfs_get_file_info (filename.c_str (), info, GNOME_VFS_FILE_INFO_DEFAULT);
if (GNOME_VFS_FILE_INFO_LOCAL (info)) {
if (!(xml = xmlParseFile(filename.c_str ())))
throw (int) 1;
} else {
GnomeVFSHandle *handle;
GnomeVFSResult result = gnome_vfs_open (&handle, filename.c_str (), GNOME_VFS_OPEN_READ);
if (result != GNOME_VFS_OK)
throw 1;
if (!(xml = xmlReadIO ((xmlInputReadCallback) cb_vfs_to_xml,
(xmlInputCloseCallback) gnome_vfs_close, handle, filename.c_str (), NULL, 0)))
throw 1;
}
if (xml->children == NULL) throw (int) 2;
if (strcmp((char*)xml->children->name, "chemistry")) throw (int) 3; //FIXME: that could change when a dtd is available
old_num_locale = g_strdup(setlocale(LC_NUMERIC, NULL));
setlocale(LC_NUMERIC, "C");
old_time_locale = g_strdup(setlocale(LC_TIME, NULL));
setlocale(LC_TIME, "C");
if (!pDoc || !pDoc->GetEmpty () || pDoc->GetDirty ()) {
create = true;
OnFileNew ();
pDoc = m_pActiveDoc;
}
pDoc->SetFileName(filename, "application/x-gchempaint");
bool result = pDoc->Load(xml->children);
setlocale(LC_NUMERIC, old_num_locale);
g_free(old_num_locale);
setlocale(LC_TIME, old_time_locale);
g_free(old_time_locale);
if (!result) {
if (create)
pDoc->GetWindow ()->Destroy ();
throw (int) 4;
}
if (!(info->permissions & (GNOME_VFS_PERM_USER_WRITE | GNOME_VFS_PERM_GROUP_WRITE)))
pDoc->SetReadOnly (true);
gnome_vfs_file_info_unref (info);
xmlFreeDoc(xml);
GtkRecentData data;
data.display_name = (char*) pDoc->GetTitle ();
data.description = NULL;
data.mime_type = (char*) "application/x-gchempaint";
data.app_name = (char*) "gchempaint";
data.app_exec = (char*) "gchempaint %u";
data.groups = NULL;
gtk_recent_manager_add_full (GetRecentManager (), filename.c_str (), &data);
}
catch (int num)
{
if (info)
gnome_vfs_file_info_unref (info);
if (num > 1) xmlFreeDoc(xml);
gchar *mess = NULL;
GtkWidget* message;
switch (num)
{
case 0:
mess = g_strdup_printf(_("No filename"));
break;
case 1:
mess = g_strdup_printf(_("Could not load file\n%s"), filename.c_str ());
break;
case 2:
mess = g_strdup_printf(_("%s: invalid xml file.\nTree is empty?"), filename.c_str ());
break;
case 3:
mess = g_strdup_printf(_("%s: invalid file format."), filename.c_str ());
break;
case 4:
mess = g_strdup_printf(_("%s: parse error."), filename.c_str ());
break;
default:
throw (num); //this should not occur
}
message = gtk_message_dialog_new(NULL, (GtkDialogFlags) 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, mess);
gtk_window_set_icon_name (GTK_WINDOW (message), "gchempaint");
g_signal_connect_swapped (G_OBJECT (message), "response", G_CALLBACK (gtk_widget_destroy), G_OBJECT (message));
gtk_widget_show(message);
g_free(mess);
}
}
void gcpApplication::InitTools()
{
map<string, gcpTool*>::iterator i = Tools.begin (), end = Tools.end ();
for (; i != end; i++)
if ((*i).second)
(*i).second->Activate ((*i).first == "Select");
}
void gcpApplication::OnSaveAsImage ()
{
if (!m_pActiveDoc)
return;
list<string> l;
map<string, GdkPixbufFormat*>::iterator i, end = m_SupportedPixbufFormats.end ();
for (i = m_SupportedPixbufFormats.begin (); i != end; i++)
l.push_front ((*i).first.c_str ());
l.push_front ("image/x-eps");
l.push_front ("image/svg+xml");
FileChooser (this, true, l, m_pActiveDoc, _("Save as image"), GetImageResolutionWidget ());
}
void gcpApplication::Zoom (double zoom)
{
gcpView *pView = m_pActiveDoc->GetView ();
// authorized zooms: 20% to 800% all other values will open the zoom dialog.
if (zoom >= 0.2 && zoom <= 8.)
pView->Zoom (zoom);
else {
Dialog *pDialog = GetDialog ("Zoom");
if (pDialog)
gtk_window_present (pDialog->GetWindow ());
else
new gcpZoomDlg (m_pActiveDoc);
}
}
static void on_tool_changed (GtkAction *action, GtkAction *current, gcpApplication* App)
{
App->OnToolChanged (current);
}
void gcpApplication::OnToolChanged (GtkAction *current)
{
if (m_pActiveTool)
m_pActiveTool->Activate(false);
m_pActiveTool = Tools[gtk_action_get_name (current)];
gcpTools *Tools = dynamic_cast<gcpTools*> (GetDialog ("tools"));
if (Tools)
Tools->OnSelectTool (m_pActiveTool);
if (m_pActiveTool)
m_pActiveTool->Activate(true);
}
void gcpApplication::BuildTools ()
{
gcpTools *ToolsBox = new gcpTools (this);
map<int, string>::iterator i, iend = ToolbarNames.end ();
list<char const*>::iterator j, jend = UiDescs.end ();
string s;
GError *error = NULL;
GtkUIManager *ToolsManager = gtk_ui_manager_new ();
ToolsBox->SetUIManager (ToolsManager);
GtkActionGroup *action_group = gtk_action_group_new ("Tools");
gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
gtk_action_group_add_radio_actions (action_group, RadioActions, m_entries, 0, G_CALLBACK (on_tool_changed), this);
gtk_ui_manager_insert_action_group (ToolsManager, action_group, 0);
for (j = UiDescs.begin (); j != jend; j++)
if (!gtk_ui_manager_add_ui_from_string (ToolsManager, *j, -1, &error)) {
g_message ("building user interface failed: %s", error->message);
g_error_free (error);
exit (EXIT_FAILURE);
}
for (i = ToolbarNames.begin (); i != iend; i++) {
s = "ui/";
s += (*i).second;
ToolsBox->AddToolbar (s);
}
g_object_unref (ToolsManager);
m_pActiveTool = Tools["Select"];
if (m_pActiveTool)
m_pActiveTool->Activate(true);
ToolsBox->OnSelectTool (m_pActiveTool);
ToolsBox->OnElementChanged (m_CurZ);
}
void gcpApplication::ShowTools (bool visible)
{
gcpTools *Tools = dynamic_cast<gcpTools*> (GetDialog ("tools"));
if (!Tools) {
if (visible)
BuildTools ();
} else
Tools->Show (visible);
}
void gcpApplication::AddActions (GtkRadioActionEntry const *entries, int nb, char const *ui_description, gcpIconDesc const *icons)
{
static int cur_entry = 1;
if (nb > 0) {
if (m_entries)
RadioActions = g_renew (GtkRadioActionEntry, RadioActions, m_entries + nb);
else
RadioActions = g_new (GtkRadioActionEntry, nb);
memcpy (RadioActions + m_entries, entries, nb * sizeof (GtkRadioActionEntry));
for (int i = 0; i < nb; i++)
if (strcmp (RadioActions[i + m_entries].name, "Select"))
RadioActions[i + m_entries].value = cur_entry++;
else
RadioActions[i + m_entries].value = 0;
m_entries += nb;
}
if (ui_description)
UiDescs.push_back (ui_description);
if (icons) {
GtkIconSet *set;
GtkIconSource *src;
while (icons->name) {
set = gtk_icon_set_new ();
src = gtk_icon_source_new ();
gtk_icon_source_set_size_wildcarded (src, TRUE);
gtk_icon_source_set_pixbuf (src,
gdk_pixbuf_new_from_inline (-1, icons->data_24, FALSE, NULL));
gtk_icon_set_add_source (set, src); /* copies the src */
gtk_icon_factory_add (IconFactory, icons->name, set); /* keeps reference to set */
gtk_icon_set_unref (set);
gtk_icon_source_free (src);
icons++;
}
}
}
void gcpApplication::RegisterToolbar (char const *name, int index)
{
if (ToolbarNames[index] == "")
ToolbarNames[index] = name;
}
void gcpApplication::AddWindow (gcpWindow *window)
{
m_Windows.insert (window);
NotifyIconification (false);
}
void gcpApplication::DeleteWindow (gcpWindow *window)
{
m_Windows.erase (window);
ShowTools (false);
}
void gcpApplication::NotifyIconification (bool iconified)
{
if (iconified) {
ShowTools (false);
}
}
void gcpApplication::NotifyFocus (bool has_focus, gcpWindow *window)
{
if (window) {
m_pActiveWin = window;
m_pActiveDoc = window->GetDocument ();
m_pActiveTool->Activate ();
if (has_focus)
ShowTools (true);
}
}
void gcpApplication::CloseAll ()
{
while (!m_Windows.empty ())
if (!(*m_Windows.begin ())->Close ())
return;
}
void gcpApplication::ActivateWindowsActionWidget (const char *path, bool activate)
{
std::set<gcpWindow*>::iterator i, iend = m_Windows.end ();
for (i = m_Windows.begin (); i != iend; i++)
(*i)->ActivateActionWidget (path, activate);
}
void gcpApplication::OnConfigChanged (GConfClient *client, guint cnxn_id, GConfEntry *entry)
{
if (client != m_ConfClient)
return; // we might want an error message?
if (cnxn_id != m_NotificationId)
return; // we might want an error message?
if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"compression")) {
CompressionLevel = gconf_value_get_int (gconf_entry_get_value (entry));
} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"tearable-mendeleiev")) {
TearableMendeleiev = gconf_value_get_bool (gconf_entry_get_value (entry));
gcpTools *Tools = dynamic_cast<gcpTools*> (GetDialog ("tools"));
if (Tools)
Tools->Update ();
} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"copy-as-text"))
ClipboardFormats = (gconf_value_get_bool (gconf_entry_get_value (entry)))? GCP_CLIPBOARD_ALL: GCP_CLIPBOARD_NO_TEXT;
}
void gcpApplication::TestSupportedType (char const *mime_type)
{
OBFormat *f = OBConversion::FormatFromMIME (mime_type);
if (f != NULL) {
m_SupportedMimeTypes.push_back (mime_type);
if (!(f->Flags () & NOTWRITABLE))
m_WriteableMimeTypes.push_back (mime_type);
}
}
list<string> &gcpApplication::GetExtensions(string &mime_type)
{
return globs[mime_type];
}
void gcpApplication::OnThemeNamesChanged ()
{
gcpNewFileDlg *dlg = dynamic_cast <gcpNewFileDlg *> (GetDialog ("newfile"));
if (dlg)
dlg->OnThemeNamesChanged ();
set <Document*>::iterator i, end = m_Docs.end ();
for (i = m_Docs.begin (); i != end; i++)
dynamic_cast <gcpDocument *> (*i)->OnThemeNamesChanged ();
}
syntax highlighted by Code2HTML, v. 0.9.1