On tool change, we used to simply halt tools before switching to the new one, which meant losing ongoing live-previewed tool changes, like transforms, warps and color corrections. This change makes them being applied to the image instead before switching to the new tool: Add enum value GIMP_TOOL_ACTION_COMMIT that is passed to GimpTool::control() before tool switching. Handle the new enum value in all tools, and actually commit the previewed stuff. This changes the behavior of GimpCageTool, GimpImageMapTool, GimpTransformTool and GimpWarpTool.
1990 lines
64 KiB
C
1990 lines
64 KiB
C
/* GIMP - The GNU Image Manipulation Program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* GimpTextTool
|
|
* Copyright (C) 2002-2010 Sven Neumann <sven@gimp.org>
|
|
* Daniel Eddeland <danedde@svn.gnome.org>
|
|
* Michael Natterer <mitch@gimp.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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <gegl.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
#include "libgimpconfig/gimpconfig.h"
|
|
#include "libgimpwidgets/gimpwidgets.h"
|
|
|
|
#include "tools-types.h"
|
|
|
|
#include "core/gimp.h"
|
|
#include "core/gimpcontext.h"
|
|
#include "core/gimpimage.h"
|
|
#include "core/gimpimage-pick-layer.h"
|
|
#include "core/gimpimage-undo.h"
|
|
#include "core/gimpimage-undo-push.h"
|
|
#include "core/gimplayer-floating-sel.h"
|
|
#include "core/gimpmarshal.h"
|
|
#include "core/gimptoolinfo.h"
|
|
#include "core/gimpundostack.h"
|
|
|
|
#include "text/gimptext.h"
|
|
#include "text/gimptext-vectors.h"
|
|
#include "text/gimptextlayer.h"
|
|
#include "text/gimptextlayout.h"
|
|
#include "text/gimptextundo.h"
|
|
|
|
#include "vectors/gimpvectors-warp.h"
|
|
|
|
#include "widgets/gimpdialogfactory.h"
|
|
#include "widgets/gimphelp-ids.h"
|
|
#include "widgets/gimpmenufactory.h"
|
|
#include "widgets/gimptextbuffer.h"
|
|
#include "widgets/gimpuimanager.h"
|
|
#include "widgets/gimpviewabledialog.h"
|
|
|
|
#include "display/gimpcanvasgroup.h"
|
|
#include "display/gimpdisplay.h"
|
|
#include "display/gimpdisplayshell.h"
|
|
|
|
#include "gimprectangletool.h"
|
|
#include "gimptextoptions.h"
|
|
#include "gimptexttool.h"
|
|
#include "gimptexttool-editor.h"
|
|
#include "gimptoolcontrol.h"
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
|
|
#define TEXT_UNDO_TIMEOUT 3
|
|
|
|
|
|
/* local function prototypes */
|
|
|
|
static void gimp_text_tool_rectangle_tool_iface_init (GimpRectangleToolInterface *iface);
|
|
|
|
static void gimp_text_tool_constructed (GObject *object);
|
|
static void gimp_text_tool_finalize (GObject *object);
|
|
|
|
static void gimp_text_tool_control (GimpTool *tool,
|
|
GimpToolAction action,
|
|
GimpDisplay *display);
|
|
static void gimp_text_tool_button_press (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
GimpButtonPressType press_type,
|
|
GimpDisplay *display);
|
|
static void gimp_text_tool_button_release (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
GimpButtonReleaseType release_type,
|
|
GimpDisplay *display);
|
|
static void gimp_text_tool_motion (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
GimpDisplay *display);
|
|
static gboolean gimp_text_tool_key_press (GimpTool *tool,
|
|
GdkEventKey *kevent,
|
|
GimpDisplay *display);
|
|
static gboolean gimp_text_tool_key_release (GimpTool *tool,
|
|
GdkEventKey *kevent,
|
|
GimpDisplay *display);
|
|
static void gimp_text_tool_oper_update (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
GdkModifierType state,
|
|
gboolean proximity,
|
|
GimpDisplay *display);
|
|
static void gimp_text_tool_cursor_update (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
GdkModifierType state,
|
|
GimpDisplay *display);
|
|
static GimpUIManager * gimp_text_tool_get_popup (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
GdkModifierType state,
|
|
GimpDisplay *display,
|
|
const gchar **ui_path);
|
|
|
|
static void gimp_text_tool_draw (GimpDrawTool *draw_tool);
|
|
static void gimp_text_tool_draw_selection (GimpDrawTool *draw_tool);
|
|
|
|
static void gimp_text_tool_frame_item (GimpTextTool *text_tool);
|
|
|
|
static gboolean gimp_text_tool_rectangle_change_complete
|
|
(GimpRectangleTool *rect_tool);
|
|
|
|
static void gimp_text_tool_connect (GimpTextTool *text_tool,
|
|
GimpTextLayer *layer,
|
|
GimpText *text);
|
|
|
|
static void gimp_text_tool_layer_notify (GimpTextLayer *layer,
|
|
const GParamSpec *pspec,
|
|
GimpTextTool *text_tool);
|
|
static void gimp_text_tool_proxy_notify (GimpText *text,
|
|
const GParamSpec *pspec,
|
|
GimpTextTool *text_tool);
|
|
|
|
static void gimp_text_tool_text_notify (GimpText *text,
|
|
const GParamSpec *pspec,
|
|
GimpTextTool *text_tool);
|
|
static void gimp_text_tool_text_changed (GimpText *text,
|
|
GimpTextTool *text_tool);
|
|
|
|
static gboolean gimp_text_tool_apply (GimpTextTool *text_tool,
|
|
gboolean push_undo);
|
|
|
|
static void gimp_text_tool_create_layer (GimpTextTool *text_tool,
|
|
GimpText *text);
|
|
|
|
static void gimp_text_tool_layer_changed (GimpImage *image,
|
|
GimpTextTool *text_tool);
|
|
static void gimp_text_tool_set_image (GimpTextTool *text_tool,
|
|
GimpImage *image);
|
|
static gboolean gimp_text_tool_set_drawable (GimpTextTool *text_tool,
|
|
GimpDrawable *drawable,
|
|
gboolean confirm);
|
|
|
|
static void gimp_text_tool_block_drawing (GimpTextTool *text_tool);
|
|
static void gimp_text_tool_unblock_drawing (GimpTextTool *text_tool);
|
|
|
|
static void gimp_text_tool_buffer_begin_edit (GimpTextBuffer *buffer,
|
|
GimpTextTool *text_tool);
|
|
static void gimp_text_tool_buffer_end_edit (GimpTextBuffer *buffer,
|
|
GimpTextTool *text_tool);
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GimpTextTool, gimp_text_tool,
|
|
GIMP_TYPE_DRAW_TOOL,
|
|
G_IMPLEMENT_INTERFACE (GIMP_TYPE_RECTANGLE_TOOL,
|
|
gimp_text_tool_rectangle_tool_iface_init))
|
|
|
|
#define parent_class gimp_text_tool_parent_class
|
|
|
|
|
|
void
|
|
gimp_text_tool_register (GimpToolRegisterCallback callback,
|
|
gpointer data)
|
|
{
|
|
(* callback) (GIMP_TYPE_TEXT_TOOL,
|
|
GIMP_TYPE_TEXT_OPTIONS,
|
|
gimp_text_options_gui,
|
|
GIMP_CONTEXT_FOREGROUND_MASK |
|
|
GIMP_CONTEXT_FONT_MASK |
|
|
GIMP_CONTEXT_PALETTE_MASK /* for the color popup's palette tab */,
|
|
"gimp-text-tool",
|
|
_("Text"),
|
|
_("Text Tool: Create or edit text layers"),
|
|
N_("Te_xt"), "T",
|
|
NULL, GIMP_HELP_TOOL_TEXT,
|
|
GIMP_STOCK_TOOL_TEXT,
|
|
data);
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_class_init (GimpTextToolClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
|
|
GimpDrawToolClass *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);
|
|
|
|
object_class->constructed = gimp_text_tool_constructed;
|
|
object_class->finalize = gimp_text_tool_finalize;
|
|
object_class->set_property = gimp_rectangle_tool_set_property;
|
|
object_class->get_property = gimp_rectangle_tool_get_property;
|
|
|
|
tool_class->control = gimp_text_tool_control;
|
|
tool_class->button_press = gimp_text_tool_button_press;
|
|
tool_class->motion = gimp_text_tool_motion;
|
|
tool_class->button_release = gimp_text_tool_button_release;
|
|
tool_class->key_press = gimp_text_tool_key_press;
|
|
tool_class->key_release = gimp_text_tool_key_release;
|
|
tool_class->oper_update = gimp_text_tool_oper_update;
|
|
tool_class->cursor_update = gimp_text_tool_cursor_update;
|
|
tool_class->get_popup = gimp_text_tool_get_popup;
|
|
|
|
draw_tool_class->draw = gimp_text_tool_draw;
|
|
|
|
gimp_rectangle_tool_install_properties (object_class);
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_rectangle_tool_iface_init (GimpRectangleToolInterface *iface)
|
|
{
|
|
iface->execute = NULL;
|
|
iface->cancel = NULL;
|
|
iface->rectangle_change_complete = gimp_text_tool_rectangle_change_complete;
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_init (GimpTextTool *text_tool)
|
|
{
|
|
GimpTool *tool = GIMP_TOOL (text_tool);
|
|
|
|
text_tool->proxy = NULL;
|
|
text_tool->pending = NULL;
|
|
text_tool->idle_id = 0;
|
|
|
|
text_tool->text = NULL;
|
|
text_tool->layer = NULL;
|
|
text_tool->image = NULL;
|
|
text_tool->layout = NULL;
|
|
|
|
text_tool->buffer = gimp_text_buffer_new ();
|
|
|
|
g_signal_connect (text_tool->buffer, "begin-user-action",
|
|
G_CALLBACK (gimp_text_tool_buffer_begin_edit),
|
|
text_tool);
|
|
g_signal_connect (text_tool->buffer, "end-user-action",
|
|
G_CALLBACK (gimp_text_tool_buffer_end_edit),
|
|
text_tool);
|
|
|
|
text_tool->handle_rectangle_change_complete = TRUE;
|
|
|
|
gimp_text_tool_editor_init (text_tool);
|
|
|
|
gimp_tool_control_set_scroll_lock (tool->control, TRUE);
|
|
gimp_tool_control_set_handle_empty_image (tool->control, TRUE);
|
|
gimp_tool_control_set_wants_click (tool->control, TRUE);
|
|
gimp_tool_control_set_wants_double_click (tool->control, TRUE);
|
|
gimp_tool_control_set_wants_triple_click (tool->control, TRUE);
|
|
gimp_tool_control_set_wants_all_key_events (tool->control, TRUE);
|
|
gimp_tool_control_set_precision (tool->control,
|
|
GIMP_CURSOR_PRECISION_PIXEL_BORDER);
|
|
gimp_tool_control_set_tool_cursor (tool->control,
|
|
GIMP_TOOL_CURSOR_TEXT);
|
|
gimp_tool_control_set_action_object_1 (tool->control,
|
|
"context/context-font-select-set");
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_constructed (GObject *object)
|
|
{
|
|
GimpTextTool *text_tool = GIMP_TEXT_TOOL (object);
|
|
GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
|
|
|
|
G_OBJECT_CLASS (parent_class)->constructed (object);
|
|
|
|
gimp_rectangle_tool_constructor (object);
|
|
|
|
text_tool->proxy = g_object_new (GIMP_TYPE_TEXT, NULL);
|
|
|
|
gimp_text_options_connect_text (options, text_tool->proxy);
|
|
|
|
g_signal_connect_object (text_tool->proxy, "notify",
|
|
G_CALLBACK (gimp_text_tool_proxy_notify),
|
|
text_tool, 0);
|
|
|
|
g_object_set (options,
|
|
"highlight", FALSE,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_finalize (GObject *object)
|
|
{
|
|
GimpTextTool *text_tool = GIMP_TEXT_TOOL (object);
|
|
|
|
if (text_tool->proxy)
|
|
{
|
|
g_object_unref (text_tool->proxy);
|
|
text_tool->proxy = NULL;
|
|
}
|
|
|
|
if (text_tool->buffer)
|
|
{
|
|
g_object_unref (text_tool->buffer);
|
|
text_tool->buffer = NULL;
|
|
}
|
|
|
|
gimp_text_tool_editor_finalize (text_tool);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_control (GimpTool *tool,
|
|
GimpToolAction action,
|
|
GimpDisplay *display)
|
|
{
|
|
GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
|
|
|
|
switch (action)
|
|
{
|
|
case GIMP_TOOL_ACTION_PAUSE:
|
|
case GIMP_TOOL_ACTION_RESUME:
|
|
break;
|
|
|
|
case GIMP_TOOL_ACTION_HALT:
|
|
gimp_text_tool_editor_halt (text_tool);
|
|
gimp_text_tool_set_drawable (text_tool, NULL, FALSE);
|
|
break;
|
|
|
|
case GIMP_TOOL_ACTION_COMMIT:
|
|
break;
|
|
}
|
|
|
|
gimp_rectangle_tool_control (tool, action, display);
|
|
|
|
GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_button_press (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
GimpButtonPressType press_type,
|
|
GimpDisplay *display)
|
|
{
|
|
GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
|
|
GimpRectangleTool *rect_tool = GIMP_RECTANGLE_TOOL (tool);
|
|
GimpImage *image = gimp_display_get_image (display);
|
|
GimpText *text = text_tool->text;
|
|
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
|
|
|
|
if (tool->display && tool->display != display)
|
|
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
|
|
|
|
if (press_type == GIMP_BUTTON_PRESS_NORMAL)
|
|
{
|
|
gimp_tool_control_activate (tool->control);
|
|
|
|
gimp_text_tool_reset_im_context (text_tool);
|
|
|
|
text_tool->selecting = FALSE;
|
|
|
|
if (gimp_rectangle_tool_point_in_rectangle (rect_tool,
|
|
coords->x,
|
|
coords->y) &&
|
|
! text_tool->moving)
|
|
{
|
|
gimp_rectangle_tool_set_function (rect_tool, GIMP_RECTANGLE_TOOL_DEAD);
|
|
}
|
|
else
|
|
{
|
|
gimp_rectangle_tool_button_press (tool, coords, time, state, display);
|
|
}
|
|
|
|
/* bail out now if the user user clicked on a handle of an
|
|
* existing rectangle, but not inside an existing framed layer
|
|
*/
|
|
if (gimp_rectangle_tool_get_function (rect_tool) !=
|
|
GIMP_RECTANGLE_TOOL_CREATING)
|
|
{
|
|
if (text_tool->layer)
|
|
{
|
|
GimpItem *item = GIMP_ITEM (text_tool->layer);
|
|
gdouble x = coords->x - gimp_item_get_offset_x (item);
|
|
gdouble y = coords->y - gimp_item_get_offset_y (item);
|
|
|
|
if (x < 0 || x >= gimp_item_get_width (item) ||
|
|
y < 0 || y >= gimp_item_get_height (item))
|
|
{
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* if the the click is not related to the currently edited text
|
|
* layer in any way, try to pick a text layer
|
|
*/
|
|
if (! text_tool->moving &&
|
|
gimp_rectangle_tool_get_function (rect_tool) ==
|
|
GIMP_RECTANGLE_TOOL_CREATING)
|
|
{
|
|
GimpTextLayer *text_layer;
|
|
|
|
text_layer = gimp_image_pick_text_layer (image, coords->x, coords->y);
|
|
|
|
if (text_layer && text_layer != text_tool->layer)
|
|
{
|
|
if (text_tool->image == image)
|
|
g_signal_handlers_block_by_func (image,
|
|
gimp_text_tool_layer_changed,
|
|
text_tool);
|
|
|
|
gimp_image_set_active_layer (image, GIMP_LAYER (text_layer));
|
|
|
|
if (text_tool->image == image)
|
|
g_signal_handlers_unblock_by_func (image,
|
|
gimp_text_tool_layer_changed,
|
|
text_tool);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gimp_image_coords_in_active_pickable (image, coords, FALSE, FALSE))
|
|
{
|
|
GimpDrawable *drawable = gimp_image_get_active_drawable (image);
|
|
GimpItem *item = GIMP_ITEM (drawable);
|
|
gdouble x = coords->x - gimp_item_get_offset_x (item);
|
|
gdouble y = coords->y - gimp_item_get_offset_y (item);
|
|
|
|
/* did the user click on a text layer? */
|
|
if (gimp_text_tool_set_drawable (text_tool, drawable, TRUE))
|
|
{
|
|
if (press_type == GIMP_BUTTON_PRESS_NORMAL)
|
|
{
|
|
/* if we clicked on a text layer while the tool was idle
|
|
* (didn't show a rectangle), frame the layer and switch to
|
|
* selecting instead of drawing a new rectangle
|
|
*/
|
|
if (gimp_rectangle_tool_get_function (rect_tool) ==
|
|
GIMP_RECTANGLE_TOOL_CREATING)
|
|
{
|
|
gimp_rectangle_tool_set_function (rect_tool,
|
|
GIMP_RECTANGLE_TOOL_DEAD);
|
|
|
|
gimp_text_tool_frame_item (text_tool);
|
|
}
|
|
|
|
if (text_tool->text && text_tool->text != text)
|
|
{
|
|
gimp_text_tool_editor_start (text_tool);
|
|
}
|
|
}
|
|
|
|
if (text_tool->text && ! text_tool->moving)
|
|
{
|
|
text_tool->selecting = TRUE;
|
|
|
|
gimp_text_tool_editor_button_press (text_tool, x, y, press_type);
|
|
}
|
|
else
|
|
{
|
|
text_tool->selecting = FALSE;
|
|
}
|
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (press_type == GIMP_BUTTON_PRESS_NORMAL)
|
|
{
|
|
/* create a new text layer */
|
|
text_tool->text_box_fixed = FALSE;
|
|
|
|
gimp_text_tool_connect (text_tool, NULL, NULL);
|
|
gimp_text_tool_editor_start (text_tool);
|
|
}
|
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_button_release (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
GimpButtonReleaseType release_type,
|
|
GimpDisplay *display)
|
|
{
|
|
GimpRectangleTool *rect_tool = GIMP_RECTANGLE_TOOL (tool);
|
|
GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
|
|
|
|
gimp_tool_control_halt (tool->control);
|
|
|
|
if (text_tool->selecting)
|
|
{
|
|
/* we are in a selection process (user has initially clicked on
|
|
* an existing text layer), so finish the selection process and
|
|
* ignore rectangle-change-complete.
|
|
*/
|
|
|
|
/* need to block "end-user-action" on the text buffer, because
|
|
* GtkTextBuffer considers copying text to the clipboard an
|
|
* undo-relevant user action, which is clearly a bug, but what
|
|
* can we do...
|
|
*/
|
|
g_signal_handlers_block_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_begin_edit,
|
|
text_tool);
|
|
g_signal_handlers_block_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_end_edit,
|
|
text_tool);
|
|
|
|
gimp_text_tool_editor_button_release (text_tool);
|
|
|
|
g_signal_handlers_unblock_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_end_edit,
|
|
text_tool);
|
|
g_signal_handlers_unblock_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_begin_edit,
|
|
text_tool);
|
|
|
|
text_tool->selecting = FALSE;
|
|
|
|
text_tool->handle_rectangle_change_complete = FALSE;
|
|
|
|
/* there is no cancelling of selections yet */
|
|
if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
|
|
release_type = GIMP_BUTTON_RELEASE_NORMAL;
|
|
}
|
|
else if (gimp_rectangle_tool_get_function (rect_tool) ==
|
|
GIMP_RECTANGLE_TOOL_DEAD)
|
|
{
|
|
/* the user clicked in dead space (like between the corner and
|
|
* edge handles, so completely ignore that.
|
|
*/
|
|
|
|
text_tool->handle_rectangle_change_complete = FALSE;
|
|
}
|
|
else if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
|
|
{
|
|
/* user has clicked outside of any text layer in order to
|
|
* create a new text, but cancelled the operation.
|
|
*/
|
|
|
|
gimp_text_tool_editor_halt (text_tool);
|
|
|
|
text_tool->handle_rectangle_change_complete = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gint x1, y1;
|
|
gint x2, y2;
|
|
|
|
/* otherwise the user has clicked outside of any text layer in
|
|
* order to create a new text, fall through and let
|
|
* rectangle-change-complete do its job of setting the new text
|
|
* layer's size.
|
|
*/
|
|
|
|
g_object_get (text_tool,
|
|
"x1", &x1,
|
|
"y1", &y1,
|
|
"x2", &x2,
|
|
"y2", &y2,
|
|
NULL);
|
|
|
|
if (release_type == GIMP_BUTTON_RELEASE_CLICK ||
|
|
(x2 - x1) < 3 ||
|
|
(y2 - y1) < 3)
|
|
{
|
|
/* unless the rectangle is unreasonably small to hold any
|
|
* real text (the user has eitherjust clicked or just made
|
|
* a rectangle of a few pixels), so set the text box to
|
|
* dynamic and ignore rectangle-change-complete.
|
|
*/
|
|
|
|
g_object_set (text_tool->proxy,
|
|
"box-mode", GIMP_TEXT_BOX_DYNAMIC,
|
|
NULL);
|
|
|
|
text_tool->handle_rectangle_change_complete = FALSE;
|
|
}
|
|
}
|
|
|
|
gimp_rectangle_tool_button_release (tool, coords, time, state,
|
|
release_type, display);
|
|
|
|
text_tool->handle_rectangle_change_complete = TRUE;
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_motion (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
GimpDisplay *display)
|
|
{
|
|
GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
|
|
|
|
if (! text_tool->selecting)
|
|
{
|
|
gimp_rectangle_tool_motion (tool, coords, time, state, display);
|
|
}
|
|
else
|
|
{
|
|
GimpItem *item = GIMP_ITEM (text_tool->layer);
|
|
gdouble x = coords->x - gimp_item_get_offset_x (item);
|
|
gdouble y = coords->y - gimp_item_get_offset_y (item);
|
|
|
|
gimp_text_tool_editor_motion (text_tool, x, y);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gimp_text_tool_key_press (GimpTool *tool,
|
|
GdkEventKey *kevent,
|
|
GimpDisplay *display)
|
|
{
|
|
GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
|
|
|
|
if (display == tool->display)
|
|
return gimp_text_tool_editor_key_press (text_tool, kevent);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
gimp_text_tool_key_release (GimpTool *tool,
|
|
GdkEventKey *kevent,
|
|
GimpDisplay *display)
|
|
{
|
|
GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
|
|
|
|
if (display == tool->display)
|
|
return gimp_text_tool_editor_key_release (text_tool, kevent);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_oper_update (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
GdkModifierType state,
|
|
gboolean proximity,
|
|
GimpDisplay *display)
|
|
{
|
|
GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
|
|
GimpRectangleTool *rect_tool = GIMP_RECTANGLE_TOOL (tool);
|
|
|
|
gimp_rectangle_tool_oper_update (tool, coords, state, proximity, display);
|
|
|
|
text_tool->moving = (gimp_rectangle_tool_get_function (rect_tool) ==
|
|
GIMP_RECTANGLE_TOOL_MOVING &&
|
|
(state & GDK_MOD1_MASK));
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_cursor_update (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
GdkModifierType state,
|
|
GimpDisplay *display)
|
|
{
|
|
if (tool->display == display)
|
|
{
|
|
GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
|
|
|
|
if (gimp_rectangle_tool_point_in_rectangle (GIMP_RECTANGLE_TOOL (tool),
|
|
coords->x,
|
|
coords->y) &&
|
|
! text_tool->moving)
|
|
{
|
|
gimp_tool_control_set_cursor (tool->control, GDK_XTERM);
|
|
gimp_tool_control_set_cursor_modifier (tool->control,
|
|
GIMP_CURSOR_MODIFIER_NONE);
|
|
}
|
|
else
|
|
{
|
|
gimp_rectangle_tool_cursor_update (tool, coords, state, display);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gimp_tool_control_set_cursor (tool->control, GDK_XTERM);
|
|
gimp_tool_control_set_cursor_modifier (tool->control,
|
|
GIMP_CURSOR_MODIFIER_NONE);
|
|
}
|
|
|
|
GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
|
|
}
|
|
|
|
static GimpUIManager *
|
|
gimp_text_tool_get_popup (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
GdkModifierType state,
|
|
GimpDisplay *display,
|
|
const gchar **ui_path)
|
|
{
|
|
GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
|
|
|
|
if (gimp_rectangle_tool_point_in_rectangle (GIMP_RECTANGLE_TOOL (text_tool),
|
|
coords->x,
|
|
coords->y))
|
|
{
|
|
if (! text_tool->ui_manager)
|
|
{
|
|
GimpDialogFactory *dialog_factory;
|
|
GtkWidget *im_menu;
|
|
GList *children;
|
|
|
|
dialog_factory = gimp_dialog_factory_get_singleton ();
|
|
|
|
text_tool->ui_manager =
|
|
gimp_menu_factory_manager_new (gimp_dialog_factory_get_menu_factory (dialog_factory),
|
|
"<TextTool>",
|
|
text_tool, FALSE);
|
|
|
|
im_menu = gtk_ui_manager_get_widget (GTK_UI_MANAGER (text_tool->ui_manager),
|
|
"/text-tool-popup/text-tool-input-methods-menu");
|
|
|
|
if (GTK_IS_MENU_ITEM (im_menu))
|
|
im_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (im_menu));
|
|
|
|
/* hide the generated "empty" item */
|
|
children = gtk_container_get_children (GTK_CONTAINER (im_menu));
|
|
while (children)
|
|
{
|
|
gtk_widget_hide (children->data);
|
|
children = g_list_remove (children, children->data);
|
|
}
|
|
|
|
gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (text_tool->im_context),
|
|
GTK_MENU_SHELL (im_menu));
|
|
}
|
|
|
|
gimp_ui_manager_update (text_tool->ui_manager, text_tool);
|
|
|
|
*ui_path = "/text-tool-popup";
|
|
|
|
return text_tool->ui_manager;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_draw (GimpDrawTool *draw_tool)
|
|
{
|
|
GimpTextTool *text_tool = GIMP_TEXT_TOOL (draw_tool);
|
|
|
|
g_object_set (text_tool,
|
|
"narrow-mode", TRUE,
|
|
NULL);
|
|
|
|
gimp_rectangle_tool_draw (draw_tool, NULL);
|
|
|
|
if (! text_tool->text ||
|
|
! text_tool->layer ||
|
|
! text_tool->layer->text)
|
|
return;
|
|
|
|
gimp_text_tool_ensure_layout (text_tool);
|
|
|
|
if (gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (text_tool->buffer)))
|
|
{
|
|
/* If the text buffer has a selection, highlight the selected letters */
|
|
|
|
gimp_text_tool_draw_selection (draw_tool);
|
|
}
|
|
else
|
|
{
|
|
/* If the text buffer has no selection, draw the text cursor */
|
|
|
|
GimpCanvasItem *item;
|
|
PangoRectangle cursor_rect;
|
|
gint off_x, off_y;
|
|
gboolean overwrite;
|
|
|
|
gimp_text_tool_editor_get_cursor_rect (text_tool,
|
|
text_tool->overwrite_mode,
|
|
&cursor_rect);
|
|
|
|
gimp_item_get_offset (GIMP_ITEM (text_tool->layer), &off_x, &off_y);
|
|
cursor_rect.x += off_x;
|
|
cursor_rect.y += off_y;
|
|
|
|
overwrite = text_tool->overwrite_mode && cursor_rect.width != 0;
|
|
|
|
item = gimp_draw_tool_add_text_cursor (draw_tool, &cursor_rect,
|
|
overwrite);
|
|
gimp_canvas_item_set_highlight (item, TRUE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_draw_selection (GimpDrawTool *draw_tool)
|
|
{
|
|
GimpTextTool *text_tool = GIMP_TEXT_TOOL (draw_tool);
|
|
GtkTextBuffer *buffer = GTK_TEXT_BUFFER (text_tool->buffer);
|
|
GimpCanvasGroup *fill_group;
|
|
PangoLayout *layout;
|
|
gint offset_x;
|
|
gint offset_y;
|
|
gint off_x, off_y;
|
|
PangoLayoutIter *iter;
|
|
GtkTextIter sel_start, sel_end;
|
|
gint min, max;
|
|
gint i;
|
|
|
|
fill_group = gimp_draw_tool_add_fill_group (draw_tool);
|
|
gimp_canvas_item_set_highlight (GIMP_CANVAS_ITEM (fill_group), TRUE);
|
|
|
|
gtk_text_buffer_get_selection_bounds (buffer, &sel_start, &sel_end);
|
|
|
|
min = gimp_text_buffer_get_iter_index (text_tool->buffer, &sel_start, TRUE);
|
|
max = gimp_text_buffer_get_iter_index (text_tool->buffer, &sel_end, TRUE);
|
|
|
|
layout = gimp_text_layout_get_pango_layout (text_tool->layout);
|
|
|
|
gimp_text_layout_get_offsets (text_tool->layout, &offset_x, &offset_y);
|
|
|
|
gimp_item_get_offset (GIMP_ITEM (text_tool->layer), &off_x, &off_y);
|
|
offset_x += off_x;
|
|
offset_y += off_y;
|
|
|
|
iter = pango_layout_get_iter (layout);
|
|
|
|
gimp_draw_tool_push_group (draw_tool, fill_group);
|
|
|
|
do
|
|
{
|
|
if (! pango_layout_iter_get_run (iter))
|
|
continue;
|
|
|
|
i = pango_layout_iter_get_index (iter);
|
|
|
|
if (i >= min && i < max)
|
|
{
|
|
PangoRectangle rect;
|
|
gint ytop, ybottom;
|
|
|
|
pango_layout_iter_get_char_extents (iter, &rect);
|
|
pango_layout_iter_get_line_yrange (iter, &ytop, &ybottom);
|
|
|
|
rect.y = ytop;
|
|
rect.height = ybottom - ytop;
|
|
|
|
pango_extents_to_pixels (&rect, NULL);
|
|
|
|
gimp_text_layout_transform_rect (text_tool->layout, &rect);
|
|
|
|
rect.x += offset_x;
|
|
rect.y += offset_y;
|
|
|
|
gimp_draw_tool_add_rectangle (draw_tool, TRUE,
|
|
rect.x, rect.y,
|
|
rect.width, rect.height);
|
|
}
|
|
}
|
|
while (pango_layout_iter_next_char (iter));
|
|
|
|
gimp_draw_tool_pop_group (draw_tool);
|
|
|
|
pango_layout_iter_free (iter);
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_frame_item (GimpTextTool *text_tool)
|
|
{
|
|
g_return_if_fail (GIMP_IS_LAYER (text_tool->layer));
|
|
|
|
text_tool->handle_rectangle_change_complete = FALSE;
|
|
|
|
gimp_rectangle_tool_frame_item (GIMP_RECTANGLE_TOOL (text_tool),
|
|
GIMP_ITEM (text_tool->layer));
|
|
|
|
text_tool->handle_rectangle_change_complete = TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gimp_text_tool_rectangle_change_complete (GimpRectangleTool *rect_tool)
|
|
{
|
|
GimpTextTool *text_tool = GIMP_TEXT_TOOL (rect_tool);
|
|
|
|
gimp_text_tool_editor_position (text_tool);
|
|
|
|
if (text_tool->handle_rectangle_change_complete)
|
|
{
|
|
GimpItem *item = GIMP_ITEM (text_tool->layer);
|
|
gint x1, y1;
|
|
gint x2, y2;
|
|
|
|
if (! item)
|
|
{
|
|
/* we can't set properties for the text layer, because it
|
|
* isn't created until some text has been inserted, so we
|
|
* need to make a special note that will remind us what to
|
|
* do when we actually create the layer
|
|
*/
|
|
text_tool->text_box_fixed = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
g_object_get (rect_tool,
|
|
"x1", &x1,
|
|
"y1", &y1,
|
|
"x2", &x2,
|
|
"y2", &y2,
|
|
NULL);
|
|
|
|
if (x1 != gimp_item_get_offset_x (item) ||
|
|
y1 != gimp_item_get_offset_y (item) ||
|
|
(x2 - x1) != gimp_item_get_width (item) ||
|
|
(y2 - y1) != gimp_item_get_height (item))
|
|
{
|
|
GimpUnit box_unit = text_tool->proxy->box_unit;
|
|
gdouble xres, yres;
|
|
gboolean push_undo = TRUE;
|
|
GimpUndo *undo;
|
|
|
|
gimp_image_get_resolution (text_tool->image, &xres, &yres);
|
|
|
|
g_object_set (text_tool->proxy,
|
|
"box-mode", GIMP_TEXT_BOX_FIXED,
|
|
"box-width", gimp_pixels_to_units (x2 - x1,
|
|
box_unit, xres),
|
|
"box-height", gimp_pixels_to_units (y2 - y1,
|
|
box_unit, yres),
|
|
NULL);
|
|
|
|
undo = gimp_image_undo_can_compress (text_tool->image,
|
|
GIMP_TYPE_UNDO_STACK,
|
|
GIMP_UNDO_GROUP_TEXT);
|
|
|
|
if (undo &&
|
|
gimp_undo_get_age (undo) <= TEXT_UNDO_TIMEOUT &&
|
|
g_object_get_data (G_OBJECT (undo), "reshape-text-layer") == (gpointer) item)
|
|
push_undo = FALSE;
|
|
|
|
if (push_undo)
|
|
{
|
|
gimp_image_undo_group_start (text_tool->image, GIMP_UNDO_GROUP_TEXT,
|
|
_("Reshape Text Layer"));
|
|
|
|
undo = gimp_image_undo_can_compress (text_tool->image, GIMP_TYPE_UNDO_STACK,
|
|
GIMP_UNDO_GROUP_TEXT);
|
|
|
|
if (undo)
|
|
g_object_set_data (G_OBJECT (undo), "reshape-text-layer",
|
|
(gpointer) item);
|
|
}
|
|
|
|
gimp_item_translate (item,
|
|
x1 - gimp_item_get_offset_x (item),
|
|
y1 - gimp_item_get_offset_y (item),
|
|
push_undo);
|
|
gimp_text_tool_apply (text_tool, push_undo);
|
|
|
|
if (push_undo)
|
|
gimp_image_undo_group_end (text_tool->image);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_connect (GimpTextTool *text_tool,
|
|
GimpTextLayer *layer,
|
|
GimpText *text)
|
|
{
|
|
GimpTool *tool = GIMP_TOOL (text_tool);
|
|
|
|
g_return_if_fail (text == NULL || (layer != NULL && layer->text == text));
|
|
|
|
if (text_tool->text != text)
|
|
{
|
|
GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (tool);
|
|
|
|
g_signal_handlers_block_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_begin_edit,
|
|
text_tool);
|
|
g_signal_handlers_block_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_end_edit,
|
|
text_tool);
|
|
|
|
if (text_tool->text)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (text_tool->text,
|
|
gimp_text_tool_text_notify,
|
|
text_tool);
|
|
g_signal_handlers_disconnect_by_func (text_tool->text,
|
|
gimp_text_tool_text_changed,
|
|
text_tool);
|
|
|
|
if (text_tool->pending)
|
|
gimp_text_tool_apply (text_tool, TRUE);
|
|
|
|
g_object_unref (text_tool->text);
|
|
text_tool->text = NULL;
|
|
|
|
g_object_set (text_tool->proxy,
|
|
"text", NULL,
|
|
"markup", NULL,
|
|
NULL);
|
|
gimp_text_buffer_set_text (text_tool->buffer, NULL);
|
|
|
|
gimp_text_tool_clear_layout (text_tool);
|
|
}
|
|
|
|
gimp_context_define_property (GIMP_CONTEXT (options),
|
|
GIMP_CONTEXT_PROP_FOREGROUND,
|
|
text != NULL);
|
|
|
|
if (text)
|
|
{
|
|
if (text->unit != text_tool->proxy->unit)
|
|
gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (options->size_entry),
|
|
text->unit);
|
|
|
|
gimp_config_sync (G_OBJECT (text), G_OBJECT (text_tool->proxy), 0);
|
|
|
|
if (text->markup)
|
|
gimp_text_buffer_set_markup (text_tool->buffer, text->markup);
|
|
else
|
|
gimp_text_buffer_set_text (text_tool->buffer, text->text);
|
|
|
|
gimp_text_tool_clear_layout (text_tool);
|
|
|
|
text_tool->text = g_object_ref (text);
|
|
|
|
g_signal_connect (text, "notify",
|
|
G_CALLBACK (gimp_text_tool_text_notify),
|
|
text_tool);
|
|
g_signal_connect (text, "changed",
|
|
G_CALLBACK (gimp_text_tool_text_changed),
|
|
text_tool);
|
|
}
|
|
|
|
g_signal_handlers_unblock_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_end_edit,
|
|
text_tool);
|
|
g_signal_handlers_unblock_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_begin_edit,
|
|
text_tool);
|
|
}
|
|
|
|
if (text_tool->layer != layer)
|
|
{
|
|
if (text_tool->layer)
|
|
g_signal_handlers_disconnect_by_func (text_tool->layer,
|
|
gimp_text_tool_layer_notify,
|
|
text_tool);
|
|
|
|
text_tool->layer = layer;
|
|
|
|
if (layer)
|
|
g_signal_connect_object (text_tool->layer, "notify",
|
|
G_CALLBACK (gimp_text_tool_layer_notify),
|
|
text_tool, 0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_layer_notify (GimpTextLayer *layer,
|
|
const GParamSpec *pspec,
|
|
GimpTextTool *text_tool)
|
|
{
|
|
GimpTool *tool = GIMP_TOOL (text_tool);
|
|
|
|
if (! strcmp (pspec->name, "modified"))
|
|
{
|
|
if (layer->modified)
|
|
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
|
|
}
|
|
else if (! strcmp (pspec->name, "text"))
|
|
{
|
|
if (! layer->text)
|
|
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gimp_text_tool_apply_idle (gpointer text_tool)
|
|
{
|
|
return gimp_text_tool_apply (text_tool, TRUE);
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_proxy_notify (GimpText *text,
|
|
const GParamSpec *pspec,
|
|
GimpTextTool *text_tool)
|
|
{
|
|
if (! text_tool->text)
|
|
return;
|
|
|
|
if ((pspec->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE &&
|
|
pspec->owner_type == GIMP_TYPE_TEXT)
|
|
{
|
|
gimp_text_tool_block_drawing (text_tool);
|
|
|
|
text_tool->pending = g_list_append (text_tool->pending, (gpointer) pspec);
|
|
|
|
if (text_tool->idle_id)
|
|
g_source_remove (text_tool->idle_id);
|
|
|
|
text_tool->idle_id =
|
|
g_idle_add_full (G_PRIORITY_LOW,
|
|
gimp_text_tool_apply_idle, text_tool,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_text_notify (GimpText *text,
|
|
const GParamSpec *pspec,
|
|
GimpTextTool *text_tool)
|
|
{
|
|
g_return_if_fail (text == text_tool->text);
|
|
|
|
gimp_text_tool_block_drawing (text_tool);
|
|
|
|
if ((pspec->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE)
|
|
{
|
|
GValue value = { 0, };
|
|
|
|
g_value_init (&value, pspec->value_type);
|
|
|
|
g_object_get_property (G_OBJECT (text), pspec->name, &value);
|
|
|
|
g_signal_handlers_block_by_func (text_tool->proxy,
|
|
gimp_text_tool_proxy_notify,
|
|
text_tool);
|
|
|
|
g_object_set_property (G_OBJECT (text_tool->proxy), pspec->name, &value);
|
|
|
|
g_signal_handlers_unblock_by_func (text_tool->proxy,
|
|
gimp_text_tool_proxy_notify,
|
|
text_tool);
|
|
|
|
g_value_unset (&value);
|
|
}
|
|
|
|
/* if the text has changed, (probably because of an undo), we put
|
|
* the new text into the text buffer
|
|
*/
|
|
if (strcmp (pspec->name, "text") == 0 ||
|
|
strcmp (pspec->name, "markup") == 0)
|
|
{
|
|
g_signal_handlers_block_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_begin_edit,
|
|
text_tool);
|
|
g_signal_handlers_block_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_end_edit,
|
|
text_tool);
|
|
if (text->markup)
|
|
gimp_text_buffer_set_markup (text_tool->buffer, text->markup);
|
|
else
|
|
gimp_text_buffer_set_text (text_tool->buffer, text->text);
|
|
|
|
g_signal_handlers_unblock_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_end_edit,
|
|
text_tool);
|
|
g_signal_handlers_unblock_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_begin_edit,
|
|
text_tool);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_text_changed (GimpText *text,
|
|
GimpTextTool *text_tool)
|
|
{
|
|
/* we need to redraw the rectangle in any case because whatever
|
|
* changes to the text can change its size
|
|
*/
|
|
gimp_text_tool_frame_item (text_tool);
|
|
|
|
gimp_text_tool_unblock_drawing (text_tool);
|
|
}
|
|
|
|
static gboolean
|
|
gimp_text_tool_apply (GimpTextTool *text_tool,
|
|
gboolean push_undo)
|
|
{
|
|
const GParamSpec *pspec = NULL;
|
|
GimpImage *image;
|
|
GimpTextLayer *layer;
|
|
GObject *src;
|
|
GObject *dest;
|
|
GList *list;
|
|
gboolean undo_group = FALSE;
|
|
|
|
if (text_tool->idle_id)
|
|
{
|
|
g_source_remove (text_tool->idle_id);
|
|
text_tool->idle_id = 0;
|
|
}
|
|
|
|
g_return_val_if_fail (text_tool->text != NULL, FALSE);
|
|
g_return_val_if_fail (text_tool->layer != NULL, FALSE);
|
|
|
|
layer = text_tool->layer;
|
|
image = gimp_item_get_image (GIMP_ITEM (layer));
|
|
|
|
g_return_val_if_fail (layer->text == text_tool->text, FALSE);
|
|
|
|
/* Walk over the list of changes and figure out if we are changing
|
|
* a single property or need to push a full text undo.
|
|
*/
|
|
for (list = text_tool->pending;
|
|
list && list->next && list->next->data == list->data;
|
|
list = list->next)
|
|
/* do nothing */;
|
|
|
|
if (g_list_length (list) == 1)
|
|
pspec = list->data;
|
|
|
|
/* If we are changing a single property, we don't need to push
|
|
* an undo if all of the following is true:
|
|
* - the redo stack is empty
|
|
* - the last item on the undo stack is a text undo
|
|
* - the last undo changed the same text property on the same layer
|
|
* - the last undo happened less than TEXT_UNDO_TIMEOUT seconds ago
|
|
*/
|
|
if (pspec)
|
|
{
|
|
GimpUndo *undo = gimp_image_undo_can_compress (image, GIMP_TYPE_TEXT_UNDO,
|
|
GIMP_UNDO_TEXT_LAYER);
|
|
|
|
if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layer))
|
|
{
|
|
GimpTextUndo *text_undo = GIMP_TEXT_UNDO (undo);
|
|
|
|
if (text_undo->pspec == pspec)
|
|
{
|
|
if (gimp_undo_get_age (undo) < TEXT_UNDO_TIMEOUT)
|
|
{
|
|
GimpTool *tool = GIMP_TOOL (text_tool);
|
|
GimpContext *context;
|
|
|
|
context = GIMP_CONTEXT (gimp_tool_get_options (tool));
|
|
|
|
push_undo = FALSE;
|
|
gimp_undo_reset_age (undo);
|
|
gimp_undo_refresh_preview (undo, context);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (push_undo)
|
|
{
|
|
if (layer->modified)
|
|
{
|
|
undo_group = TRUE;
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TEXT, NULL);
|
|
|
|
gimp_image_undo_push_text_layer_modified (image, NULL, layer);
|
|
|
|
/* see comment in gimp_text_layer_set() */
|
|
gimp_image_undo_push_drawable_mod (image, NULL,
|
|
GIMP_DRAWABLE (layer), TRUE);
|
|
}
|
|
|
|
gimp_image_undo_push_text_layer (image, NULL, layer, pspec);
|
|
}
|
|
|
|
src = G_OBJECT (text_tool->proxy);
|
|
dest = G_OBJECT (text_tool->text);
|
|
|
|
g_signal_handlers_block_by_func (dest,
|
|
gimp_text_tool_text_notify,
|
|
text_tool);
|
|
g_signal_handlers_block_by_func (dest,
|
|
gimp_text_tool_text_changed,
|
|
text_tool);
|
|
|
|
g_object_freeze_notify (dest);
|
|
|
|
for (; list; list = g_list_next (list))
|
|
{
|
|
GValue value = { 0, };
|
|
|
|
/* look ahead and compress changes */
|
|
if (list->next && list->next->data == list->data)
|
|
continue;
|
|
|
|
pspec = list->data;
|
|
|
|
g_value_init (&value, pspec->value_type);
|
|
|
|
g_object_get_property (src, pspec->name, &value);
|
|
g_object_set_property (dest, pspec->name, &value);
|
|
|
|
g_value_unset (&value);
|
|
}
|
|
|
|
g_list_free (text_tool->pending);
|
|
text_tool->pending = NULL;
|
|
|
|
g_object_thaw_notify (dest);
|
|
|
|
g_signal_handlers_unblock_by_func (dest,
|
|
gimp_text_tool_text_notify,
|
|
text_tool);
|
|
g_signal_handlers_unblock_by_func (dest,
|
|
gimp_text_tool_text_changed,
|
|
text_tool);
|
|
|
|
if (push_undo)
|
|
{
|
|
g_object_set (layer, "modified", FALSE, NULL);
|
|
|
|
if (undo_group)
|
|
gimp_image_undo_group_end (image);
|
|
}
|
|
|
|
gimp_text_tool_frame_item (text_tool);
|
|
|
|
gimp_image_flush (image);
|
|
|
|
gimp_text_tool_unblock_drawing (text_tool);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_create_layer (GimpTextTool *text_tool,
|
|
GimpText *text)
|
|
{
|
|
GimpRectangleTool *rect_tool = GIMP_RECTANGLE_TOOL (text_tool);
|
|
GimpTool *tool = GIMP_TOOL (text_tool);
|
|
GimpImage *image = gimp_display_get_image (tool->display);
|
|
GimpLayer *layer;
|
|
gint x1, y1;
|
|
gint x2, y2;
|
|
|
|
gimp_text_tool_block_drawing (text_tool);
|
|
|
|
if (text)
|
|
{
|
|
text = gimp_config_duplicate (GIMP_CONFIG (text));
|
|
}
|
|
else
|
|
{
|
|
gchar *string;
|
|
|
|
if (gimp_text_buffer_has_markup (text_tool->buffer))
|
|
{
|
|
string = gimp_text_buffer_get_markup (text_tool->buffer);
|
|
|
|
g_object_set (text_tool->proxy,
|
|
"markup", string,
|
|
"box-mode", GIMP_TEXT_BOX_DYNAMIC,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
string = gimp_text_buffer_get_text (text_tool->buffer);
|
|
|
|
g_object_set (text_tool->proxy,
|
|
"text", string,
|
|
"box-mode", GIMP_TEXT_BOX_DYNAMIC,
|
|
NULL);
|
|
}
|
|
|
|
g_free (string);
|
|
|
|
text = gimp_config_duplicate (GIMP_CONFIG (text_tool->proxy));
|
|
}
|
|
|
|
layer = gimp_text_layer_new (image, text);
|
|
|
|
g_object_unref (text);
|
|
|
|
if (! layer)
|
|
{
|
|
gimp_text_tool_unblock_drawing (text_tool);
|
|
return;
|
|
}
|
|
|
|
gimp_text_tool_connect (text_tool, GIMP_TEXT_LAYER (layer), text);
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TEXT,
|
|
_("Add Text Layer"));
|
|
|
|
if (gimp_image_get_floating_selection (image))
|
|
{
|
|
g_signal_handlers_block_by_func (image,
|
|
gimp_text_tool_layer_changed,
|
|
text_tool);
|
|
|
|
floating_sel_anchor (gimp_image_get_floating_selection (image));
|
|
|
|
g_signal_handlers_unblock_by_func (image,
|
|
gimp_text_tool_layer_changed,
|
|
text_tool);
|
|
}
|
|
|
|
g_object_get (rect_tool,
|
|
"x1", &x1,
|
|
"y1", &y1,
|
|
"x2", &x2,
|
|
"y2", &y2,
|
|
NULL);
|
|
|
|
gimp_item_set_offset (GIMP_ITEM (layer), x1, y1);
|
|
|
|
gimp_image_add_layer (image, layer,
|
|
GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
|
|
|
|
if (text_tool->text_box_fixed)
|
|
{
|
|
GimpUnit box_unit = text_tool->proxy->box_unit;
|
|
gdouble xres, yres;
|
|
|
|
gimp_image_get_resolution (image, &xres, &yres);
|
|
|
|
g_object_set (text_tool->proxy,
|
|
"box-mode", GIMP_TEXT_BOX_FIXED,
|
|
"box-width", gimp_pixels_to_units (x2 - x1,
|
|
box_unit, xres),
|
|
"box-height", gimp_pixels_to_units (y2 - y1,
|
|
box_unit, yres),
|
|
NULL);
|
|
|
|
gimp_text_tool_apply (text_tool, TRUE); /* unblocks drawing */
|
|
}
|
|
else
|
|
{
|
|
gimp_text_tool_frame_item (text_tool);
|
|
|
|
gimp_text_tool_unblock_drawing (text_tool);
|
|
}
|
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
gimp_image_flush (image);
|
|
|
|
gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer), FALSE);
|
|
}
|
|
|
|
#define RESPONSE_NEW 1
|
|
|
|
static void
|
|
gimp_text_tool_confirm_response (GtkWidget *widget,
|
|
gint response_id,
|
|
GimpTextTool *text_tool)
|
|
{
|
|
GimpTextLayer *layer = text_tool->layer;
|
|
|
|
gtk_widget_destroy (widget);
|
|
|
|
if (layer && layer->text)
|
|
{
|
|
switch (response_id)
|
|
{
|
|
case RESPONSE_NEW:
|
|
gimp_text_tool_create_layer (text_tool, layer->text);
|
|
break;
|
|
|
|
case GTK_RESPONSE_ACCEPT:
|
|
gimp_text_tool_connect (text_tool, layer, layer->text);
|
|
|
|
/* cause the text layer to be rerendered */
|
|
g_object_notify (G_OBJECT (text_tool->proxy), "markup");
|
|
|
|
gimp_text_tool_editor_start (text_tool);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_confirm_dialog (GimpTextTool *text_tool)
|
|
{
|
|
GimpTool *tool = GIMP_TOOL (text_tool);
|
|
GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
|
|
GtkWidget *dialog;
|
|
GtkWidget *vbox;
|
|
GtkWidget *label;
|
|
|
|
g_return_if_fail (text_tool->layer != NULL);
|
|
|
|
if (text_tool->confirm_dialog)
|
|
{
|
|
gtk_window_present (GTK_WINDOW (text_tool->confirm_dialog));
|
|
return;
|
|
}
|
|
|
|
dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (text_tool->layer),
|
|
GIMP_CONTEXT (gimp_tool_get_options (tool)),
|
|
_("Confirm Text Editing"),
|
|
"gimp-text-tool-confirm",
|
|
GIMP_STOCK_TEXT_LAYER,
|
|
_("Confirm Text Editing"),
|
|
GTK_WIDGET (shell),
|
|
gimp_standard_help_func, NULL,
|
|
|
|
_("Create _New Layer"), RESPONSE_NEW,
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
|
GTK_STOCK_EDIT, GTK_RESPONSE_ACCEPT,
|
|
|
|
NULL);
|
|
|
|
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
|
RESPONSE_NEW,
|
|
GTK_RESPONSE_ACCEPT,
|
|
GTK_RESPONSE_CANCEL,
|
|
-1);
|
|
|
|
gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
|
|
|
|
g_signal_connect (dialog, "response",
|
|
G_CALLBACK (gimp_text_tool_confirm_response),
|
|
text_tool);
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
|
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
|
vbox, FALSE, FALSE, 0);
|
|
gtk_widget_show (vbox);
|
|
|
|
label = gtk_label_new (_("The layer you selected is a text layer but "
|
|
"it has been modified using other tools. "
|
|
"Editing the layer with the text tool will "
|
|
"discard these modifications."
|
|
"\n\n"
|
|
"You can edit the layer or create a new "
|
|
"text layer from its text attributes."));
|
|
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
|
|
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
|
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
|
|
gtk_widget_show (label);
|
|
|
|
gtk_widget_show (dialog);
|
|
|
|
text_tool->confirm_dialog = dialog;
|
|
g_signal_connect_swapped (dialog, "destroy",
|
|
G_CALLBACK (g_nullify_pointer),
|
|
&text_tool->confirm_dialog);
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_layer_changed (GimpImage *image,
|
|
GimpTextTool *text_tool)
|
|
{
|
|
GimpLayer *layer = gimp_image_get_active_layer (image);
|
|
|
|
if (layer == GIMP_LAYER (text_tool->layer))
|
|
return;
|
|
|
|
/* all this stuff doesn't work quite yet, but it's better than before
|
|
*/
|
|
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (text_tool));
|
|
|
|
gimp_text_tool_editor_halt (text_tool);
|
|
gimp_text_tool_clear_layout (text_tool);
|
|
|
|
if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (text_tool)))
|
|
gimp_draw_tool_stop (GIMP_DRAW_TOOL (text_tool));
|
|
|
|
if (gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer), FALSE) &&
|
|
GIMP_LAYER (text_tool->layer) == layer)
|
|
{
|
|
gimp_draw_tool_start (GIMP_DRAW_TOOL (text_tool),
|
|
GIMP_TOOL (text_tool)->display);
|
|
|
|
gimp_text_tool_frame_item (text_tool);
|
|
gimp_text_tool_editor_start (text_tool);
|
|
gimp_text_tool_editor_position (text_tool);
|
|
}
|
|
else
|
|
{
|
|
gimp_tool_control (GIMP_TOOL (text_tool), GIMP_TOOL_ACTION_HALT,
|
|
GIMP_TOOL (text_tool)->display);
|
|
}
|
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (text_tool));
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_set_image (GimpTextTool *text_tool,
|
|
GimpImage *image)
|
|
{
|
|
if (text_tool->image == image)
|
|
return;
|
|
|
|
if (text_tool->image)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (text_tool->image,
|
|
gimp_text_tool_layer_changed,
|
|
text_tool);
|
|
|
|
g_object_remove_weak_pointer (G_OBJECT (text_tool->image),
|
|
(gpointer) &text_tool->image);
|
|
}
|
|
|
|
text_tool->image = image;
|
|
|
|
if (image)
|
|
{
|
|
GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
|
|
gdouble xres;
|
|
gdouble yres;
|
|
|
|
g_object_add_weak_pointer (G_OBJECT (text_tool->image),
|
|
(gpointer) &text_tool->image);
|
|
|
|
g_signal_connect_object (text_tool->image, "active-layer-changed",
|
|
G_CALLBACK (gimp_text_tool_layer_changed),
|
|
text_tool, 0);
|
|
|
|
gimp_image_get_resolution (image, &xres, &yres);
|
|
gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (options->size_entry), 0,
|
|
yres, FALSE);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gimp_text_tool_set_drawable (GimpTextTool *text_tool,
|
|
GimpDrawable *drawable,
|
|
gboolean confirm)
|
|
{
|
|
GimpImage *image = NULL;
|
|
|
|
if (text_tool->confirm_dialog)
|
|
gtk_widget_destroy (text_tool->confirm_dialog);
|
|
|
|
if (drawable)
|
|
image = gimp_item_get_image (GIMP_ITEM (drawable));
|
|
|
|
gimp_text_tool_set_image (text_tool, image);
|
|
|
|
if (GIMP_IS_TEXT_LAYER (drawable) && GIMP_TEXT_LAYER (drawable)->text)
|
|
{
|
|
GimpTextLayer *layer = GIMP_TEXT_LAYER (drawable);
|
|
|
|
if (layer == text_tool->layer && layer->text == text_tool->text)
|
|
return TRUE;
|
|
|
|
if (layer->modified)
|
|
{
|
|
if (confirm)
|
|
{
|
|
gimp_text_tool_connect (text_tool, layer, NULL);
|
|
gimp_text_tool_confirm_dialog (text_tool);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gimp_text_tool_connect (text_tool, layer, layer->text);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
gimp_text_tool_connect (text_tool, NULL, NULL);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_block_drawing (GimpTextTool *text_tool)
|
|
{
|
|
if (! text_tool->drawing_blocked)
|
|
{
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (text_tool));
|
|
|
|
gimp_text_tool_clear_layout (text_tool);
|
|
|
|
text_tool->drawing_blocked = TRUE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_unblock_drawing (GimpTextTool *text_tool)
|
|
{
|
|
g_return_if_fail (text_tool->drawing_blocked == TRUE);
|
|
|
|
text_tool->drawing_blocked = FALSE;
|
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (text_tool));
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_buffer_begin_edit (GimpTextBuffer *buffer,
|
|
GimpTextTool *text_tool)
|
|
{
|
|
gimp_text_tool_block_drawing (text_tool);
|
|
}
|
|
|
|
static void
|
|
gimp_text_tool_buffer_end_edit (GimpTextBuffer *buffer,
|
|
GimpTextTool *text_tool)
|
|
{
|
|
if (text_tool->text)
|
|
{
|
|
gchar *string;
|
|
|
|
if (gimp_text_buffer_has_markup (buffer))
|
|
{
|
|
string = gimp_text_buffer_get_markup (buffer);
|
|
|
|
g_object_set (text_tool->proxy,
|
|
"markup", string,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
string = gimp_text_buffer_get_text (buffer);
|
|
|
|
g_object_set (text_tool->proxy,
|
|
"text", string,
|
|
NULL);
|
|
}
|
|
|
|
g_free (string);
|
|
}
|
|
else
|
|
{
|
|
gimp_text_tool_create_layer (text_tool, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
/* public functions */
|
|
|
|
void
|
|
gimp_text_tool_clear_layout (GimpTextTool *text_tool)
|
|
{
|
|
if (text_tool->layout)
|
|
{
|
|
g_object_unref (text_tool->layout);
|
|
text_tool->layout = NULL;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gimp_text_tool_ensure_layout (GimpTextTool *text_tool)
|
|
{
|
|
if (! text_tool->layout && text_tool->text)
|
|
{
|
|
GimpImage *image = gimp_item_get_image (GIMP_ITEM (text_tool->layer));
|
|
gdouble xres;
|
|
gdouble yres;
|
|
GError *error = NULL;
|
|
|
|
gimp_image_get_resolution (image, &xres, &yres);
|
|
|
|
text_tool->layout = gimp_text_layout_new (text_tool->layer->text,
|
|
xres, yres, &error);
|
|
if (error)
|
|
{
|
|
gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_ERROR, error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
return text_tool->layout != NULL;
|
|
}
|
|
|
|
void
|
|
gimp_text_tool_set_layer (GimpTextTool *text_tool,
|
|
GimpLayer *layer)
|
|
{
|
|
g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
|
|
g_return_if_fail (layer == NULL || GIMP_IS_LAYER (layer));
|
|
|
|
if (gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer), TRUE))
|
|
{
|
|
GimpTool *tool = GIMP_TOOL (text_tool);
|
|
GimpItem *item = GIMP_ITEM (layer);
|
|
GimpContext *context;
|
|
GimpDisplay *display;
|
|
|
|
context = gimp_get_user_context (tool->tool_info->gimp);
|
|
display = gimp_context_get_display (context);
|
|
|
|
if (! display ||
|
|
gimp_display_get_image (display) != gimp_item_get_image (item))
|
|
{
|
|
GList *list;
|
|
|
|
display = NULL;
|
|
|
|
for (list = gimp_get_display_iter (tool->tool_info->gimp);
|
|
list;
|
|
list = g_list_next (list))
|
|
{
|
|
display = list->data;
|
|
|
|
if (gimp_display_get_image (display) == gimp_item_get_image (item))
|
|
{
|
|
gimp_context_set_display (context, display);
|
|
break;
|
|
}
|
|
|
|
display = NULL;
|
|
}
|
|
}
|
|
|
|
tool->display = display;
|
|
|
|
if (tool->display)
|
|
{
|
|
GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (tool);
|
|
|
|
tool->drawable = GIMP_DRAWABLE (layer);
|
|
|
|
if (gimp_draw_tool_is_active (draw_tool))
|
|
gimp_draw_tool_stop (draw_tool);
|
|
|
|
gimp_draw_tool_start (draw_tool, display);
|
|
|
|
gimp_text_tool_frame_item (text_tool);
|
|
|
|
gimp_text_tool_editor_start (text_tool);
|
|
}
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gimp_text_tool_get_has_text_selection (GimpTextTool *text_tool)
|
|
{
|
|
GtkTextBuffer *buffer = GTK_TEXT_BUFFER (text_tool->buffer);
|
|
|
|
return gtk_text_buffer_get_has_selection (buffer);
|
|
}
|
|
|
|
void
|
|
gimp_text_tool_delete_selection (GimpTextTool *text_tool)
|
|
{
|
|
GtkTextBuffer *buffer = GTK_TEXT_BUFFER (text_tool->buffer);
|
|
|
|
if (gtk_text_buffer_get_has_selection (buffer))
|
|
{
|
|
gtk_text_buffer_delete_selection (buffer, TRUE, TRUE);
|
|
}
|
|
}
|
|
|
|
void
|
|
gimp_text_tool_cut_clipboard (GimpTextTool *text_tool)
|
|
{
|
|
GimpDisplayShell *shell;
|
|
GtkClipboard *clipboard;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
|
|
|
|
shell = gimp_display_get_shell (GIMP_TOOL (text_tool)->display);
|
|
|
|
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (shell),
|
|
GDK_SELECTION_CLIPBOARD);
|
|
|
|
gtk_text_buffer_cut_clipboard (GTK_TEXT_BUFFER (text_tool->buffer),
|
|
clipboard, TRUE);
|
|
}
|
|
|
|
void
|
|
gimp_text_tool_copy_clipboard (GimpTextTool *text_tool)
|
|
{
|
|
GimpDisplayShell *shell;
|
|
GtkClipboard *clipboard;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
|
|
|
|
shell = gimp_display_get_shell (GIMP_TOOL (text_tool)->display);
|
|
|
|
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (shell),
|
|
GDK_SELECTION_CLIPBOARD);
|
|
|
|
/* need to block "end-user-action" on the text buffer, because
|
|
* GtkTextBuffer considers copying text to the clipboard an
|
|
* undo-relevant user action, which is clearly a bug, but what
|
|
* can we do...
|
|
*/
|
|
g_signal_handlers_block_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_begin_edit,
|
|
text_tool);
|
|
g_signal_handlers_block_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_end_edit,
|
|
text_tool);
|
|
|
|
gtk_text_buffer_copy_clipboard (GTK_TEXT_BUFFER (text_tool->buffer),
|
|
clipboard);
|
|
|
|
g_signal_handlers_unblock_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_end_edit,
|
|
text_tool);
|
|
g_signal_handlers_unblock_by_func (text_tool->buffer,
|
|
gimp_text_tool_buffer_begin_edit,
|
|
text_tool);
|
|
}
|
|
|
|
void
|
|
gimp_text_tool_paste_clipboard (GimpTextTool *text_tool)
|
|
{
|
|
GimpDisplayShell *shell;
|
|
GtkClipboard *clipboard;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
|
|
|
|
shell = gimp_display_get_shell (GIMP_TOOL (text_tool)->display);
|
|
|
|
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (shell),
|
|
GDK_SELECTION_CLIPBOARD);
|
|
|
|
gtk_text_buffer_paste_clipboard (GTK_TEXT_BUFFER (text_tool->buffer),
|
|
clipboard, NULL, TRUE);
|
|
}
|
|
|
|
void
|
|
gimp_text_tool_create_vectors (GimpTextTool *text_tool)
|
|
{
|
|
GimpVectors *vectors;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
|
|
|
|
if (! text_tool->text || ! text_tool->image)
|
|
return;
|
|
|
|
vectors = gimp_text_vectors_new (text_tool->image, text_tool->text);
|
|
|
|
if (text_tool->layer)
|
|
{
|
|
gint x, y;
|
|
|
|
gimp_item_get_offset (GIMP_ITEM (text_tool->layer), &x, &y);
|
|
gimp_item_translate (GIMP_ITEM (vectors), x, y, FALSE);
|
|
}
|
|
|
|
gimp_image_add_vectors (text_tool->image, vectors,
|
|
GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
|
|
|
|
gimp_image_flush (text_tool->image);
|
|
}
|
|
|
|
void
|
|
gimp_text_tool_create_vectors_warped (GimpTextTool *text_tool)
|
|
{
|
|
GimpVectors *vectors0;
|
|
GimpVectors *vectors;
|
|
gdouble box_height;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
|
|
|
|
if (! text_tool->text || ! text_tool->image || ! text_tool->layer)
|
|
return;
|
|
|
|
box_height = gimp_item_get_height (GIMP_ITEM (text_tool->layer));
|
|
|
|
vectors0 = gimp_image_get_active_vectors (text_tool->image);
|
|
if (! vectors0)
|
|
return;
|
|
|
|
vectors = gimp_text_vectors_new (text_tool->image, text_tool->text);
|
|
|
|
gimp_vectors_warp_vectors (vectors0, vectors, 0.5 * box_height);
|
|
|
|
gimp_item_set_visible (GIMP_ITEM (vectors), TRUE, FALSE);
|
|
|
|
gimp_image_add_vectors (text_tool->image, vectors,
|
|
GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
|
|
|
|
gimp_image_flush (text_tool->image);
|
|
}
|