gimp/app/tools/gimptexttool.c

2415 lines
79 KiB
C
Raw Normal View History

/* GIMP - The GNU Image Manipulation Program
1997-11-25 06:05:25 +08:00
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* GimpTextTool
2010-02-18 04:39:33 +08:00
* 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
1997-11-25 06:05:25 +08:00
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
1997-11-25 06:05:25 +08:00
* (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 <https://www.gnu.org/licenses/>.
1997-11-25 06:05:25 +08:00
*/
#include "config.h"
#include <gegl.h>
app/Makefile.am app/channel_pvt.h app/drawable_pvt.h app/gdisplayF.h 2000-12-29 Michael Natterer <mitch@gimp.org> * app/Makefile.am * app/channel_pvt.h * app/drawable_pvt.h * app/gdisplayF.h * app/gimpdrawableP.h * app/gimpimageP.h * app/layer_pvt.h * app/toolsF.h: removed these files. * app/apptypes.h * tools/pdbgen/enums.pl: added tons of opaque typedefs and enums. * tools/pdbgen/pdb/brush_select.pdb * tools/pdbgen/pdb/brushes.pdb * tools/pdbgen/pdb/channel.pdb * tools/pdbgen/pdb/color.pdb * tools/pdbgen/pdb/convert.pdb * tools/pdbgen/pdb/display.pdb * tools/pdbgen/pdb/drawable.pdb * tools/pdbgen/pdb/fileops.pdb * tools/pdbgen/pdb/gradient_select.pdb * tools/pdbgen/pdb/gradients.pdb * tools/pdbgen/pdb/help.pdb * tools/pdbgen/pdb/image.pdb * tools/pdbgen/pdb/layer.pdb * tools/pdbgen/pdb/pattern_select.pdb * tools/pdbgen/pdb/patterns.pdb * tools/pdbgen/pdb/selection.pdb * tools/pdbgen/pdb/tools.pdb * app/*: chainsaw #include cleanup: - Never (never!!) include stuff in header files except where we need access to structures' contents (like derived objects). - Added prototypes and proper formating in many files. - The #include order in *all* *.c files is as follows: #include "config.h" #include <system stuff> #include <gtk/gtk.h> #include "apptypes.h" #include "gimp stuff" #include "libgimp stuff" #include "libgimp/gimpintl.h" By following this scheme we can easily see a file's dependencies from it's #include's and can grep for the inclusion to find out where a file is used. * tools/pdbgen/app.pl: changed to follow the include scheme above. * libgimp/Makefile.am * libgimp/gimpuitypes.h: new file, included from libgimp/gimpui.h and from app/apptypes.h. * libgimp/gimpcolorbutton.[ch] * libgimp/gimpdialog.[ch] * libgimp/gimphelpui.[ch] * libgimp/gimpparasite.[ch] * libgimp/gimppatheditor.[ch] * libgimp/gimpprotocol.c * libgimp/gimpquerybox.[ch] * libgimp/gimpsizeentry.[ch] * libgimp/gimptypes.h * libgimp/gimpui.h * libgimp/gimpunit.h * libgimp/gimpunitmenu.[ch] * libgimp/gimpwidgets.[ch]: changed accordingly. * plug-ins/FractalExplorer/Dialogs.c * plug-ins/gdyntext/message_window.c * plug-ins/imagemap/imap_default_dialog.c * plug-ins/imagemap/imap_file.c: these files used to include "libgimp/gimpui.h" without including "libgimp/gimp.h". This is no longer possible because the libgimpui headers don't inlcude "libgimp/gimpunit.h" any more.
2000-12-29 23:22:01 +08:00
#include <gtk/gtk.h>
2010-02-22 00:35:04 +08:00
#include "libgimpbase/gimpbase.h"
2005-01-26 03:11:26 +08:00
#include "libgimpconfig/gimpconfig.h"
Makefile.am configure.in added the new library below. 2001-01-24 Michael Natterer <mitch@gimp.org> * Makefile.am * configure.in * gimptool.in: added the new library below. * libgimpwidgets/Makefile.am * libgimpwidgets/gimpchainbutton.[ch] * libgimpwidgets/gimpcolorarea.[ch] * libgimpwidgets/gimpcolorbutton.[ch] * libgimpwidgets/gimpdialog.[ch] * libgimpwidgets/gimpfileselection.[ch] * libgimpwidgets/gimphelpui.[ch] * libgimpwidgets/gimppatheditor.[ch] * libgimpwidgets/gimppixmap.[ch] * libgimpwidgets/gimpquerybox.[ch] * libgimpwidgets/gimpsizeentry.[ch] * libgimpwidgets/gimpunitmenu.[ch] * libgimpwidgets/gimpwidgets.[ch] * libgimpwidgets/gimpwidgets.def * libgimpwidgets/gimpwidgetstypes.h: new shared library. Currently there are some ugly dependencies into libgimp. These will be removed and go to a "libgimpglue" library which will be a library for functions which share a common interface between plug-ins and the app but have different implementations. Include "libgimp/gimpunit.h" from "libgimpwidgets/gimpwidgetstypes.h" to simulate this upcoming separation. * libgimp/Makefile.am * libgimp/gimpchainbutton.[ch] * libgimp/gimpcolorarea.[ch] * libgimp/gimpcolorbutton.[ch] * libgimp/gimpdialog.[ch] * libgimp/gimpfileselection.[ch] * libgimp/gimphelpui.[ch] * libgimp/gimppatheditor.[ch] * libgimp/gimppixmap.[ch] * libgimp/gimpquerybox.[ch] * libgimp/gimpsizeentry.[ch] * libgimp/gimpunitmenu.[ch] * libgimp/gimpwidgets.[ch]: removed from here. * libgimp/gimpui.h * libgimp/gimpuitypes.h * libgimp/makefile.mingw.in * libgimp/makefile.msc: changed accordingly. * app/[all ui files] * app/pdb/palette_cmds.c * app/pdb/tools_cmds.c * tools/pdbgen/pdb/palette.pdb * tools/pdbgen/pdb/tools.pdb: #include "libgimpwidgets/gimpwidgets.h" and removed useless includes. * app/apptypes.h: #include "libgimpwidgets/gimpwidgetstypes.h" * app/Makefile.am * plug-ins/[all makefiles which link against libgimpui]: link against libgimpwidgets.la * po-libgimp/POTFILES.in: changed file locations.
2001-01-25 06:36:18 +08:00
#include "libgimpwidgets/gimpwidgets.h"
devel-docs/Makefile.am new file documenting the core's include policy. 2002-05-03 Michael Natterer <mitch@gimp.org> * devel-docs/Makefile.am * devel-docs/includes.txt: new file documenting the core's include policy. * HACKING: mention it here. * libgimptool/gimptooltypes.h: removed GimpToolOptions here. * app/core/core-types.h: and added it here. This is a temp hack needed because GimpToolInfo needs to know the GimpToolOptions type. * libgimpproxy/gimpproxytypes.h: regenerated. * libgimptool/gimptoolmodule.h: don't include gimptooltypes.h here... * libgimptool/gimptoolmodule.c: ...but here. * app/config/gimpconfig-params.c: include "libgimpbase/gimpbase.h" entirely, not single files from it. * app/core/gimp.c * app/core/gimpcontext.c * app/core/gimpcoreconfig.c * app/core/gimpdatafactory.c * app/core/gimpdocuments.c * app/core/gimpdrawable-blend.c * app/core/gimpdrawable-bucket-fill.c * app/core/gimpdrawable-offset.c * app/core/gimpdrawable-transform.c * app/core/gimpdrawable.c * app/core/gimpedit.c * app/core/gimpimage-convert.c * app/core/gimpimage-crop.c * app/core/gimpimage-duplicate.c * app/core/gimpimage-guides.c * app/core/gimpimage-mask.c * app/core/gimpimage-merge.c * app/core/gimpimage-new.c * app/core/gimpimage-projection.c * app/core/gimpimage-qmask.c * app/core/gimpimage-resize.c * app/core/gimpimage-scale.c * app/core/gimpimage.c * app/core/gimpitem.c * app/core/gimpmodules.c * app/core/gimppaintinfo.c * app/core/gimpparasite.c * app/core/gimppreviewcache.c * app/core/gimptoolinfo.c * app/core/gimpunit.c: include "core-types.h" and no other types file. * app/display/gimpdisplay.c * app/display/gimpdisplayshell-callbacks.c * app/display/gimpdisplayshell.c: include "tools/tools-types.h" instead of "libgimptool/gimptooltypes.h", warn about inclusion on "gui/gui-types.h" * app/file/file-open.c * app/file/file-save.c: don't include "libgimptool/gimptooltypes.h". * app/gui/about-dialog.c * app/gui/brush-select.c * app/gui/brushes-commands.c * app/gui/color-select.c * app/gui/data-commands.c * app/gui/device-status-dialog.c * app/gui/dialogs.c * app/gui/gradients-commands.c * app/gui/help-commands.c * app/gui/info-window.c * app/gui/palettes-commands.c * app/gui/patterns-commands.c * app/gui/resize-dialog.c * app/gui/tips-dialog.c * app/gui/tool-options-dialog.c: include "gui-types.h" and no other types file. * app/paint/gimpairbrush.c * app/paint/gimpclone.c * app/paint/gimpconvolve.c * app/paint/gimpdodgeburn.c * app/paint/gimperaser.c * app/paint/gimppaintbrush.c * app/paint/gimppaintcore-stroke.c * app/paint/gimppaintcore.c * app/paint/gimppaintoptions.c * app/paint/gimppencil.c * app/paint/gimpsmudge.c * app/paint/paint.c: include "paint-types.h" and no other types file. * app/pdb/pdb-types.h: don't include "libgimptool/gimptooltypes.h". * app/plug-in/plug-in-progress.c: warn about inclusion of "display/display-types.h" * app/tools/tools-types.h: include "libgimptool/gimptooltypes.h". * app/tools/gimpairbrushtool.c * app/tools/gimpbezierselecttool.c * app/tools/gimpblendtool.c * app/tools/gimpbrightnesscontrasttool.c * app/tools/gimpbucketfilltool.c * app/tools/gimpbycolorselecttool.c * app/tools/gimpclonetool.c * app/tools/gimpcolorbalancetool.c * app/tools/gimpcolorpickertool.c * app/tools/gimpconvolvetool.c * app/tools/gimpcroptool.c * app/tools/gimpcurvestool.c * app/tools/gimpdodgeburntool.c * app/tools/gimpdrawtool.c * app/tools/gimpeditselectiontool.c * app/tools/gimpellipseselecttool.c * app/tools/gimperasertool.c * app/tools/gimpfliptool.c * app/tools/gimpfreeselecttool.c * app/tools/gimpfuzzyselecttool.c * app/tools/gimphistogramtool.c * app/tools/gimphuesaturationtool.c * app/tools/gimpinktool.c * app/tools/gimplevelstool.c * app/tools/gimpmagnifytool.c * app/tools/gimpmeasuretool.c * app/tools/gimpmovetool.c * app/tools/gimppaintbrushtool.c * app/tools/gimppainttool.c * app/tools/gimppathtool.c * app/tools/gimppenciltool.c * app/tools/gimpperspectivetool.c * app/tools/gimpposterizetool.c * app/tools/gimprectselecttool.c * app/tools/gimprotatetool.c * app/tools/gimpscaletool.c * app/tools/gimpselectiontool.c * app/tools/gimpsheartool.c * app/tools/gimpsmudgetool.c * app/tools/gimptexttool.c * app/tools/gimpthresholdtool.c * app/tools/gimptoolcontrol.c * app/tools/gimptoolcontrol.h * app/tools/gimptransformtool.c * app/tools/gimpvectortool.c * app/tools/tools.c: include "tools-types.h" and no other types file, warn about inclusion of "gui/gui-types.h". * app/widgets/gimpcolorpanel.c * app/widgets/gimptoolbox-color-area.c: warn about inclusion of "gui/gui-types.h". * app/xcf/xcf-load.c * app/xcf/xcf.c: don't include "libgimptool/gimptooltypes.h". Split tool-safe-mode up in two files, one including libgimpproxy, one libgimp. * plug-ins/tools/Makefile.am * plug-ins/tools/tool-safe-mode-plug-in.[ch]: new files including libgimp/ stuff only. * plug-ins/tools/tool-safe-mode.[ch]: include libgimpproxy/ and libgimptool/ but don't include libgimp/ because of conflicting declarations. Unrelated: * app/tools/gimpclonetool.c: create the clone core so we don't crash. * app/gui/file-open-dialog.c: changed the way we create previews so that only out-of-date previews are created on a click in the preview area. Unconditional creation can still be forced by <Ctrl>+click. Changed the tooltip to document this.
2002-05-03 20:45:22 +08:00
#include "tools-types.h"
Finally landed the new GimpConfig based gimprc parser. It's not finished 2002-11-18 Sven Neumann <sven@gimp.org> Finally landed the new GimpConfig based gimprc parser. It's not finished yet but we need to start somewhere. This release removes the old gimprc.[ch] files. The gimprc format changes slightly, but the changes are minimal. The Preferences dialog is temporarily disabled since it still needs to be ported. If you are are afraid, stay away from CVS for a few days ;-) * app/Makefile.am * app/gimprc.[ch]: removed the old gimprc system. * app/base/Makefile.am * app/base/base-config.[ch]: removed these files in favor of config/gimpbaseconfig.[ch]. * app/core/Makefile.am * app/core/gimpcoreconfig.[ch]: removed these files in favor of config/gimpcoreconfig.[ch]. * app/config/Makefile.am * app/config/config-types.h: moved typedefs into this new file. * app/config/gimpbaseconfig.[ch] * app/config/gimpcoreconfig.[ch] * app/config/gimpdisplayconfig.[ch] * app/config/gimpguiconfig.[ch] * app/config/gimprc.[ch] * app/config/test-config.c: brought into shape for real use. * app/base/base-types.h: include config/config-types.h here. Added a global GimpBaseConfig *base_config variable to ease migration. * app/gui/Makefile.am: temporarily disabled the preferences dialog. * app/app_procs.c * app/undo.c * app/undo_history.c * app/base/base.[ch] * app/base/gimphistogram.c * app/base/pixel-processor.c * app/base/temp-buf.c * app/base/tile-cache.c * app/core/core-types.h * app/core/gimp-documents.c * app/core/gimp.c * app/core/gimpbrush.c * app/core/gimpbrushgenerated.c * app/core/gimpcontext.c * app/core/gimpdrawable-transform.c * app/core/gimpimage-new.c * app/core/gimpimage.c * app/core/gimpimagefile.c * app/core/gimpmodules.c * app/core/gimppattern.c * app/display/Makefile.am * app/display/gimpdisplay-handlers.c * app/display/gimpdisplay.[ch] * app/display/gimpdisplayshell-callbacks.c * app/display/gimpdisplayshell-handlers.c * app/display/gimpdisplayshell-layer-select.c * app/display/gimpdisplayshell-render.c * app/display/gimpdisplayshell-scale.c * app/display/gimpdisplayshell-scroll.c * app/display/gimpdisplayshell-selection.c * app/display/gimpdisplayshell.[ch] * app/display/gimpnavigationview.c * app/file/file-save.c * app/gui/device-status-dialog.c * app/gui/dialogs-constructors.c * app/gui/file-commands.c * app/gui/file-new-dialog.c * app/gui/file-open-dialog.c * app/gui/file-save-dialog.c * app/gui/gui.c * app/gui/menus.c * app/gui/paths-dialog.c * app/gui/resize-dialog.c * app/gui/session.c * app/gui/test-commands.c * app/gui/tips-dialog.c * app/gui/tips-dialog.h * app/gui/user-install-dialog.c * app/gui/view-commands.c * app/paint/gimppaintcore.c * app/plug-in/plug-in.c * app/plug-in/plug-ins.c * app/tools/gimpbezierselecttool.c * app/tools/gimpbucketfilltool.c * app/tools/gimpcolorpickertool.c * app/tools/gimpcroptool.c * app/tools/gimpeditselectiontool.c * app/tools/gimpfuzzyselecttool.c * app/tools/gimpinktool.c * app/tools/gimpmagnifytool.c * app/tools/gimpmeasuretool.c * app/tools/gimppainttool.c * app/tools/gimppathtool.c * app/tools/gimptexttool.[ch] * app/tools/selection_options.c * app/tools/tools.c * app/tools/transform_options.c * app/widgets/gimphelp.c * app/widgets/gimpitemfactory.c * app/widgets/gimpselectioneditor.c * app/xcf/xcf-load.c * tools/pdbgen/pdb/fileops.pdb * tools/pdbgen/pdb/gimprc.pdb * tools/pdbgen/pdb/image.pdb * tools/pdbgen/pdb/layer.pdb * tools/pdbgen/pdb/transform_tools.pdb: use the new config system instead of the old gimprc stuff. * etc/gimprc.in * etc/gimprc_user.in: adapted to the new gimprc format. Will update the man-page later... * app/pdb/fileops_cmds.c * app/pdb/gimprc_cmds.c * app/pdb/image_cmds.c * app/pdb/layer_cmds.c * app/pdb/transform_tools_cmds.c * libgimp/gimpgimprc_pdb.c: regenerated.
2002-11-19 04:50:31 +08:00
#include "core/gimp.h"
#include "core/gimpasyncset.h"
#include "core/gimpcontext.h"
#include "core/gimpdatafactory.h"
#include "core/gimperror.h"
#include "core/gimpimage.h"
#include "core/gimp-palettes.h"
#include "core/gimpimage-pick-item.h"
#include "core/gimpimage-undo.h"
#include "core/gimpimage-undo-push.h"
#include "core/gimplayer-floating-selection.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/gimpstroke.h"
#include "vectors/gimpvectors.h"
#include "vectors/gimpvectors-warp.h"
#include "widgets/gimpdialogfactory.h"
#include "widgets/gimpdockcontainer.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 "display/gimptoolrectangle.h"
Made GimpToolOptions a GimpContext subclass and objectified all tool 2003-02-05 Michael Natterer <mitch@gimp.org> Made GimpToolOptions a GimpContext subclass and objectified all tool options types. * app/core/core-types.h: replaced GimpToolOptionsNewFunc by GimpToolOptionsGUIFunc. * libgimpproxy/gimpproxytypes.h: regenerated. * app/core/gimppaintinfo.[ch]: added "GType paint_options_type". * app/core/gimptoolinfo.[ch]: added "GType tool_options_type", removed tool_info->context since GimpToolOptions are a GimpContext now. Added "gboolean use_context" as a temp_hack. * libgimptool/gimptooltypes.h: added the tool_options_type to the tool registering callback. * app/tools/tool_options.[ch]: is a real GimpContext subclass now. * app/paint/paint-types.h * app/paint/paint.c: added the paint_options_type to the paint registering stuff. * app/paint/gimppaintoptions.[ch]: is a real GimpToolOptions subclass now. * app/paint/Makefile.am * app/paint/gimpairbrushoptions.[ch] * app/paint/gimpcloneoptions.[ch] * app/paint/gimpconvolveoptions.[ch] * app/paint/gimpdodgeburnoptions.[ch] * app/paint/gimperaseroptions.[ch] * app/paint/gimpsmudgeoptions.[ch]: new files holding GimpPaintOptions subclasses. * app/paint/gimpairbrush.[ch] * app/paint/gimpclone.[ch] * app/paint/gimpconvolve.[ch] * app/paint/gimpdodgeburn.[ch] * app/paint/gimperaser.[ch] * app/paint/gimppaintbrush.c * app/paint/gimppaintcore.c * app/paint/gimppencil.[ch] * app/paint/gimpsmudge.[ch]: removed paint options stuff, lots of related changed & cleanups. * tools/pdbgen/pdb/paint_tools.pdb: changed accordingly. * app/pdb/paint_tools_cmds.c: regenerated. * app/tools/Makefile.am * app/tools/gimpblendoptions.[ch] * app/tools/gimpbucketfilloptions.[ch] * app/tools/gimpcolorpickeroptions.[ch] * app/tools/gimpcropoptions.[ch] * app/tools/gimpflipoptions.[ch] * app/tools/gimpinkoptions.[ch] * app/tools/gimpmagnifyoptions.[ch] * app/tools/gimpmeasureoptions.[ch] * app/tools/gimpmoveoptions.[ch] * app/tools/gimptextoptions.[ch] * app/tools/gimpvectoroptions.[ch]: new files holding the various tool options classes. * app/tools/selection_options.[ch] * app/tools/transform_options.[ch]: made them objects. * app/tools/paint_options.[ch]: contains only the paint_options GUI and reset stuff. * app/tools/tools-types.h: removed SelectionOptions typedef for now. * app/tools/[all tools]: removed the tool options stuff except some GUI constructors. Tons of related changes. * app/tools/tool_manager.[ch]: changed tool registration / restore / switching accordingly. * app/widgets/gimpdrawablelistview.c * app/widgets/gimpselectioneditor.c: changed accordingly.
2003-02-05 22:39:40 +08:00
#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_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 gboolean gimp_text_tool_start (GimpTextTool *text_tool,
GimpDisplay *display,
GimpLayer *layer,
GError **error);
static void gimp_text_tool_halt (GimpTextTool *text_tool);
static void gimp_text_tool_frame_item (GimpTextTool *text_tool);
static void gimp_text_tool_rectangle_response
(GimpToolRectangle *rectangle,
gint response_id,
GimpTextTool *text_tool);
static void gimp_text_tool_rectangle_change_complete
(GimpToolRectangle *rectangle,
GimpTextTool *text_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 void
gimp_text_tool_fonts_async_set_empty_notify (GimpAsyncSet *async_set,
GParamSpec *pspec,
GimpTextTool *text_tool);
static void gimp_text_tool_apply_list (GimpTextTool *text_tool,
GList *pspecs);
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);
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
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);
static void gimp_text_tool_buffer_color_applied
(GimpTextBuffer *buffer,
const GimpRGB *color,
GimpTextTool *text_tool);
G_DEFINE_TYPE (GimpTextTool, gimp_text_tool, GIMP_TYPE_DRAW_TOOL)
#define parent_class gimp_text_tool_parent_class
1997-11-25 06:05:25 +08:00
void
2002-03-29 11:50:29 +08:00
gimp_text_tool_register (GimpToolRegisterCallback callback,
app/tools/gimpairbrushtool.[ch] app/tools/gimpbezierselecttool.[ch] 2002-05-03 Sven Neumann <sven@gimp.org> * app/tools/gimpairbrushtool.[ch] * app/tools/gimpbezierselecttool.[ch] * app/tools/gimpblendtool.[ch] * app/tools/gimpbrightnesscontrasttool.[ch] * app/tools/gimpbucketfilltool[.ch] * app/tools/gimpbycolorselecttool[.ch] * app/tools/gimpclonetool[.ch] * app/tools/gimpcolorbalancetool[.ch] * app/tools/gimpcolorpickertool[.ch] * app/tools/gimpconvolvetool[.ch] * app/tools/gimpcroptool[.ch] * app/tools/gimpcurvestool[.ch] * app/tools/gimpdodgeburntool[.ch] * app/tools/gimpeditselectiontool.c * app/tools/gimpellipseselecttool[.ch] * app/tools/gimperasertool[.ch] * app/tools/gimpfliptool[.ch] * app/tools/gimpfreeselecttool[.ch] * app/tools/gimpfuzzyselecttool[.ch] * app/tools/gimphistogramtool[.ch] * app/tools/gimphuesaturationtool[.ch] * app/tools/gimpinktool[.ch] * app/tools/gimpiscissorstool[.ch] * app/tools/gimplevelstool[.ch] * app/tools/gimpmagnifytool[.ch] * app/tools/gimpmeasuretool[.ch] * app/tools/gimpmovetool[.ch] * app/tools/gimppaintbrushtool[.ch] * app/tools/gimppainttool.c * app/tools/gimppathtool[.ch] * app/tools/gimppenciltool[.ch] * app/tools/gimpperspectivetool[.ch] * app/tools/gimpposterizetool[.ch] * app/tools/gimprectselecttool[.ch] * app/tools/gimprotatetool[.ch] * app/tools/gimpscaletool[.ch] * app/tools/gimpselectiontool.c * app/tools/gimpsheartool[.ch] * app/tools/gimpsmudgetool[.ch] * app/tools/gimptexttool[.ch] * app/tools/gimpthresholdtool[.ch] * app/tools/gimptool.c * app/tools/gimptoolcontrol.h * app/tools/gimptoolmodule[.ch] * app/tools/gimptransformtool.c * app/tools/gimpvectortool[.ch] * app/tools/path_tool.c * app/tools/tool_manager[.ch] * app/tools/tools.c * libgimptool/gimptool.c * libgimptool/gimptoolcontrol.h * libgimptool/gimptoolmodule.h: removed tons of warnings. Do we need to add -Werror to the CFLAGS to avoid such a mess in the future ?! Also had to enforce the GIMP coding style in lots of places :-( * libgimp/gimppixelrgn.c: got sick and tired of debugging plug-ins, so I've added checks for most parameters passed to the GimpPixelRgn functions. This will slow down plug-in execution a little bit but should help to find bugs early.
2002-05-03 19:31:08 +08:00
gpointer data)
{
2002-03-29 11:50:29 +08:00
(* callback) (GIMP_TYPE_TEXT_TOOL,
Made GimpToolOptions a GimpContext subclass and objectified all tool 2003-02-05 Michael Natterer <mitch@gimp.org> Made GimpToolOptions a GimpContext subclass and objectified all tool options types. * app/core/core-types.h: replaced GimpToolOptionsNewFunc by GimpToolOptionsGUIFunc. * libgimpproxy/gimpproxytypes.h: regenerated. * app/core/gimppaintinfo.[ch]: added "GType paint_options_type". * app/core/gimptoolinfo.[ch]: added "GType tool_options_type", removed tool_info->context since GimpToolOptions are a GimpContext now. Added "gboolean use_context" as a temp_hack. * libgimptool/gimptooltypes.h: added the tool_options_type to the tool registering callback. * app/tools/tool_options.[ch]: is a real GimpContext subclass now. * app/paint/paint-types.h * app/paint/paint.c: added the paint_options_type to the paint registering stuff. * app/paint/gimppaintoptions.[ch]: is a real GimpToolOptions subclass now. * app/paint/Makefile.am * app/paint/gimpairbrushoptions.[ch] * app/paint/gimpcloneoptions.[ch] * app/paint/gimpconvolveoptions.[ch] * app/paint/gimpdodgeburnoptions.[ch] * app/paint/gimperaseroptions.[ch] * app/paint/gimpsmudgeoptions.[ch]: new files holding GimpPaintOptions subclasses. * app/paint/gimpairbrush.[ch] * app/paint/gimpclone.[ch] * app/paint/gimpconvolve.[ch] * app/paint/gimpdodgeburn.[ch] * app/paint/gimperaser.[ch] * app/paint/gimppaintbrush.c * app/paint/gimppaintcore.c * app/paint/gimppencil.[ch] * app/paint/gimpsmudge.[ch]: removed paint options stuff, lots of related changed & cleanups. * tools/pdbgen/pdb/paint_tools.pdb: changed accordingly. * app/pdb/paint_tools_cmds.c: regenerated. * app/tools/Makefile.am * app/tools/gimpblendoptions.[ch] * app/tools/gimpbucketfilloptions.[ch] * app/tools/gimpcolorpickeroptions.[ch] * app/tools/gimpcropoptions.[ch] * app/tools/gimpflipoptions.[ch] * app/tools/gimpinkoptions.[ch] * app/tools/gimpmagnifyoptions.[ch] * app/tools/gimpmeasureoptions.[ch] * app/tools/gimpmoveoptions.[ch] * app/tools/gimptextoptions.[ch] * app/tools/gimpvectoroptions.[ch]: new files holding the various tool options classes. * app/tools/selection_options.[ch] * app/tools/transform_options.[ch]: made them objects. * app/tools/paint_options.[ch]: contains only the paint_options GUI and reset stuff. * app/tools/tools-types.h: removed SelectionOptions typedef for now. * app/tools/[all tools]: removed the tool options stuff except some GUI constructors. Tons of related changes. * app/tools/tool_manager.[ch]: changed tool registration / restore / switching accordingly. * app/widgets/gimpdrawablelistview.c * app/widgets/gimpselectioneditor.c: changed accordingly.
2003-02-05 22:39:40 +08:00
GIMP_TYPE_TEXT_OPTIONS,
gimp_text_options_gui,
GIMP_CONTEXT_PROP_MASK_FOREGROUND |
GIMP_CONTEXT_PROP_MASK_FONT |
GIMP_CONTEXT_PROP_MASK_PALETTE /* for the color popup's palette tab */,
./mitch --sanitize-identifier-namespace 2002-03-20 Michael Natterer <mitch@gimp.org> ./mitch --sanitize-identifier-namespace * app/core/gimpcontext.c * app/display/gimpdisplayshell-callbacks.c * app/display/gimpdisplayshell-dnd.c * app/gui/dialogs-commands.c * app/gui/dialogs-constructors.c * app/gui/dialogs.c * app/gui/edit-commands.c * app/gui/gui.c * app/gui/menus.c * app/gui/vectors-commands.c * app/gui/view-commands.c * app/tools/gimpairbrushtool.c * app/tools/gimpbezierselecttool.c * app/tools/gimpblendtool.c * app/tools/gimpbrightnesscontrasttool.c * app/tools/gimpbucketfilltool.c * app/tools/gimpbycolorselecttool.c * app/tools/gimpclonetool.c * app/tools/gimpcolorbalancetool.c * app/tools/gimpcolorpickertool.c * app/tools/gimpconvolvetool.c * app/tools/gimpcroptool.c * app/tools/gimpcurvestool.c * app/tools/gimpdodgeburntool.c * app/tools/gimpellipseselecttool.c * app/tools/gimperasertool.c * app/tools/gimpfliptool.c * app/tools/gimpfreeselecttool.c * app/tools/gimpfuzzyselecttool.c * app/tools/gimphistogramtool.c * app/tools/gimphuesaturationtool.c * app/tools/gimpinktool.c * app/tools/gimpiscissorstool.c * app/tools/gimplevelstool.c * app/tools/gimpmagnifytool.c * app/tools/gimpmeasuretool.c * app/tools/gimpmovetool.c * app/tools/gimppaintbrushtool.c * app/tools/gimppathtool.c * app/tools/gimppenciltool.c * app/tools/gimpperspectivetool.c * app/tools/gimpposterizetool.c * app/tools/gimprectselecttool.c * app/tools/gimprotatetool.c * app/tools/gimpscaletool.c * app/tools/gimpsheartool.c * app/tools/gimpsmudgetool.c * app/tools/gimptexttool.c * app/tools/gimpthresholdtool.c * app/tools/gimpvectortool.c * app/widgets/gimpdnd.c * app/widgets/gimptoolbox-indicator-area.c * app/widgets/gimptoolbox.c: s/gimp:/gimp-/g and s/_/-/g for all identifier strings (e.g. gimp:eraser_tool -> gimp-eraser-tool, gimp:layer-list -> gimp-layer-list, ...) * plug-ins/tools/common/gimpbrushselecttool.c: s/gimp:brush_select_tool/gimp-brush-select-tool-module/ Don't quite remember why I introduced the "gimp:" prefix in the first place, but we can always add it back if we need it (for whatever reason) You may want to edit your ~/.gimp-1.3/sessionrc and devicerc or all session settings will be lost due to parse errors.
2002-03-21 20:17:17 +08:00
"gimp-text-tool",
_("Text"),
2006-09-19 02:00:22 +08:00
_("Text Tool: Create or edit text layers"),
N_("Te_xt"), "T",
NULL, GIMP_HELP_TOOL_TEXT,
GIMP_ICON_TOOL_TEXT,
app/tools/gimpairbrushtool.[ch] app/tools/gimpbezierselecttool.[ch] 2002-05-03 Sven Neumann <sven@gimp.org> * app/tools/gimpairbrushtool.[ch] * app/tools/gimpbezierselecttool.[ch] * app/tools/gimpblendtool.[ch] * app/tools/gimpbrightnesscontrasttool.[ch] * app/tools/gimpbucketfilltool[.ch] * app/tools/gimpbycolorselecttool[.ch] * app/tools/gimpclonetool[.ch] * app/tools/gimpcolorbalancetool[.ch] * app/tools/gimpcolorpickertool[.ch] * app/tools/gimpconvolvetool[.ch] * app/tools/gimpcroptool[.ch] * app/tools/gimpcurvestool[.ch] * app/tools/gimpdodgeburntool[.ch] * app/tools/gimpeditselectiontool.c * app/tools/gimpellipseselecttool[.ch] * app/tools/gimperasertool[.ch] * app/tools/gimpfliptool[.ch] * app/tools/gimpfreeselecttool[.ch] * app/tools/gimpfuzzyselecttool[.ch] * app/tools/gimphistogramtool[.ch] * app/tools/gimphuesaturationtool[.ch] * app/tools/gimpinktool[.ch] * app/tools/gimpiscissorstool[.ch] * app/tools/gimplevelstool[.ch] * app/tools/gimpmagnifytool[.ch] * app/tools/gimpmeasuretool[.ch] * app/tools/gimpmovetool[.ch] * app/tools/gimppaintbrushtool[.ch] * app/tools/gimppainttool.c * app/tools/gimppathtool[.ch] * app/tools/gimppenciltool[.ch] * app/tools/gimpperspectivetool[.ch] * app/tools/gimpposterizetool[.ch] * app/tools/gimprectselecttool[.ch] * app/tools/gimprotatetool[.ch] * app/tools/gimpscaletool[.ch] * app/tools/gimpselectiontool.c * app/tools/gimpsheartool[.ch] * app/tools/gimpsmudgetool[.ch] * app/tools/gimptexttool[.ch] * app/tools/gimpthresholdtool[.ch] * app/tools/gimptool.c * app/tools/gimptoolcontrol.h * app/tools/gimptoolmodule[.ch] * app/tools/gimptransformtool.c * app/tools/gimpvectortool[.ch] * app/tools/path_tool.c * app/tools/tool_manager[.ch] * app/tools/tools.c * libgimptool/gimptool.c * libgimptool/gimptoolcontrol.h * libgimptool/gimptoolmodule.h: removed tons of warnings. Do we need to add -Werror to the CFLAGS to avoid such a mess in the future ?! Also had to enforce the GIMP coding style in lots of places :-( * libgimp/gimppixelrgn.c: got sick and tired of debugging plug-ins, so I've added checks for most parameters passed to the GimpPixelRgn functions. This will slow down plug-in execution a little bit but should help to find bugs early.
2002-05-03 19:31:08 +08:00
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;
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;
}
static void
gimp_text_tool_init (GimpTextTool *text_tool)
{
GimpTool *tool = GIMP_TOOL (text_tool);
text_tool->buffer = gimp_text_buffer_new ();
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
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",
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
G_CALLBACK (gimp_text_tool_buffer_end_edit),
text_tool);
g_signal_connect (text_tool->buffer, "color-applied",
G_CALLBACK (gimp_text_tool_buffer_color_applied),
text_tool);
2010-02-18 18:47:16 +08:00
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_active_modifiers (tool->control,
GIMP_TOOL_ACTIVE_MODIFIERS_SEPARATE);
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);
GimpTool *tool = GIMP_TOOL (text_tool);
GimpAsyncSet *async_set;
G_OBJECT_CLASS (parent_class)->constructed (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);
async_set =
gimp_data_factory_get_async_set (tool->tool_info->gimp->font_factory);
g_signal_connect_object (async_set,
"notify::empty",
G_CALLBACK (gimp_text_tool_fonts_async_set_empty_notify),
text_tool, 0);
}
static void
gimp_text_tool_finalize (GObject *object)
{
GimpTextTool *text_tool = GIMP_TEXT_TOOL (object);
g_clear_object (&text_tool->proxy);
g_clear_object (&text_tool->buffer);
2020-05-19 22:05:17 +08:00
g_clear_object (&text_tool->ui_manager);
gimp_text_tool_editor_finalize (text_tool);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_text_tool_remove_empty_text_layer (GimpTextTool *text_tool)
{
GimpTextLayer *text_layer = text_tool->layer;
if (text_layer && text_layer->auto_rename)
{
GimpText *text = gimp_text_layer_get_text (text_layer);
if (text && text->box_mode == GIMP_TEXT_BOX_DYNAMIC &&
(! text->text || text->text[0] == '\0') &&
(! text->markup || text->markup[0] == '\0'))
{
GimpImage *image = gimp_item_get_image (GIMP_ITEM (text_layer));
if (text_tool->image == image)
g_signal_handlers_block_by_func (image,
gimp_text_tool_layer_changed,
text_tool);
gimp_image_remove_layer (image, GIMP_LAYER (text_layer), TRUE, NULL);
gimp_image_flush (image);
if (text_tool->image == image)
g_signal_handlers_unblock_by_func (image,
gimp_text_tool_layer_changed,
text_tool);
}
}
}
1997-11-25 06:05:25 +08:00
static void
gimp_text_tool_control (GimpTool *tool,
GimpToolAction action,
GimpDisplay *display)
1997-11-25 06:05:25 +08:00
{
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_halt (text_tool);
break;
case GIMP_TOOL_ACTION_COMMIT:
break;
}
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);
GimpImage *image = gimp_display_get_image (display);
GimpText *text = text_tool->text;
GimpToolRectangle *rectangle;
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
if (tool->display && tool->display != display)
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
if (! text_tool->widget)
{
GError *error = NULL;
if (! gimp_text_tool_start (text_tool, display, NULL, &error))
{
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
gimp_tool_message_literal (tool, display, error->message);
g_clear_error (&error);
return;
}
gimp_tool_widget_hover (text_tool->widget, coords, state, TRUE);
/* HACK: force CREATING on a newly created rectangle; otherwise,
* the above binding of properties would cause the rectangle to
* start with the size from tool options.
*/
gimp_tool_rectangle_set_function (GIMP_TOOL_RECTANGLE (text_tool->widget),
GIMP_TOOL_RECTANGLE_CREATING);
}
rectangle = GIMP_TOOL_RECTANGLE (text_tool->widget);
if (press_type == GIMP_BUTTON_PRESS_NORMAL)
{
gimp_tool_control_activate (tool->control);
/* clicking anywhere while a preedit is going on aborts the
* preedit, this is ugly but at least leaves everything in
* a consistent state
*/
if (text_tool->preedit_active)
gimp_text_tool_abort_im_context (text_tool);
else
gimp_text_tool_reset_im_context (text_tool);
text_tool->selecting = FALSE;
if (gimp_tool_rectangle_point_in_rectangle (rectangle,
coords->x,
coords->y) &&
! text_tool->moving)
{
gimp_tool_rectangle_set_function (rectangle,
GIMP_TOOL_RECTANGLE_DEAD);
}
else if (gimp_tool_widget_button_press (text_tool->widget, coords,
time, state, press_type))
{
text_tool->grab_widget = text_tool->widget;
}
/* bail out now if the user user clicked on a handle of an
* existing rectangle, but not inside an existing framed layer
*/
if (gimp_tool_rectangle_get_function (rectangle) !=
GIMP_TOOL_RECTANGLE_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_tool_rectangle_get_function (rectangle) ==
GIMP_TOOL_RECTANGLE_CREATING)
{
GimpTextLayer *text_layer;
text_layer = gimp_image_pick_text_layer (image, coords->x, coords->y);
if (text_layer && text_layer != text_tool->layer)
{
GList *selection = g_list_prepend (NULL, text_layer);
if (text_tool->image == image)
g_signal_handlers_block_by_func (image,
gimp_text_tool_layer_changed,
text_tool);
gimp_image_set_selected_layers (image, selection);
g_list_free (selection);
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, FALSE))
{
GList *drawables = gimp_image_get_selected_drawables (image);
GimpDrawable *drawable = NULL;
gdouble x = coords->x;
gdouble y = coords->y;
if (g_list_length (drawables) == 1)
{
GimpItem *item = GIMP_ITEM (drawables->data);
x = coords->x - gimp_item_get_offset_x (item);
y = coords->y - gimp_item_get_offset_y (item);
drawable = drawables->data;
}
g_list_free (drawables);
/* did the user click on a text layer? */
if (drawable &&
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_tool_rectangle_get_function (rectangle) ==
GIMP_TOOL_RECTANGLE_CREATING)
{
gimp_tool_rectangle_set_function (rectangle,
GIMP_TOOL_RECTANGLE_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;
/* make sure the text tool has an image, even if the user didn't click
* inside the active drawable, in particular, so that the text style
* editor picks the correct resolution.
*/
gimp_text_tool_set_image (text_tool, image);
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)
{
GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (text_tool->widget);
gimp_tool_control_halt (tool->control);
if (text_tool->selecting)
{
2010-02-24 07:03:15 +08:00
/* 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,
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
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,
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
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 (text_tool->moving)
{
/* the user has moved the text layer with Alt-drag, fall
* through and let rectangle-change-complete do its job of
* setting text layer's new position.
*/
}
else if (gimp_tool_rectangle_get_function (rectangle) ==
GIMP_TOOL_RECTANGLE_DEAD)
{
2010-02-24 07:03:15 +08:00
/* 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 canceled the rectangle resizing, fall through
* and let the rectangle handle restoring the previous size
*/
}
else
{
gdouble x1, y1;
gdouble x2, y2;
2010-02-24 07:03:15 +08:00
/* 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 (rectangle,
"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;
}
}
if (text_tool->grab_widget)
{
gimp_tool_widget_button_release (text_tool->grab_widget,
coords, time, state, release_type);
text_tool->grab_widget = NULL;
}
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)
{
if (text_tool->grab_widget)
{
gimp_tool_widget_motion (text_tool->grab_widget,
coords, time, state);
}
}
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)
{
2010-02-18 04:39:33 +08:00
GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
2010-02-18 04:39:33 +08:00
if (display == tool->display)
return gimp_text_tool_editor_key_press (text_tool, kevent);
2010-02-18 04:39:33 +08:00
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);
GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (text_tool->widget);
GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state,
proximity, display);
text_tool->moving = (text_tool->widget &&
gimp_tool_rectangle_get_function (rectangle) ==
GIMP_TOOL_RECTANGLE_MOVING &&
(state & GDK_MOD1_MASK));
}
1997-11-25 06:05:25 +08:00
static void
gimp_text_tool_cursor_update (GimpTool *tool,
const GimpCoords *coords,
GdkModifierType state,
GimpDisplay *display)
1997-11-25 06:05:25 +08:00
{
GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (text_tool->widget);
if (rectangle && tool->display == display)
{
if (gimp_tool_rectangle_point_in_rectangle (rectangle,
coords->x,
coords->y) &&
! text_tool->moving)
{
gimp_tool_set_cursor (tool, display,
(GimpCursorType) GDK_XTERM,
gimp_tool_control_get_tool_cursor (tool->control),
GIMP_CURSOR_MODIFIER_NONE);
}
else
{
GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state,
display);
}
}
else
{
GimpAsyncSet *async_set;
async_set =
gimp_data_factory_get_async_set (tool->tool_info->gimp->font_factory);
if (gimp_async_set_is_empty (async_set))
{
GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state,
display);
}
else
{
gimp_tool_set_cursor (tool, display,
gimp_tool_control_get_cursor (tool->control),
gimp_tool_control_get_tool_cursor (tool->control),
GIMP_CURSOR_MODIFIER_BAD);
}
}
1997-11-25 06:05:25 +08:00
}
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);
GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (text_tool->widget);
if (rectangle &&
gimp_tool_rectangle_point_in_rectangle (rectangle,
coords->x,
coords->y))
{
if (! text_tool->ui_manager)
{
GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
GimpImageWindow *image_window;
GimpDialogFactory *dialog_factory;
GtkWidget *im_menu;
GList *children;
image_window = gimp_display_shell_get_window (shell);
dialog_factory = gimp_dock_container_get_dialog_factory (GIMP_DOCK_CONTAINER (image_window));
text_tool->ui_manager =
gimp_menu_factory_manager_new (gimp_dialog_factory_get_menu_factory (dialog_factory),
"<TextTool>",
text_tool);
im_menu = gimp_ui_manager_get_widget (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);
GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool);
if (! text_tool->text ||
! text_tool->layer ||
! text_tool->layer->text)
{
gimp_text_tool_editor_update_im_cursor (text_tool);
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;
GimpTextDirection direction;
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;
direction = gimp_text_tool_get_direction (text_tool);
item = gimp_draw_tool_add_text_cursor (draw_tool, &cursor_rect,
overwrite, direction);
gimp_canvas_item_set_highlight (item, TRUE);
}
gimp_text_tool_editor_update_im_cursor (text_tool);
}
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 *group;
PangoLayout *layout;
gint offset_x;
gint offset_y;
gint width;
gint height;
gint off_x, off_y;
PangoLayoutIter *iter;
GtkTextIter sel_start, sel_end;
gint min, max;
gint i;
GimpTextDirection direction;
group = gimp_draw_tool_add_stroke_group (draw_tool);
gimp_canvas_item_set_highlight (GIMP_CANVAS_ITEM (group), TRUE);
2010-02-21 05:11:06 +08:00
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_text_layout_get_size (text_tool->layout, &width, &height);
gimp_item_get_offset (GIMP_ITEM (text_tool->layer), &off_x, &off_y);
offset_x += off_x;
offset_y += off_y;
direction = gimp_text_tool_get_direction (text_tool);
iter = pango_layout_get_iter (layout);
gimp_draw_tool_push_group (draw_tool, 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);
switch (direction)
{
case GIMP_TEXT_DIRECTION_LTR:
case GIMP_TEXT_DIRECTION_RTL:
rect.x += offset_x;
rect.y += offset_y;
gimp_draw_tool_add_rectangle (draw_tool, FALSE,
rect.x, rect.y,
rect.width, rect.height);
break;
case GIMP_TEXT_DIRECTION_TTB_RTL:
case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT:
rect.y = offset_x - rect.y + width;
rect.x = offset_y + rect.x;
gimp_draw_tool_add_rectangle (draw_tool, FALSE,
rect.y, rect.x,
-rect.height, rect.width);
break;
case GIMP_TEXT_DIRECTION_TTB_LTR:
case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT:
rect.y = offset_x + rect.y;
rect.x = offset_y - rect.x + height;
gimp_draw_tool_add_rectangle (draw_tool, FALSE,
rect.y, rect.x,
rect.height, -rect.width);
break;
}
}
}
while (pango_layout_iter_next_char (iter));
gimp_draw_tool_pop_group (draw_tool);
pango_layout_iter_free (iter);
}
static gboolean
gimp_text_tool_start (GimpTextTool *text_tool,
GimpDisplay *display,
GimpLayer *layer,
GError **error)
{
GimpTool *tool = GIMP_TOOL (text_tool);
GimpDisplayShell *shell = gimp_display_get_shell (display);
GimpToolWidget *widget;
GimpAsyncSet *async_set;
async_set =
gimp_data_factory_get_async_set (tool->tool_info->gimp->font_factory);
if (! gimp_async_set_is_empty (async_set))
{
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
_("Fonts are still loading"));
return FALSE;
}
tool->display = display;
text_tool->widget = widget = gimp_tool_rectangle_new (shell);
g_object_set (widget,
"force-narrow-mode", TRUE,
"status-title", _("Text box: "),
NULL);
gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), widget);
g_signal_connect (widget, "response",
G_CALLBACK (gimp_text_tool_rectangle_response),
text_tool);
g_signal_connect (widget, "change-complete",
G_CALLBACK (gimp_text_tool_rectangle_change_complete),
text_tool);
gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
if (layer)
{
gimp_text_tool_frame_item (text_tool);
gimp_text_tool_editor_start (text_tool);
gimp_text_tool_editor_position (text_tool);
}
return TRUE;
}
static void
gimp_text_tool_halt (GimpTextTool *text_tool)
{
GimpTool *tool = GIMP_TOOL (text_tool);
gimp_text_tool_editor_halt (text_tool);
gimp_text_tool_clear_layout (text_tool);
gimp_text_tool_set_drawable (text_tool, NULL, FALSE);
if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tool)))
gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), NULL);
g_clear_object (&text_tool->widget);
tool->display = NULL;
g_list_free (tool->drawables);
tool->drawables = NULL;
}
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_tool_rectangle_frame_item (GIMP_TOOL_RECTANGLE (text_tool->widget),
GIMP_ITEM (text_tool->layer));
text_tool->handle_rectangle_change_complete = TRUE;
}
static void
gimp_text_tool_rectangle_response (GimpToolRectangle *rectangle,
gint response_id,
GimpTextTool *text_tool)
{
GimpTool *tool = GIMP_TOOL (text_tool);
/* this happens when a newly created rectangle gets canceled,
* we have to shut down the tool
*/
if (response_id == GIMP_TOOL_WIDGET_RESPONSE_CANCEL)
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
}
static void
gimp_text_tool_rectangle_change_complete (GimpToolRectangle *rectangle,
GimpTextTool *text_tool)
{
gimp_text_tool_editor_position (text_tool);
if (text_tool->handle_rectangle_change_complete)
{
GimpItem *item = GIMP_ITEM (text_tool->layer);
gdouble x1, y1;
gdouble 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;
}
g_object_get (rectangle,
"x1", &x1,
"y1", &y1,
"x2", &x2,
"y2", &y2,
NULL);
if ((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_text_tool_block_drawing (text_tool);
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);
gimp_text_tool_unblock_drawing (text_tool);
if (push_undo)
gimp_image_undo_group_end (text_tool->image);
}
else if (x1 != gimp_item_get_offset_x (item) ||
y1 != gimp_item_get_offset_y (item))
{
gimp_text_tool_block_drawing (text_tool);
gimp_text_tool_apply (text_tool, TRUE);
gimp_item_translate (item,
x1 - gimp_item_get_offset_x (item),
y1 - gimp_item_get_offset_y (item),
TRUE);
gimp_text_tool_unblock_drawing (text_tool);
gimp_image_flush (text_tool->image);
}
}
}
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));
1997-11-25 06:05:25 +08:00
if (text_tool->text != text)
{
GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (tool);
g_signal_handlers_block_by_func (text_tool->buffer,
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
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_clear_object (&text_tool->text);
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,
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
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);
/* don't try to remove the layer if it is not attached,
* which can happen if we got here because the layer was
* somehow deleted from the image (like by the user in the
* layers dialog).
*/
if (gimp_item_is_attached (GIMP_ITEM (text_tool->layer)))
gimp_text_tool_remove_empty_text_layer (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);
}
else if (! strcmp (pspec->name, "offset-x") ||
! strcmp (pspec->name, "offset-y"))
{
if (gimp_item_is_attached (GIMP_ITEM (layer)))
{
gimp_text_tool_block_drawing (text_tool);
gimp_text_tool_frame_item (text_tool);
gimp_text_tool_unblock_drawing (text_tool);
}
}
}
static gboolean
gimp_text_tool_apply_idle (GimpTextTool *text_tool)
{
text_tool->idle_id = 0;
gimp_text_tool_apply (text_tool, TRUE);
gimp_text_tool_unblock_drawing (text_tool);
return G_SOURCE_REMOVE;
}
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)
{
if (text_tool->preedit_active)
{
/* if there is a preedit going on, don't queue pending
* changes to be idle-applied with undo; instead, flush the
* pending queue (happens only when preedit starts), and
* apply the changes to text_tool->text directly. Preedit
* will *always* end by removing the preedit string, and if
* the preedit was committed, it will insert the resulting
* text, which will not trigger this if() any more.
*/
GList *list = NULL;
/* if there are pending changes, apply them before applying
* preedit stuff directly (bypassing undo)
*/
if (text_tool->pending)
{
gimp_text_tool_block_drawing (text_tool);
gimp_text_tool_apply (text_tool, TRUE);
gimp_text_tool_unblock_drawing (text_tool);
}
gimp_text_tool_block_drawing (text_tool);
list = g_list_append (list, (gpointer) pspec);
gimp_text_tool_apply_list (text_tool, list);
g_list_free (list);
gimp_text_tool_frame_item (text_tool);
gimp_image_flush (gimp_item_get_image (GIMP_ITEM (text_tool->layer)));
gimp_text_tool_unblock_drawing (text_tool);
}
else
{
/* else queue the property change for normal processing,
* including undo
*/
text_tool->pending = g_list_append (text_tool->pending,
(gpointer) pspec);
if (! text_tool->idle_id)
{
gimp_text_tool_block_drawing (text_tool);
text_tool->idle_id =
g_idle_add_full (G_PRIORITY_LOW,
(GSourceFunc) 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);
/* an undo cancels all preedit operations */
if (text_tool->preedit_active)
gimp_text_tool_abort_im_context (text_tool);
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
gimp_text_tool_block_drawing (text_tool);
if ((pspec->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE)
{
GValue value = G_VALUE_INIT;
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,
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
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,
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
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);
}
gimp_text_tool_unblock_drawing (text_tool);
}
static void
gimp_text_tool_text_changed (GimpText *text,
GimpTextTool *text_tool)
{
gimp_text_tool_block_drawing (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);
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
gimp_text_tool_unblock_drawing (text_tool);
}
static void
gimp_text_tool_fonts_async_set_empty_notify (GimpAsyncSet *async_set,
GParamSpec *pspec,
GimpTextTool *text_tool)
{
GimpTool *tool = GIMP_TOOL (text_tool);
if (! gimp_async_set_is_empty (async_set) && tool->display)
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
}
static void
gimp_text_tool_apply_list (GimpTextTool *text_tool,
GList *pspecs)
{
GObject *src = G_OBJECT (text_tool->proxy);
GObject *dest = G_OBJECT (text_tool->text);
GList *list;
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 = pspecs; list; list = g_list_next (list))
{
const GParamSpec *pspec;
GValue value = G_VALUE_INIT;
/* 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_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);
}
static void
gimp_text_tool_create_layer (GimpTextTool *text_tool,
GimpText *text)
{
GimpTool *tool = GIMP_TOOL (text_tool);
GimpImage *image = gimp_display_get_image (tool->display);
GimpLayer *layer;
gdouble x1, y1;
gdouble x2, y2;
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
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 (text_tool->widget,
"x1", &x1,
"y1", &y1,
"x2", &x2,
"y2", &y2,
NULL);
if (text_tool->text_box_fixed == FALSE)
{
if (text_tool->text &&
(text_tool->text->base_dir == GIMP_TEXT_DIRECTION_TTB_RTL ||
text_tool->text->base_dir == GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT))
{
x1 -= gimp_item_get_width (GIMP_ITEM (layer));
}
}
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);
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
gimp_text_tool_apply (text_tool, TRUE); /* unblocks drawing */
}
else
{
gimp_text_tool_frame_item (text_tool);
}
gimp_image_undo_group_end (image);
gimp_image_flush (image);
gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer), FALSE);
gimp_text_tool_unblock_drawing (text_tool);
}
#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 (g_list_prepend (NULL, text_tool->layer),
GIMP_CONTEXT (gimp_tool_get_options (tool)),
_("Confirm Text Editing"),
"gimp-text-tool-confirm",
GIMP_ICON_LAYER_TEXT_LAYER,
_("Confirm Text Editing"),
GTK_WIDGET (shell),
gimp_standard_help_func, NULL,
_("Create _New Layer"), RESPONSE_NEW,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Edit"), GTK_RESPONSE_ACCEPT,
NULL);
gimp_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);
2011-09-30 17:29:11 +08:00
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_label_set_xalign (GTK_LABEL (label), 0.0);
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)
{
GList *layers = gimp_image_get_selected_layers (image);
if (g_list_length (layers) != 1 || layers->data != text_tool->layer)
{
GimpTool *tool = GIMP_TOOL (text_tool);
GimpDisplay *display = tool->display;
if (display)
{
GimpLayer *layer = NULL;
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
if (g_list_length (layers) == 1)
layer = layers->data;
/* The tool can only be started when a single layer is
* selected and this is a text layer.
*/
if (layer &&
gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer),
FALSE) &&
GIMP_LAYER (text_tool->layer) == layer)
{
GError *error = NULL;
if (! gimp_text_tool_start (text_tool, display, layer, &error))
{
gimp_text_tool_set_drawable (text_tool, NULL, FALSE);
gimp_tool_message_literal (tool, display, error->message);
g_clear_error (&error);
return;
}
}
}
}
}
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, "selected-layers-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;
}
2010-02-18 04:39:33 +08:00
static void
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
gimp_text_tool_block_drawing (GimpTextTool *text_tool)
{
if (text_tool->drawing_blocked == 0)
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
{
gimp_draw_tool_pause (GIMP_DRAW_TOOL (text_tool));
gimp_text_tool_clear_layout (text_tool);
}
text_tool->drawing_blocked++;
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
}
static void
gimp_text_tool_unblock_drawing (GimpTextTool *text_tool)
{
g_return_if_fail (text_tool->drawing_blocked > 0);
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
text_tool->drawing_blocked--;
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
if (text_tool->drawing_blocked == 0)
gimp_draw_tool_resume (GIMP_DRAW_TOOL (text_tool));
app: fix text tool drawing for good this time (hopefully) Earlier I claimed that drawing would work now because we make sure that buffer and layout are always in sync. This was nonsense. In fact, we constantly ran into the sutiation where the buffer was modified, and we were drawing with the previous layout. It's unclear why this didn't cause drawing artifacts, but it did cause e.g. the cursor temporarily jumping to the next position while editing in the middle of text (especially visible at line ends). Several underlying problems existed: first, we now modify the buffer from outside the text tool (from GimpTextStyleEditor) where we can't pause the tool; second, proxy changes are handled asymetrically (property changes are queued and processed all together in an idle function) so we can't pause/resume drawing across the entire operation because it has many beginnings and only one end. Therefore: - add gimp_text_tool_block_drawing()/unblock_drawing(), where block() can be called as many times as needed, and a single unblock() enables drawing again. block() also clears the layout, because it served its purpose (it was just used to pause drawing, and we know the buffer will change, so kill it). - connect to GtkTextBuffer::begin-user-action and call block() from the callback, so we undraw stuff and kill the cached layout before any buffer change happens. - call unblock() at the end of gimp_text_tool_apply() because then the text and the buffer are in sync again, the tool is undrawn and we can safely create the layout again to draw our stuff. - also call block()/unblock() from some other places, like when a new text layer is created. - get rid of *all* calls to draw_tool_pause()/resume() around buffer modifications, they are not needed any longer. - add calls to begin/end_user_action() where they were missing.
2010-03-05 06:47:23 +08:00
}
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);
}
gimp_text_tool_unblock_drawing (text_tool);
}
static void
gimp_text_tool_buffer_color_applied (GimpTextBuffer *buffer,
const GimpRGB *color,
GimpTextTool *text_tool)
{
gimp_palettes_add_color_history (GIMP_TOOL (text_tool)->tool_info->gimp,
color);
}
/* public functions */
void
gimp_text_tool_clear_layout (GimpTextTool *text_tool)
{
g_clear_object (&text_tool->layout);
}
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_apply (GimpTextTool *text_tool,
gboolean push_undo)
{
const GParamSpec *pspec = NULL;
GimpImage *image;
GimpTextLayer *layer;
GList *list;
gboolean undo_group = FALSE;
if (text_tool->idle_id)
{
g_source_remove (text_tool->idle_id);
text_tool->idle_id = 0;
gimp_text_tool_unblock_drawing (text_tool);
}
g_return_if_fail (text_tool->text != NULL);
g_return_if_fail (text_tool->layer != NULL);
layer = text_tool->layer;
image = gimp_item_get_image (GIMP_ITEM (layer));
g_return_if_fail (layer->text == text_tool->text);
/* 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);
}
gimp_text_tool_apply_list (text_tool, list);
g_list_free (text_tool->pending);
text_tool->pending = NULL;
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);
}
gboolean
gimp_text_tool_set_layer (GimpTextTool *text_tool,
GimpLayer *layer)
{
g_return_val_if_fail (GIMP_IS_TEXT_TOOL (text_tool), FALSE);
g_return_val_if_fail (layer == NULL || GIMP_IS_LAYER (layer), FALSE);
if (layer == GIMP_LAYER (text_tool->layer))
return TRUE;
/* FIXME this function works, and I have no clue why: first we set
* the drawable, then we HALT the tool and start() it without
* re-setting the drawable. Why this works perfectly anyway when
* double clicking a text layer in the layers dialog... no idea.
*/
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;
}
}
if (tool->display)
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
if (display)
{
GError *error = NULL;
if (! gimp_text_tool_start (text_tool, display, layer, &error))
{
gimp_text_tool_set_drawable (text_tool, NULL, FALSE);
gimp_tool_message_literal (tool, display, error->message);
g_clear_error (&error);
return FALSE;
}
g_list_free (tool->drawables);
tool->drawables = g_list_prepend (NULL, GIMP_DRAWABLE (layer));
}
}
return TRUE;
}
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_width;
gdouble box_height;
GimpTextDirection dir;
gdouble offset = 0.0;
g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
if (! text_tool->text || ! text_tool->image || ! text_tool->layer)
return;
box_width = gimp_item_get_width (GIMP_ITEM (text_tool->layer));
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);
offset = 0;
dir = gimp_text_tool_get_direction (text_tool);
switch (dir)
{
case GIMP_TEXT_DIRECTION_LTR:
case GIMP_TEXT_DIRECTION_RTL:
offset = 0.5 * box_height;
break;
case GIMP_TEXT_DIRECTION_TTB_RTL:
case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT:
case GIMP_TEXT_DIRECTION_TTB_LTR:
case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT:
{
GimpStroke *stroke = NULL;
while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke)))
{
gimp_stroke_rotate (stroke, 0, 0, 270);
gimp_stroke_translate (stroke, 0, box_width);
}
}
offset = 0.5 * box_width;
break;
}
gimp_vectors_warp_vectors (vectors0, vectors, offset);
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);
}
GimpTextDirection
gimp_text_tool_get_direction (GimpTextTool *text_tool)
{
GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
return options->base_dir;
}