source: trunk/third/gtkhtml/src/htmlengine-edit-fontstyle.c @ 18136

Revision 18136, 15.9 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18135, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2/* This file is part of the GtkHTML library
3
4   Copyright (C) 2000 Helix Code, Inc.
5   
6   This library is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Library General Public
8   License as published by the Free Software Foundation; either
9   version 2 of the License, or (at your option) any later version.
10   
11   This library is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Library General Public License for more details.
15   
16   You should have received a copy of the GNU Library General Public License
17   along with this library; see the file COPYING.LIB.  If not, write to
18   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19   Boston, MA 02111-1307, USA.
20*/
21
22#include <config.h>
23#include "gtkhtmldebug.h"
24#include "htmlclue.h"
25#include "htmlcolor.h"
26#include "htmlcolorset.h"
27#include "htmlengine-edit.h"
28#include "htmlengine-edit-fontstyle.h"
29#include "htmlengine-edit-movement.h"
30#include "htmlengine-edit-selection-updater.h"
31#include "htmlinterval.h"
32#include "htmltext.h"
33#include "htmlselection.h"
34#include "htmlsettings.h"
35#include "htmlundo.h"
36
37/* #define PARANOID_DEBUG */
38static HTMLObject * html_engine_text_style_object (HTMLEngine *e);
39
40
41static GtkHTMLFontStyle
42get_font_style_from_selection (HTMLEngine *engine)
43{
44        GtkHTMLFontStyle style;
45        GtkHTMLFontStyle conflicts;
46        gboolean first;
47        HTMLPoint p;
48
49        g_return_val_if_fail (engine->clue != NULL, GTK_HTML_FONT_STYLE_DEFAULT);
50        g_return_val_if_fail (html_engine_is_selection_active (engine), GTK_HTML_FONT_STYLE_DEFAULT);
51
52        /* printf ("%s mark %p,%d cursor %p,%d\n",
53                __FUNCTION__,
54                engine->mark, engine->mark->position,
55                engine->cursor, engine->cursor->position); */
56
57
58        style = GTK_HTML_FONT_STYLE_DEFAULT;
59        conflicts = GTK_HTML_FONT_STYLE_DEFAULT;
60        first = TRUE;
61
62        p = engine->selection->from;
63
64        while (1) {
65                if (html_object_is_text (p.object) && p.offset != html_object_get_length (p.object)) {
66                        if (first) {
67                                style = HTML_TEXT (p.object)->font_style;
68                                first = FALSE;
69                        } else
70                                conflicts |= HTML_TEXT (p.object)->font_style ^ style;
71                }
72
73                if (html_point_cursor_object_eq (&p, &engine->selection->to))
74                        break;
75
76                html_point_next_cursor (&p);
77
78                if (p.object == NULL) {
79                        g_warning ("Unable to find style for end of selection");
80                        return style;
81                }
82        }
83
84        return style & ~conflicts;
85}
86
87static HTMLColor *
88get_color_from_selection (HTMLEngine *engine)
89{
90        HTMLColor *color = NULL;
91        HTMLPoint p;
92
93        g_return_val_if_fail (engine->clue != NULL, NULL);
94        g_return_val_if_fail (html_engine_is_selection_active (engine), NULL);
95
96        p = engine->selection->from;
97        while (1) {
98                if (html_object_is_text (p.object)  && p.offset != html_object_get_length (p.object)) {
99                        color = HTML_TEXT (p.object)->color;
100                        break;
101                }
102
103                if (html_point_cursor_object_eq (&p, &engine->selection->to))
104                        break;
105                html_point_next_cursor (&p);
106
107                if (p.object == NULL) {
108                        g_warning ("Unable to find color for end of selection");
109                        return color;
110                }
111        }
112
113        return color;
114}
115
116GtkHTMLFontStyle
117html_engine_get_document_font_style (HTMLEngine *engine)
118{
119        g_return_val_if_fail (engine != NULL, GTK_HTML_FONT_STYLE_DEFAULT);
120        g_return_val_if_fail (HTML_IS_ENGINE (engine), GTK_HTML_FONT_STYLE_DEFAULT);
121        g_return_val_if_fail (engine->editable, GTK_HTML_FONT_STYLE_DEFAULT);
122
123        if (html_engine_is_selection_active (engine))
124                return get_font_style_from_selection (engine);
125        else {
126                HTMLObject *curr = engine->cursor->object;
127
128                if (curr == NULL)
129                        return GTK_HTML_FONT_STYLE_DEFAULT;
130                else if (! html_object_is_text (curr))
131                        return GTK_HTML_FONT_STYLE_DEFAULT;
132                else {
133                        HTMLObject *obj;
134
135                        obj = html_engine_text_style_object (engine);
136                        return obj
137                                ? HTML_TEXT (obj)->font_style
138                                : GTK_HTML_FONT_STYLE_DEFAULT;
139                }
140        }
141}
142
143HTMLColor *
144html_engine_get_document_color (HTMLEngine *engine)
145{
146        g_return_val_if_fail (engine != NULL, NULL);
147        g_return_val_if_fail (HTML_IS_ENGINE (engine), NULL);
148        g_return_val_if_fail (engine->editable, NULL);
149
150        if (html_engine_is_selection_active (engine))
151                return get_color_from_selection (engine);
152        else {
153                HTMLObject *curr = engine->cursor->object;
154
155                if (curr == NULL)
156                        return NULL;
157                else if (! html_object_is_text (curr))
158                        return NULL;
159                else {
160                        HTMLObject *obj;
161
162                        obj = html_engine_text_style_object (engine);
163                        return obj
164                                ? HTML_TEXT (obj)->color
165                                : html_colorset_get_color (engine->settings->color_set, HTMLTextColor);
166                }
167        }
168}
169
170GtkHTMLFontStyle
171html_engine_get_font_style (HTMLEngine *engine)
172{
173        return (engine->insertion_font_style == GTK_HTML_FONT_STYLE_DEFAULT)
174                ? html_engine_get_document_font_style (engine)
175                : engine->insertion_font_style;
176}
177
178HTMLColor *
179html_engine_get_color (HTMLEngine *engine)
180{
181        return engine->insertion_color;
182}
183
184/**
185 * html_engine_update_insertion_font_style:
186 * @engine: An HTMLEngine
187 *
188 * Update @engine's current insertion font style according to the
189 * current selection and cursor position.
190 *
191 * Return value:
192 **/
193gboolean
194html_engine_update_insertion_font_style (HTMLEngine *engine)
195{
196        GtkHTMLFontStyle new_style;
197
198        new_style = html_engine_get_document_font_style (engine);
199
200        if (new_style != engine->insertion_font_style) {
201                engine->insertion_font_style = new_style;
202                return TRUE;
203        }
204
205        return FALSE;
206}
207
208/**
209 * html_engine_update_insertion_style:
210 * @engine: An HTMLEngine
211 *
212 * Update @engine's current insertion font style/color according to the
213 * current selection and cursor position.
214 *
215 * Return value:
216 **/
217gboolean
218html_engine_update_insertion_color (HTMLEngine *engine)
219{
220        HTMLColor *new_color;
221
222        new_color = html_engine_get_document_color (engine);
223
224        if (new_color && !html_color_equal (new_color, engine->insertion_color)) {
225                html_color_unref (engine->insertion_color);
226                engine->insertion_color = new_color;
227                html_color_ref (engine->insertion_color);
228                return TRUE;
229        }
230
231        return FALSE;
232}
233
234/**
235 * html_engine_set_font_style:
236 * @engine: An HTMLEngine
237 * @style: An HTMLFontStyle
238 *
239 * Set the current font style for `engine'.  This has the same semantics as the
240 * bold, italics etc. buttons in a word processor, i.e.:
241 *
242 * - If there is a selection, the selection gets the specified style.
243 *
244 * - If there is no selection, the style gets "attached" to the cursor.  So
245 *   inserting text after this will cause text to have this style.
246 *
247 * Instead of specifying an "absolute" style, we specify it as a "difference"
248 * from the current one, through an AND mask and an OR mask.
249 *
250 **/
251struct tmp_font {
252        GtkHTMLFontStyle and_mask;
253        GtkHTMLFontStyle or_mask;
254};
255
256static void
257object_set_font_style (HTMLObject *o, HTMLEngine *e, gpointer data)
258{
259        if (html_object_is_text (o)) {
260                struct tmp_font *tf = (struct tmp_font *) data;
261                HTMLObject *prev;
262
263                HTML_TEXT (o)->font_style &= tf->and_mask;
264                HTML_TEXT (o)->font_style |= tf->or_mask;
265
266                if (o->parent) {
267                        prev = html_object_prev_not_slave (o);
268                        if (prev) {
269                                html_object_merge (prev, o, e, NULL, NULL, NULL);
270                        }
271                }
272        }
273}
274
275struct _HTMLEmptyParaSetStyle {
276        HTMLUndoData data;
277
278        GtkHTMLFontStyle and_mask;
279        GtkHTMLFontStyle or_mask;
280};
281typedef struct _HTMLEmptyParaSetStyle HTMLEmptyParaSetStyle;
282
283static void set_empty_flow_style (HTMLEngine *e, GtkHTMLFontStyle and_mask, GtkHTMLFontStyle or_mask, HTMLUndoDirection dir);
284
285static void
286set_empty_flow_style_undo_action (HTMLEngine *e, HTMLUndoData *undo_data, HTMLUndoDirection dir, guint position_after)
287{
288        HTMLEmptyParaSetStyle *undo = (HTMLEmptyParaSetStyle *) undo_data;
289
290        set_empty_flow_style (e, undo->and_mask, undo->or_mask, html_undo_direction_reverse (dir));
291}
292
293static void
294set_empty_flow_style (HTMLEngine *e, GtkHTMLFontStyle and_mask, GtkHTMLFontStyle or_mask, HTMLUndoDirection dir)
295{
296        HTMLEmptyParaSetStyle *undo;
297        GtkHTMLFontStyle old_or_mask;
298
299        g_return_if_fail (html_object_is_text (e->cursor->object));
300
301        old_or_mask = HTML_TEXT (e->cursor->object)->font_style & ~and_mask;
302        HTML_TEXT (e->cursor->object)->font_style &= and_mask;
303        HTML_TEXT (e->cursor->object)->font_style |= or_mask;
304
305        undo = g_new (HTMLEmptyParaSetStyle, 1);
306        html_undo_data_init (HTML_UNDO_DATA (undo));
307        undo->and_mask = and_mask;
308        undo->or_mask = old_or_mask;
309        undo->data.destroy = NULL;
310        html_undo_add_action (e->undo,
311                              html_undo_action_new ("Set empty paragraph text style", set_empty_flow_style_undo_action,
312                                                    HTML_UNDO_DATA (undo), html_cursor_get_position (e->cursor),
313                                                    html_cursor_get_position (e->cursor)), dir);
314}
315
316gboolean
317html_engine_set_font_style (HTMLEngine *e,
318                            GtkHTMLFontStyle and_mask,
319                            GtkHTMLFontStyle or_mask)
320{
321        gboolean rv;
322        GtkHTMLFontStyle old = e->insertion_font_style;
323
324        g_return_val_if_fail (e != NULL, FALSE);
325        g_return_val_if_fail (HTML_IS_ENGINE (e), FALSE);
326        g_return_val_if_fail (e->editable, FALSE);
327
328        /* printf ("and %d or %d\n", and_mask, or_mask); */
329        e->insertion_font_style &= and_mask;
330        e->insertion_font_style |= or_mask;
331
332        if (html_engine_is_selection_active (e)) {
333                struct tmp_font *tf = g_new (struct tmp_font, 1);
334                tf->and_mask = and_mask;
335                tf->or_mask  = or_mask;
336                html_engine_cut_and_paste (e, "Set font style", "Unset font style", object_set_font_style, tf);
337                g_free (tf);
338                rv = TRUE;
339        } else {
340                if (e->cursor->object->parent && html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent))) {
341                        set_empty_flow_style (e, and_mask, or_mask, HTML_UNDO_UNDO);
342                }
343                rv = (old == e->insertion_font_style) ? FALSE : TRUE;
344        }
345        return rv;
346}
347
348gboolean
349html_engine_toggle_font_style (HTMLEngine *engine, GtkHTMLFontStyle style)
350{
351        GtkHTMLFontStyle cur_style;
352
353        cur_style = html_engine_get_font_style (engine);
354
355        if (cur_style & style)
356                return html_engine_set_font_style (engine, GTK_HTML_FONT_STYLE_MAX & ~style, 0);
357        else
358                return html_engine_set_font_style (engine, GTK_HTML_FONT_STYLE_MAX, style);
359}
360
361static GtkHTMLFontStyle
362inc_dec_size (GtkHTMLFontStyle style, gboolean inc)
363{
364        GtkHTMLFontStyle size;
365
366        if (style == GTK_HTML_FONT_STYLE_DEFAULT)
367                style = GTK_HTML_FONT_STYLE_SIZE_3;
368
369        size = style & GTK_HTML_FONT_STYLE_SIZE_MASK;
370        if (inc && size < GTK_HTML_FONT_STYLE_SIZE_7)
371                size++;
372        else if (!inc && size > GTK_HTML_FONT_STYLE_SIZE_1)
373                size--;
374
375        style &= ~GTK_HTML_FONT_STYLE_SIZE_MASK;
376        style |= size;
377
378        return style;
379}
380
381static void
382inc_dec_size_cb (HTMLObject *o, HTMLEngine *e, gpointer data)
383{
384        if (html_object_is_text (o)) {
385                html_text_set_font_style (HTML_TEXT (o), e, inc_dec_size (HTML_TEXT (o)->font_style, GPOINTER_TO_INT (data)));
386                if (o->prev)
387                        html_object_merge (o->prev, o, e, NULL, NULL, NULL);
388        }
389}
390
391void
392html_engine_font_size_inc_dec (HTMLEngine *e, gboolean inc)
393{
394        if (html_engine_is_selection_active (e))
395                html_engine_cut_and_paste (e,
396                                           inc ? "Increase font size" : "Decrease font size",
397                                           inc ? "Decrease font size" : "Increase font size",
398                                           inc_dec_size_cb, GINT_TO_POINTER (inc));
399        else
400                e->insertion_font_style = inc_dec_size (e->insertion_font_style, inc);
401}
402
403static void
404set_color (HTMLObject *o, HTMLEngine *e, gpointer data)
405{
406        if (html_object_is_text (o)) {
407                HTMLObject *prev;
408
409                html_text_set_color (HTML_TEXT (o), NULL, (HTMLColor *) data);
410
411                if (o->parent) {
412                        prev = html_object_prev_not_slave (o);
413                        if (prev) {
414                                html_object_merge (prev, o, e, NULL, NULL, NULL);
415                        }
416                }
417        }
418}
419
420struct _HTMLEmptyParaSetColor {
421        HTMLUndoData data;
422
423        HTMLColor *color;
424};
425typedef struct _HTMLEmptyParaSetColor HTMLEmptyParaSetColor;
426
427static void set_empty_flow_color (HTMLEngine *e, HTMLColor *c, HTMLUndoDirection dir);
428
429static void
430set_empty_flow_color_undo_action (HTMLEngine *e, HTMLUndoData *undo_data, HTMLUndoDirection dir, guint position_after)
431{
432        HTMLEmptyParaSetColor *undo = (HTMLEmptyParaSetColor *) undo_data;
433
434        set_empty_flow_color (e, undo->color, html_undo_direction_reverse (dir));
435}
436
437static void
438set_empty_flow_color_destroy (HTMLUndoData *undo_data)
439{
440        HTMLEmptyParaSetColor *undo = (HTMLEmptyParaSetColor *) undo_data;
441
442        html_color_unref (undo->color);
443}
444
445static void
446set_empty_flow_color (HTMLEngine *e, HTMLColor *color, HTMLUndoDirection dir)
447{
448        HTMLColor *old_color;
449        HTMLEmptyParaSetColor *undo;
450
451        g_return_if_fail (html_object_is_text (e->cursor->object));
452
453        old_color = HTML_TEXT (e->cursor->object)->color;
454        html_color_ref (old_color);
455        html_text_set_color (HTML_TEXT (e->cursor->object), e, color);
456
457        undo = g_new (HTMLEmptyParaSetColor, 1);
458        html_undo_data_init (HTML_UNDO_DATA (undo));
459        undo->color = old_color;
460        undo->data.destroy = set_empty_flow_color_destroy;
461        html_undo_add_action (e->undo,
462                              html_undo_action_new ("Set empty paragraph color", set_empty_flow_color_undo_action,
463                                                    HTML_UNDO_DATA (undo), html_cursor_get_position (e->cursor),
464                                                    html_cursor_get_position (e->cursor)), dir);
465}
466
467gboolean
468html_engine_set_color (HTMLEngine *e, HTMLColor *color)
469{
470        gboolean rv = TRUE;
471
472        if (!color)
473                color = html_colorset_get_color (e->settings->color_set, HTMLTextColor);
474
475        if (html_engine_is_selection_active (e))
476                html_engine_cut_and_paste (e, "Set color", "Unset color", set_color, color);
477        else {
478                if (e->cursor->object->parent && html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent))) {
479                        set_empty_flow_color (e, color, HTML_UNDO_UNDO);
480                }
481                if (gdk_color_equal (&e->insertion_color->color, &color->color))
482                        rv = FALSE;
483        }
484        html_color_unref (e->insertion_color);
485        e->insertion_color = color;
486        html_color_ref (e->insertion_color);
487
488        return rv;
489}
490
491/* URL/Target
492
493   get actual url/target
494*/
495
496const gchar *
497html_engine_get_url (HTMLEngine *e)
498{
499        return e->insertion_url;
500}
501
502const gchar *
503html_engine_get_target (HTMLEngine *e)
504{
505        return e->insertion_target;
506}
507
508void
509html_engine_set_url (HTMLEngine *e, const gchar *url)
510{
511        if (e->insertion_url)
512                g_free (e->insertion_url);
513        e->insertion_url = g_strdup (url);
514}
515
516void
517html_engine_set_target (HTMLEngine *e, const gchar *target)
518{
519        if (e->insertion_target)
520                g_free (e->insertion_target);
521        e->insertion_target = g_strdup (target);
522}
523
524/* get url/target from document */
525
526static const gchar *
527get_url_or_target_from_selection (HTMLEngine *e, gboolean get_url)
528{
529        const gchar *str = NULL;
530        HTMLPoint p;
531
532        g_return_val_if_fail (e->clue != NULL, NULL);
533        g_return_val_if_fail (html_engine_is_selection_active (e), NULL);
534
535        p = e->selection->from;
536        while (1) {
537                str = get_url ? html_object_get_url (p.object) : html_object_get_target (p.object);
538                if (str || html_point_cursor_object_eq (&p, &e->selection->to))
539                        break;
540                html_point_next_cursor (&p);
541               
542                if (p.object == NULL) {
543                        g_warning ("Unable to find url by end of selection");
544                        return str;
545                }
546        }
547
548        return str;
549}
550
551static HTMLObject *
552html_engine_text_style_object (HTMLEngine *e)
553{
554        if (HTML_IS_TEXT (e->cursor->object)
555            || (e->cursor->offset && e->cursor->offset != html_object_get_length (e->cursor->object)))
556                return e->cursor->object;
557
558        if (e->cursor->offset) {
559                HTMLObject *next;
560
561                next = html_object_next_not_slave (e->cursor->object);
562                if (next && HTML_IS_TEXT (next))
563                        return next;
564        } else {
565                HTMLObject *prev;
566
567                prev = html_object_prev_not_slave (e->cursor->object);
568                if (prev && HTML_IS_TEXT (prev))
569                        return prev;
570        }
571
572        return NULL;
573}
574
575const gchar *
576html_engine_get_document_url (HTMLEngine *e)
577{
578        if (html_engine_is_selection_active (e))
579                return get_url_or_target_from_selection (e, TRUE);
580        else {
581                HTMLObject *obj;
582
583                obj = html_engine_text_style_object (e);
584                return obj ? html_object_get_url (obj) : NULL;
585        }
586}
587
588const gchar *
589html_engine_get_document_target (HTMLEngine *e)
590{
591        if (html_engine_is_selection_active (e))
592                return get_url_or_target_from_selection (e, FALSE);
593        else {
594                HTMLObject *obj;
595
596                obj = html_engine_text_style_object (e);
597                return obj ? html_object_get_target (obj) : NULL;
598        }
599}
600
601gboolean
602html_engine_update_insertion_url_and_target (HTMLEngine *engine)
603{
604        const gchar *url, *target;
605        gboolean retval = FALSE;
606
607        url    = html_engine_get_document_url    (engine);
608        target = html_engine_get_document_target (engine);
609
610        if (url != engine->insertion_url) {
611                html_engine_set_url (engine, url);
612                retval = TRUE;
613        }
614
615        if (target != engine->insertion_target) {
616                html_engine_set_target (engine, target);
617                retval = TRUE;
618        }
619
620        return retval;
621}
Note: See TracBrowser for help on using the repository browser.