// -*- C++ -*-
/*
* GChemPaint selection plugin
* selectiontool.cc
*
* Copyright (C) 2001-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 "selectiontool.h"
#include "group.h"
#include "groupdlg.h"
#include "lib/molecule.h"
#include "lib/settings.h"
#include "lib/document.h"
#include "lib/application.h"
#include "lib/theme.h"
#include "lib/window.h"
#include <glib/gi18n-lib.h>
#include <cmath>
#include <stdexcept>
static void on_flip(GtkWidget *btn, gcpApplication* App)
{
gcpSelectionTool *tool = (gcpSelectionTool*) App->GetTool ("Select");
if (GTK_IS_WIDGET (btn))
tool->OnFlip (strcmp (gtk_widget_get_name (btn), "VertFlip"));
else
tool->OnFlip (strcmp (gtk_action_get_name (GTK_ACTION (btn)), "VertFlip"));
}
static void on_rotate(GtkWidget *btn, gcpApplication* App)
{
gcpSelectionTool *tool = (gcpSelectionTool*) App->GetTool ("Select");
if (GTK_IS_WIDGET (btn))
tool->Rotate (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (btn)));
else
tool->Rotate (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (btn)));
}
static void on_merge(GtkWidget *btn, gcpApplication* App)
{
gcpSelectionTool *tool = (gcpSelectionTool*) App->GetTool ("Select");
tool->Merge ();
}
gcpSelectionTool::gcpSelectionTool(gcpApplication *App): gcpTool(App, "Select")
{
m_bRotate = false;
m_UIManager = NULL;
}
gcpSelectionTool::~gcpSelectionTool()
{
if (m_UIManager)
g_object_unref (m_UIManager);
}
bool gcpSelectionTool::OnClicked()
{
gcpWindow *win = m_pView->GetDoc ()->GetWindow ();
if (m_pObject)
{
Object* pObj = m_pObject->GetGroup();
if (pObj) m_pObject = pObj;
if (!m_pData->IsSelected(m_pObject))
{
m_pData->UnselectAll();
m_pData->SetSelected(m_pObject);
win->ActivateActionWidget ("/MainMenu/EditMenu/Copy", true);
win->ActivateActionWidget ("/MainMenu/EditMenu/Cut", true);
win->ActivateActionWidget ("/MainMenu/EditMenu/Erase", true);
}
}
else
{
m_pData->UnselectAll();
win->ActivateActionWidget ("/MainMenu/EditMenu/Copy", false);
win->ActivateActionWidget ("/MainMenu/EditMenu/Cut", false);
win->ActivateActionWidget ("/MainMenu/EditMenu/Erase", false);
}
if (m_bRotate) {
// Calculate center of selection
ArtDRect rect;
m_pData->GetSelectionBounds(rect);
m_cx = (rect.x0 + rect.x1) / 2.;
m_cy = (rect.y0 + rect.y1) / 2.;
m_dAngle = 0.;
m_x0 -= m_cx;
m_y0 -= m_cy;
if (m_x0 == 0)
m_dAngleInit = (m_y0 <= 0) ? 90 : 270;
else
m_dAngleInit = atan(-m_y0/m_x0) * 180 / M_PI;
if (m_x0 < 0) m_dAngleInit += 180.;
std::list<Object*>::iterator i;
gcpDocument* pDoc = m_pView->GetDoc();
m_pOp = pDoc-> GetNewOperation(GCP_MODIFY_OPERATION);
for (i = m_pData->SelectedObjects.begin(); i != m_pData->SelectedObjects.end(); i++)
m_pOp->AddObject(*i,0);
}
return true;
}
void gcpSelectionTool::OnDrag()
{
double dx = m_x - m_x1, dy = m_y - m_y1, x1, y1, x2, y2;
m_x1 = m_x;
m_y1 = m_y;
if (m_pObject)
{
if (m_bRotate) {
double dAngle;
m_x-= m_cx;
m_y -= m_cy;
if (m_x == 0)
{
if (m_y == 0) return;
dAngle = (m_y < 0) ? 90 : 270;
}
else
{
dAngle = atan(-m_y/m_x) * 180. / M_PI;
if (m_x < 0) dAngle += 180.;
dAngle -= m_dAngleInit;
if (!(m_nState & GDK_CONTROL_MASK)) dAngle = rint(dAngle / 5) * 5;
}
if (dAngle < -180.) dAngle += 360.;
if (dAngle > 180.) dAngle -= 360.;
if (dAngle != m_dAngle) {
m_pData->RotateSelection (m_cx, m_cy, dAngle - m_dAngle);
m_dAngle = dAngle;
}
char tmp[32];
snprintf(tmp, sizeof(tmp) - 1, _("Orientation: %g"), dAngle);
m_pApp->SetStatusText(tmp);
} else
m_pData->MoveSelectedItems(dx, dy);
}
else
{
if (m_pItem)
{
gnome_canvas_item_get_bounds(m_pItem, &x1, &y1, &x2, &y2);
g_object_set(G_OBJECT(m_pItem), "x2", m_x, "y2", m_y, NULL);
gnome_canvas_request_redraw(GNOME_CANVAS(m_pWidget), (int)x1, (int)y1, (int)x2, (int)y2);
}
else
{
gcpTheme *pTheme = m_pView->GetDoc ()->GetTheme ();
m_pItem = gnome_canvas_item_new(
m_pData->Group,
gnome_canvas_rect_get_type(),
"x1", m_x0,
"y1", m_y0,
"x2", m_x,
"y2", m_y,
"outline_color", SelectColor,
"width_units", pTheme->GetBondWidth (),
NULL);
}
}
}
void gcpSelectionTool::OnRelease()
{
m_pApp->ClearStatus();
if (m_pObject)
{
if (m_bRotate) {
std::list<Object*>::iterator i;
gcpDocument* pDoc = m_pView->GetDoc();
for (i = m_pData->SelectedObjects.begin(); i != m_pData->SelectedObjects.end(); i++)
m_pOp->AddObject(*i,1);
pDoc->FinishOperation();
} else {
double dx = m_x1 - m_x0, dy = m_y1 - m_y0;
if (dx != 0.0 && dy != 0.0)
{
m_pData->MoveSelectedItems(-dx, -dy);
m_pData->MoveSelection(dx, dy);
}
}
}
else
{
if (m_x < m_x0)
{
m_x1 = m_x0;
m_x0 = m_x;
}
else m_x1 = m_x;
if (m_y < m_y0)
{
m_y1 = m_y0;
m_y0 = m_y;
}
else m_y1 = m_y;
double x0, y0, x1, y1;
std::map<Object*, GnomeCanvasGroup*>::iterator j;
for (j = m_pData->Items.begin (); j != m_pData->Items.end (); j++) {
if (!m_pData->IsSelected ((*j).first)) {
gnome_canvas_item_get_bounds(GNOME_CANVAS_ITEM((*j).second), &x0, &y0,&x1, &y1);
if ((x0 < m_x1) && (y0 < m_y1) && (x1 > m_x0) && (y1 > m_y0)) {
m_pObject = (*j).first->GetGroup (); //GetMolecule();
if (m_pObject) {
if (!m_pData->IsSelected(m_pObject))
m_pData->SetSelected(m_pObject);
}
else
m_pData->SetSelected((*j).first);
}
}
}
}
AddSelection(m_pData);
}
void gcpSelectionTool::Activate()
{
if (m_UIManager)
gtk_widget_set_sensitive (m_MergeBtn, false);
gcpDocument *pDoc = m_pApp->GetActiveDocument ();
if (pDoc) {
m_pView = m_pApp->GetActiveDocument ()->GetView ();
GtkWidget *w = m_pView->GetWidget ();
m_pData = (gcpWidgetData*) g_object_get_data (G_OBJECT (w), "data");
}
}
bool gcpSelectionTool::Deactivate()
{
while (!SelectedWidgets.empty())
{
SelectedWidgets.front()->UnselectAll();
SelectedWidgets.pop_front();
}
return true;
}
void gcpSelectionTool::AddSelection(gcpWidgetData* data)
{
gcpWidgetData *d = m_pData;
m_pData = data;
m_pView = data->View;
gcpWindow *win = m_pView->GetDoc ()->GetWindow ();
if (m_pData->HasSelection())
{
GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
m_pView->OnCopySelection (m_pData->Canvas, clipboard);
win->ActivateActionWidget ("/MainMenu/EditMenu/Copy", true);
win->ActivateActionWidget ("/MainMenu/EditMenu/Cut", true);
win->ActivateActionWidget ("/MainMenu/EditMenu/Erase", true);
}
SelectedWidgets.remove(m_pData);
SelectedWidgets.push_front(m_pData);
if (d) {
m_pView = d->View;
m_pData = d;
}
// If the selection is made of two molecules, activate the merge tool
if (m_UIManager)
gtk_widget_set_sensitive (m_MergeBtn, ((m_pData->SelectedObjects.size () == 2) &&
(m_pData->SelectedObjects.front()->GetType() == MoleculeType) &&
(m_pData->SelectedObjects.back()->GetType() == MoleculeType)));
}
void gcpSelectionTool::OnFlip (bool horizontal)
{
if (!m_pData) {
m_pView = m_pApp->GetActiveDocument ()->GetView ();
GtkWidget *w = m_pView->GetWidget ();
m_pData = (gcpWidgetData*) g_object_get_data (G_OBJECT (w), "data");
}
if (!m_pData->SelectedObjects.size ())
return;
ArtDRect rect;
m_pData->GetSelectionBounds(rect);
m_cx = (rect.x0 + rect.x1) / 2.;
m_cy = (rect.y0 + rect.y1) / 2.;
m_x = (horizontal)? -1.: 1.;
Matrix2D m(m_x, 0., 0., -m_x);
std::list<Object*>::iterator i;
gcpDocument* pDoc = m_pView->GetDoc();
gcpTheme *pTheme = pDoc->GetTheme ();
m_pOp = pDoc-> GetNewOperation(GCP_MODIFY_OPERATION);
for (i = m_pData->SelectedObjects.begin(); i != m_pData->SelectedObjects.end(); i++) {
m_pOp->AddObject(*i,0);
(*i)->Transform2D (m, m_cx / pTheme->GetZoomFactor (), m_cy / pTheme->GetZoomFactor ());
m_pView->Update(*i);
m_pOp->AddObject(*i,1);
}
pDoc->FinishOperation();
}
void gcpSelectionTool::Rotate (bool rotate)
{
m_bRotate = rotate;
}
void gcpSelectionTool::Merge ()
{
gcpMolecule *pMol0, *pMol1;
gcpDocument* pDoc = m_pApp->GetActiveDocument ();
if (!m_pData) {
m_pView = pDoc->GetView ();
GtkWidget *w = m_pView->GetWidget ();
m_pData = (gcpWidgetData*) g_object_get_data (G_OBJECT (w), "data");
}
pMol0 = (gcpMolecule*) m_pData->SelectedObjects.front();
pMol1 = (gcpMolecule*) m_pData->SelectedObjects.back();
m_pOp = pDoc-> GetNewOperation(GCP_MODIFY_OPERATION);
m_pOp->AddObject (pMol0, 0);
m_pOp->AddObject (pMol1, 0);
m_pData->UnselectAll ();
if (pMol0->Merge (pMol1, true)) {
m_pOp->AddObject (pMol0, 1);
m_pData->SetSelected (pMol0);
AddSelection(m_pData);
m_pView->Update (pMol0);
pDoc->FinishOperation();
} else {
pDoc->AbortOperation ();
}
}
static void on_create_group (gcpSelectionTool* tool)
{
tool->CreateGroup ();
}
static void on_group (gcpSelectionTool* tool)
{
tool->Group ();
}
void gcpSelectionTool::Group ()
{
gcpDocument *pDoc = m_pView->GetDoc ();
new gcpGroupDlg (pDoc, NULL);
}
void gcpSelectionTool::CreateGroup ()
{
gcpDocument *pDoc = m_pView->GetDoc ();
Object *pObj = Object::CreateObject (Object::GetTypeName (m_Type), pDoc);
try {
m_pOp = pDoc-> GetNewOperation(GCP_MODIFY_OPERATION);
std::list<Object*>::iterator i;
for (i = m_pData->SelectedObjects.begin(); i != m_pData->SelectedObjects.end(); i++)
m_pOp->AddObject(*i,0);
if (!pObj->Build (m_pData->SelectedObjects))
throw logic_error (_("Creation failed!"));
m_pView->Update (pObj);
m_pData->UnselectAll ();
m_pData->SetSelected (pObj);
AddSelection(m_pData);
m_pOp->AddObject (pObj, 1);
pDoc->FinishOperation ();
}
catch (invalid_argument& e) {
pDoc->AbortOperation ();
delete pObj;
GtkWidget* message = gtk_message_dialog_new (NULL, (GtkDialogFlags) 0,
GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, e.what ());
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);
}
}
bool gcpSelectionTool::OnRightButtonClicked (GtkUIManager *UIManager)
{
// first destroy the GtkUIManager
if (m_pData->SelectedObjects.size () > 1) {
GtkActionGroup *group = gtk_action_group_new ("selection");
GtkAction *action = gtk_action_new ("group", _("Group and/or align objects"), NULL, NULL);
gtk_action_group_add_action (group, action);
m_uiIds.push_front (gtk_ui_manager_add_ui_from_string (UIManager, "<ui><popup><menuitem action='group'/></popup></ui>", -1, NULL));
g_signal_connect_swapped (action, "activate", G_CALLBACK (on_group), this);
set<TypeId> possible_types, types, wrong_types;
list<Object*>::iterator i = m_pData->SelectedObjects.begin (),
end = m_pData->SelectedObjects.end ();
(*i)->GetPossibleAncestorTypes (possible_types);
set<TypeId>::iterator type;
for (i++; i != end; i++) {
(*i)->GetPossibleAncestorTypes (types);
for (type = possible_types.begin(); type != possible_types.end (); type++)
if (types.find (*type) == types.end ())
wrong_types.insert (*type);
for (type = wrong_types.begin(); type != wrong_types.end (); type++)
possible_types.erase (*type);
wrong_types.clear ();
types.clear ();
}
if (possible_types.size () == 1) {
// Add a new action.
m_Type = *possible_types.begin();
const string &label = Object::GetCreationLabel (m_Type);
if (label.size ()) {
action = gtk_action_new ("create_group", label.c_str (), NULL, NULL);
gtk_action_group_add_action (group, action);
char buf[] = "<ui><popup><menuitem action='create_group'/></popup></ui>";
m_uiIds.push_front (gtk_ui_manager_add_ui_from_string (UIManager, buf, -1, NULL));
g_signal_connect_swapped (action, "activate", G_CALLBACK (on_create_group), this);
}
}
gtk_ui_manager_insert_action_group (UIManager, group, 0);
return true;
}
return false;
}
static GtkActionEntry entries[] = {
{ "HorizFlip", "gcp_Horiz", N_("Horizontal flip"), NULL,
N_("Flip the selection horizontally"), G_CALLBACK (on_flip) },
{ "VertFlip", "gcp_Vert", N_("Vertical flip"), NULL,
N_("Flip the selection vertically"), G_CALLBACK (on_flip) },
{ "Merge", "gcp_Merge", N_("Merge"), NULL,
N_("Merge two molecules"), G_CALLBACK (on_merge) }
};
static GtkToggleActionEntry toggles[] = {
{ "Rotate", "gcp_Rotate", N_("_Rotate"), NULL,
N_("Rotate the selection"), G_CALLBACK (on_rotate), false }
};
static const char *ui_description =
"<ui>"
" <toolbar name='Selection'>"
" <toolitem action='HorizFlip'/>"
" <toolitem action='VertFlip'/>"
" <toolitem action='Rotate'/>"
" <toolitem action='Merge'/>"
" </toolbar>"
"</ui>";
GtkWidget *gcpSelectionTool::GetPropertyPage ()
{
GtkWidget *box, *w;
GtkActionGroup *action_group;
GError *error;
box = gtk_vbox_new (FALSE, 0);
action_group = gtk_action_group_new ("SelectionToolActions");
gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), m_pApp);
gtk_action_group_add_toggle_actions (action_group, toggles, G_N_ELEMENTS (toggles), m_pApp);
m_UIManager = gtk_ui_manager_new ();
if (!gtk_ui_manager_add_ui_from_string (m_UIManager, ui_description, -1, &error))
{
g_message ("building property page failed: %s", error->message);
g_error_free (error);
gtk_widget_destroy (box);
g_object_unref (m_UIManager);
m_UIManager = NULL;
return NULL;;
}
gtk_ui_manager_insert_action_group (m_UIManager, action_group, 0);
w = gtk_ui_manager_get_widget (m_UIManager, "/Selection");
gtk_toolbar_set_style (GTK_TOOLBAR (w), GTK_TOOLBAR_ICONS);
gtk_toolbar_set_show_arrow (GTK_TOOLBAR (w), false);
gtk_toolbar_set_tooltips (GTK_TOOLBAR (w), true);
gtk_box_pack_start (GTK_BOX (box), w, false, false, 0);
gtk_widget_show_all (box);
m_MergeBtn = gtk_ui_manager_get_widget (m_UIManager, "/Selection/Merge");
gtk_widget_set_sensitive (m_MergeBtn, false);
return box;
}
char const *gcpSelectionTool::GetHelpTag ()
{
if (m_bRotate)
return "rotate";
return "selection";
}
syntax highlighted by Code2HTML, v. 0.9.1